diff options
author | 2017-02-15 19:29:36 -0800 | |
---|---|---|
committer | 2017-02-16 11:07:41 -0800 | |
commit | 47de0fad87b5e9a02b1fdde124ff32247f0b477e (patch) | |
tree | 87fda7f87a8d10cd67177298b68fd7e46e0ac098 | |
parent | 2bece1f9df4a060db2fc5b65cd3f3785e4e6e877 (diff) |
ART: Fix Unstarted Runtime Class.forName
We really only support the boot classloader, and only in the null
denotation. Otherwise the class-linker would want to install a
class table into the fake BootClassLoader.
Add tests.
Bug: 34956610
Test: m test-art-host-gtest-unstarted_runtime_test
Change-Id: I47e284fbd17eb8d33665d2788afcbcc3d09d3d2e
-rw-r--r-- | runtime/interpreter/unstarted_runtime.cc | 65 | ||||
-rw-r--r-- | runtime/interpreter/unstarted_runtime.h | 7 | ||||
-rw-r--r-- | runtime/interpreter/unstarted_runtime_test.cc | 194 |
3 files changed, 236 insertions, 30 deletions
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 66f14b99d8..fc219450df 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -175,56 +175,61 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz return param->AsString(); } -void UnstartedRuntime::UnstartedClassForName( - Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { +void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self, + ShadowFrame* shadow_frame, + JValue* result, + size_t arg_offset, + bool long_form, + const char* caller) { mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset); if (class_name == nullptr) { return; } + bool initialize_class; + mirror::ClassLoader* class_loader; + if (long_form) { + initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0; + class_loader = down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2)); + } else { + initialize_class = true; + // TODO: This is really only correct for the boot classpath, and for robustness we should + // check the caller. + class_loader = nullptr; + } + + ScopedObjectAccessUnchecked soa(self); + if (class_loader != nullptr && !ClassLinker::IsBootClassLoader(soa, class_loader)) { + AbortTransactionOrFail(self, + "Only the boot classloader is supported: %s", + mirror::Object::PrettyTypeOf(class_loader).c_str()); + return; + } + StackHandleScope<1> hs(self); Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); UnstartedRuntimeFindClass(self, h_class_name, ScopedNullHandle<mirror::ClassLoader>(), result, - "Class.forName", - true, + caller, + initialize_class, false); CheckExceptionGenerateClassNotFound(self); } +void UnstartedRuntime::UnstartedClassForName( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, false, "Class.forName"); +} + void UnstartedRuntime::UnstartedClassForNameLong( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { - mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset); - if (class_name == nullptr) { - return; - } - bool initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0; - mirror::ClassLoader* class_loader = - down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2)); - StackHandleScope<2> hs(self); - Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); - Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader)); - UnstartedRuntimeFindClass(self, h_class_name, h_class_loader, result, "Class.forName", - initialize_class, false); - CheckExceptionGenerateClassNotFound(self); + UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, true, "Class.forName"); } void UnstartedRuntime::UnstartedClassClassForName( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { - mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset); - if (class_name == nullptr) { - return; - } - bool initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0; - mirror::ClassLoader* class_loader = - down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2)); - StackHandleScope<2> hs(self); - Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); - Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader)); - UnstartedRuntimeFindClass(self, h_class_name, h_class_loader, result, "Class.classForName", - initialize_class, false); - CheckExceptionGenerateClassNotFound(self); + UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, true, "Class.classForName"); } void UnstartedRuntime::UnstartedClassNewInstance( diff --git a/runtime/interpreter/unstarted_runtime.h b/runtime/interpreter/unstarted_runtime.h index 3f36a27e92..bc9ead8360 100644 --- a/runtime/interpreter/unstarted_runtime.h +++ b/runtime/interpreter/unstarted_runtime.h @@ -89,6 +89,13 @@ class UnstartedRuntime { #undef UNSTARTED_RUNTIME_JNI_LIST #undef UNSTARTED_JNI + static void UnstartedClassForNameCommon(Thread* self, + ShadowFrame* shadow_frame, + JValue* result, + size_t arg_offset, + bool long_form, + const char* caller) REQUIRES_SHARED(Locks::mutator_lock_); + static void InitializeInvokeHandlers(); static void InitializeJNIHandlers(); diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 98a17e41f9..3a0d0e71c2 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -1084,5 +1084,199 @@ TEST_F(UnstartedRuntimeTest, LogManager) { ASSERT_TRUE(class_linker->EnsureInitialized(self, log_manager_class, true, true)); } +class UnstartedClassForNameTest : public UnstartedRuntimeTest { + public: + template <typename T> + void RunTest(T& runner, bool in_transaction, bool should_succeed) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + // Ensure that Class is initialized. + { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_class = hs.NewHandle(mirror::Class::GetJavaLangClass()); + CHECK(class_linker->EnsureInitialized(self, h_class, true, true)); + } + + // A selection of classes from different core classpath components. + constexpr const char* kTestCases[] = { + "java.net.CookieManager", // From libcore. + "dalvik.system.ClassExt", // From libart. + }; + + if (in_transaction) { + // For transaction mode, we cannot load any classes, as the pre-fence initialization of + // classes isn't transactional. Load them ahead of time. + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + for (const char* name : kTestCases) { + class_linker->FindClass(self, + DotToDescriptor(name).c_str(), + ScopedNullHandle<mirror::ClassLoader>()); + CHECK(!self->IsExceptionPending()) << self->GetException()->Dump(); + } + } + + if (!should_succeed) { + // Negative test. In general, currentThread should fail (as we should not leak a peer that will + // be recreated at runtime). + PrepareForAborts(); + } + + JValue result; + ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + + for (const char* name : kTestCases) { + mirror::String* name_string = mirror::String::AllocFromModifiedUtf8(self, name); + CHECK(name_string != nullptr); + + Transaction transaction; + if (in_transaction) { + Runtime::Current()->EnterTransactionMode(&transaction); + } + CHECK(!self->IsExceptionPending()); + + runner(self, shadow_frame, name_string, &result); + + if (in_transaction) { + Runtime::Current()->ExitTransactionMode(); + } + + if (should_succeed) { + CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump(); + CHECK(result.GetL() != nullptr) << name; + } else { + CHECK(self->IsExceptionPending()) << name; + if (in_transaction) { + ASSERT_TRUE(transaction.IsAborted()); + } + self->ClearException(); + } + } + + ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); + } + + mirror::ClassLoader* GetBootClassLoader() REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); + MutableHandle<mirror::ClassLoader> boot_cp = hs.NewHandle<mirror::ClassLoader>(nullptr); + + { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + + // Create the fake boot classloader. Any instance is fine, they are technically interchangeable. + Handle<mirror::Class> boot_cp_class = hs.NewHandle( + class_linker->FindClass(self, + "Ljava/lang/BootClassLoader;", + ScopedNullHandle<mirror::ClassLoader>())); + CHECK(boot_cp_class != nullptr); + CHECK(class_linker->EnsureInitialized(self, boot_cp_class, true, true)); + + boot_cp.Assign(boot_cp_class->AllocObject(self)->AsClassLoader()); + CHECK(boot_cp != nullptr); + + ArtMethod* boot_cp_init = boot_cp_class->FindDeclaredDirectMethod( + "<init>", "()V", class_linker->GetImagePointerSize()); + CHECK(boot_cp_init != nullptr); + + JValue result; + ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, boot_cp_init, 0); + shadow_frame->SetVRegReference(0, boot_cp.Get()); + + // create instruction data for invoke-direct {v0} of method with fake index + uint16_t inst_data[3] = { 0x1070, 0x0000, 0x0010 }; + const Instruction* inst = Instruction::At(inst_data); + + interpreter::DoCall<false, false>(boot_cp_init, + self, + *shadow_frame, + inst, + inst_data[0], + &result); + CHECK(!self->IsExceptionPending()); + + ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); + } + + return boot_cp.Get(); + } +}; + +TEST_F(UnstartedClassForNameTest, ClassForName) { + auto runner = [](Thread* self, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + UnstartedClassForName(self, shadow_frame, result, 0); + }; + RunTest(runner, false, true); +} + +TEST_F(UnstartedClassForNameTest, ClassForNameLong) { + auto runner = [](Thread* self, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + shadow_frame->SetVReg(1, 0); + shadow_frame->SetVRegReference(2, nullptr); + UnstartedClassForNameLong(self, shadow_frame, result, 0); + }; + RunTest(runner, false, true); +} + +TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoader) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> boot_cp = hs.NewHandle(GetBootClassLoader()); + + auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + shadow_frame->SetVReg(1, 0); + shadow_frame->SetVRegReference(2, boot_cp.Get()); + UnstartedClassForNameLong(th, shadow_frame, result, 0); + }; + RunTest(runner, false, true); +} + +TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderTransaction) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> boot_cp = hs.NewHandle(GetBootClassLoader()); + + auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + shadow_frame->SetVReg(1, 0); + shadow_frame->SetVRegReference(2, boot_cp.Get()); + UnstartedClassForNameLong(th, shadow_frame, result, 0); + }; + RunTest(runner, true, true); +} + +TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderFail) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + StackHandleScope<2> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + jobject path_jobj = class_linker->CreatePathClassLoader(self, {}); + ASSERT_TRUE(path_jobj != nullptr); + Handle<mirror::ClassLoader> path_cp = hs.NewHandle<mirror::ClassLoader>( + self->DecodeJObject(path_jobj)->AsClassLoader()); + + auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + shadow_frame->SetVReg(1, 0); + shadow_frame->SetVRegReference(2, path_cp.Get()); + UnstartedClassForNameLong(th, shadow_frame, result, 0); + }; + RunTest(runner, true, false); +} + } // namespace interpreter } // namespace art |