ART: Add constructor support to unstarted runtime

Add support for Constructor.newInstance0.

Bug: 34956610
Test: m
Test: m test-art-host-gtest-unstarted_runtime_test
Test: Device boots
Change-Id: Ifcea33359c30b70262cd2f4f56f59515b06532ce
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 4a321e6..af0478c 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -632,6 +632,72 @@
   GetResourceAsStream(self, shadow_frame, result, arg_offset);
 }
 
+void UnstartedRuntime::UnstartedConstructorNewInstance0(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+  // This is a cutdown version of java_lang_reflect_Constructor.cc's implementation.
+  StackHandleScope<4> hs(self);
+  Handle<mirror::Constructor> m = hs.NewHandle(
+      reinterpret_cast<mirror::Constructor*>(shadow_frame->GetVRegReference(arg_offset)));
+  Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle(
+      reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(
+          shadow_frame->GetVRegReference(arg_offset + 1)));
+  Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass()));
+  if (UNLIKELY(c->IsAbstract())) {
+    AbortTransactionOrFail(self, "Cannot handle abstract classes");
+    return;
+  }
+  // Verify that we can access the class.
+  if (!m->IsAccessible() && !c->IsPublic()) {
+    // Go 2 frames back, this method is always called from newInstance0, which is called from
+    // Constructor.newInstance(Object... args).
+    ObjPtr<mirror::Class> caller = GetCallingClass(self, 2);
+    // If caller is null, then we called from JNI, just avoid the check since JNI avoids most
+    // access checks anyways. TODO: Investigate if this the correct behavior.
+    if (caller != nullptr && !caller->CanAccess(c.Get())) {
+      AbortTransactionOrFail(self, "Cannot access class");
+      return;
+    }
+  }
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, c, true, true)) {
+    DCHECK(self->IsExceptionPending());
+    return;
+  }
+  if (c->IsClassClass()) {
+    AbortTransactionOrFail(self, "new Class() is not supported");
+    return;
+  }
+
+  // String constructor is replaced by a StringFactory method in InvokeMethod.
+  if (c->IsStringClass()) {
+    // We don't support strings.
+    AbortTransactionOrFail(self, "String construction is not supported");
+    return;
+  }
+
+  Handle<mirror::Object> receiver = hs.NewHandle(c->AllocObject(self));
+  if (receiver == nullptr) {
+    AbortTransactionOrFail(self, "Could not allocate");
+    return;
+  }
+
+  // It's easier to use reflection to make the call, than create the uint32_t array.
+  {
+    ScopedObjectAccessUnchecked soa(self);
+    ScopedLocalRef<jobject> method_ref(self->GetJniEnv(),
+                                       soa.AddLocalReference<jobject>(m.Get()));
+    ScopedLocalRef<jobject> object_ref(self->GetJniEnv(),
+                                       soa.AddLocalReference<jobject>(receiver.Get()));
+    ScopedLocalRef<jobject> args_ref(self->GetJniEnv(),
+                                     soa.AddLocalReference<jobject>(args.Get()));
+    InvokeMethod(soa, method_ref.get(), object_ref.get(), args_ref.get(), 2);
+  }
+  if (self->IsExceptionPending()) {
+    AbortTransactionOrFail(self, "Failed running constructor");
+  } else {
+    result->SetL(receiver.Get());
+  }
+}
+
 void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
   mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index c6114da..6fc7989 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -34,6 +34,7 @@
   V(ClassGetSignatureAnnotation, "java.lang.String[] java.lang.Class.getSignatureAnnotation()") \
   V(ClassIsAnonymousClass, "boolean java.lang.Class.isAnonymousClass()") \
   V(ClassLoaderGetResourceAsStream, "java.io.InputStream java.lang.ClassLoader.getResourceAsStream(java.lang.String)") \
+  V(ConstructorNewInstance0, "java.lang.Object java.lang.reflect.Constructor.newInstance0(java.lang.Object[])") \
   V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \
   V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \
   V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 4186c37..db222fa 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -1317,5 +1317,55 @@
   ASSERT_EQ(output_string, "<E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;");
 }
 
+TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) {
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  StackHandleScope<4> hs(self);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+  // Get Throwable.
+  Handle<mirror::Class> throw_class = hs.NewHandle(mirror::Throwable::GetJavaLangThrowable());
+  ASSERT_TRUE(class_linker->EnsureInitialized(self, throw_class, true, true));
+
+  // Get an input object.
+  Handle<mirror::String> input = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "abd"));
+
+  // Find the constructor.
+  ArtMethod* throw_cons = throw_class->FindDeclaredDirectMethod(
+      "<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
+  ASSERT_TRUE(throw_cons != nullptr);
+
+  Handle<mirror::Constructor> cons = hs.NewHandle(
+      mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>(self, throw_cons));
+  ASSERT_TRUE(cons != nullptr);
+
+  Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle(
+      class_linker->AllocObjectArray<mirror::Object>(self, 1));
+  ASSERT_TRUE(args != nullptr);
+  args->Set(0, input.Get());
+
+  // OK, we're ready now.
+  JValue result;
+  ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+  shadow_frame->SetVRegReference(0, cons.Get());
+  shadow_frame->SetVRegReference(1, args.Get());
+  UnstartedConstructorNewInstance0(self, shadow_frame, &result, 0);
+
+  ASSERT_TRUE(result.GetL() != nullptr);
+  ASSERT_FALSE(self->IsExceptionPending());
+
+  // Should be a new object.
+  ASSERT_NE(result.GetL(), input.Get());
+  // Should be a String.
+  ASSERT_EQ(mirror::Throwable::GetJavaLangThrowable(), result.GetL()->GetClass());
+  // Should have the right string.
+  ObjPtr<mirror::String> result_msg =
+      reinterpret_cast<mirror::Throwable*>(result.GetL())->GetDetailMessage();
+  EXPECT_EQ(input.Get(), result_msg.Ptr());
+
+  ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+}
+
 }  // namespace interpreter
 }  // namespace art