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