From 4e99b3d8955131f3fc71aa113f0fa71f0092cb6f Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Tue, 24 Jun 2014 14:35:40 +0200 Subject: Add missing class initialization during compilation and tests Adds missing class initialization during compilation and tests, especially java.lang.Class. Otherwise, we'd be able to execute code while the referring class is not initialized or initializing. Also adds mirror::Class::AssertInitializedOrInitializingInThread method to check class initialization when entering the interpreter: the called method's declaring class must either be initialized or be initializing by the current thread (other threads must be waiting for the class initialization to complete holding its lock). Note we only do this check in debug build. Bump oat version to force compilation. Bug: 15899971 Change-Id: I9a4edd3739a3ca4cf1c4929dcbb44cdf7a1ca1fe --- runtime/interpreter/interpreter.cc | 1 + runtime/interpreter/interpreter_common.cc | 78 ++++++++++++++-------- runtime/interpreter/interpreter_goto_table_impl.cc | 1 + runtime/interpreter/interpreter_switch_impl.cc | 1 + 4 files changed, 55 insertions(+), 26 deletions(-) (limited to 'runtime/interpreter') diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index cb4d44451..9cc144149 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -354,6 +354,7 @@ static inline JValue Execute(Thread* self, MethodHelper& mh, const DexFile::Code shadow_frame.GetMethod()->GetDeclaringClass()->IsProxyClass()); DCHECK(!shadow_frame.GetMethod()->IsAbstract()); DCHECK(!shadow_frame.GetMethod()->IsNative()); + shadow_frame.GetMethod()->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); bool transaction_active = Runtime::Current()->IsActiveTransaction(); if (LIKELY(shadow_frame.GetMethod()->IsPreverified())) { diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 9f04b90c5..5a03601bc 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -35,6 +35,7 @@ bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst CHECK(self->IsExceptionPending()); return false; } + f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); Object* obj; if (is_static) { obj = f->GetDeclaringClass(); @@ -210,6 +211,7 @@ bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction CHECK(self->IsExceptionPending()); return false; } + f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); Object* obj; if (is_static) { obj = f->GetDeclaringClass(); @@ -757,40 +759,64 @@ void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count) } } +// Helper function to deal with class loading in an unstarted runtime. +static void UnstartedRuntimeFindClass(Thread* self, Handle className, + Handle class_loader, JValue* result, + const std::string& method_name, bool initialize_class, + bool abort_if_not_found) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(className.Get() != nullptr); + std::string descriptor(DotToDescriptor(className->ToModifiedUtf8().c_str())); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + + Class* found = class_linker->FindClass(self, descriptor.c_str(), class_loader); + if (found == nullptr && abort_if_not_found) { + if (!self->IsExceptionPending()) { + AbortTransaction(self, "%s failed in un-started runtime for class: %s", + method_name.c_str(), PrettyDescriptor(descriptor).c_str()); + } + return; + } + if (found != nullptr && initialize_class) { + StackHandleScope<1> hs(self); + Handle h_class(hs.NewHandle(found)); + if (!class_linker->EnsureInitialized(h_class, true, true)) { + CHECK(self->IsExceptionPending()); + return; + } + } + result->SetL(found); +} + static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // In a runtime that's not started we intercept certain methods to avoid complicated dependency // problems in core libraries. std::string name(PrettyMethod(shadow_frame->GetMethod())); - if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)" - || name == "java.lang.Class java.lang.VMClassLoader.loadClass(java.lang.String, boolean)") { - // TODO Class#forName should actually call Class::EnsureInitialized always. Support for the - // other variants that take more arguments should also be added. - std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str())); - - // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); - Class* found = Runtime::Current()->GetClassLinker()->FindClass( - self, descriptor.c_str(), NullHandle()); - if (found == NULL) { - if (!self->IsExceptionPending()) { - AbortTransaction(self, "Class.forName failed in un-started runtime for class: %s", - PrettyDescriptor(descriptor).c_str()); - } - return; - } - result->SetL(found); + if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") { + // TODO: Support for the other variants that take more arguments should also be added. + mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset)->AsString(); + StackHandleScope<1> hs(self); + Handle h_class_name(hs.NewHandle(class_name)); + UnstartedRuntimeFindClass(self, h_class_name, NullHandle(), result, name, + true, true); + } else if (name == "java.lang.Class java.lang.VMClassLoader.loadClass(java.lang.String, boolean)") { + mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset)->AsString(); + StackHandleScope<1> hs(self); + Handle h_class_name(hs.NewHandle(class_name)); + UnstartedRuntimeFindClass(self, h_class_name, NullHandle(), result, name, + false, true); + } else if (name == "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") { + mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); + mirror::ClassLoader* class_loader = + down_cast(shadow_frame->GetVRegReference(arg_offset)); + StackHandleScope<2> hs(self); + Handle h_class_name(hs.NewHandle(class_name)); + Handle h_class_loader(hs.NewHandle(class_loader)); + UnstartedRuntimeFindClass(self, h_class_name, h_class_loader, result, name, false, false); } else if (name == "java.lang.Class java.lang.Void.lookupType()") { result->SetL(Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V')); - } else if (name == "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") { - StackHandleScope<1> hs(self); - Handle class_loader( - hs.NewHandle(down_cast(shadow_frame->GetVRegReference(arg_offset)))); - std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset + 1)->AsString()->ToModifiedUtf8().c_str())); - - Class* found = Runtime::Current()->GetClassLinker()->FindClass(self, descriptor.c_str(), - class_loader); - result->SetL(found); } else if (name == "java.lang.Object java.lang.Class.newInstance()") { Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); ArtMethod* c = klass->FindDeclaredDirectMethod("", "()V"); diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index cb4868c95..abd4b44d3 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -536,6 +536,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { + obj->GetClass()->AssertInitializedOrInitializingInThread(self); // Don't allow finalizable objects to be allocated during a transaction since these can't be // finalized without a started runtime. if (transaction_active && obj->GetClass()->IsFinalizable()) { diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index bdf2a2019..c6356485a 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -449,6 +449,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { + obj->GetClass()->AssertInitializedOrInitializingInThread(self); // Don't allow finalizable objects to be allocated during a transaction since these can't // be finalized without a started runtime. if (transaction_active && obj->GetClass()->IsFinalizable()) { -- cgit v1.2.3