diff options
Diffstat (limited to 'runtime/class_linker.cc')
| -rw-r--r-- | runtime/class_linker.cc | 4640 |
1 files changed, 2254 insertions, 2386 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 59117350c6..9ca6492fb1 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -16,25 +16,31 @@ #include "class_linker.h" -#include <fcntl.h> -#include <sys/file.h> -#include <sys/stat.h> -#include <unistd.h> #include <deque> +#include <iostream> #include <memory> +#include <queue> #include <string> +#include <unistd.h> #include <utility> #include <vector> +#include "art_field-inl.h" +#include "art_method-inl.h" +#include "base/arena_allocator.h" #include "base/casts.h" #include "base/logging.h" +#include "base/scoped_arena_containers.h" #include "base/scoped_flock.h" #include "base/stl_util.h" +#include "base/time_utils.h" #include "base/unix_file/fd_file.h" +#include "base/value_object.h" #include "class_linker-inl.h" #include "compiler_callbacks.h" #include "debugger.h" #include "dex_file-inl.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "gc_root-inl.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap.h" @@ -43,18 +49,21 @@ #include "handle_scope.h" #include "intern_table.h" #include "interpreter/interpreter.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" #include "leb128.h" -#include "method_helper-inl.h" +#include "linear_alloc.h" #include "oat.h" #include "oat_file.h" +#include "oat_file_assistant.h" #include "object_lock.h" -#include "mirror/art_field-inl.h" -#include "mirror/art_method-inl.h" #include "mirror/class.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" +#include "mirror/field.h" #include "mirror/iftable-inl.h" +#include "mirror/method.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/proxy.h" @@ -67,13 +76,18 @@ #include "ScopedLocalRef.h" #include "scoped_thread_state_change.h" #include "handle_scope-inl.h" -#include "thread.h" +#include "thread-inl.h" #include "utils.h" #include "verifier/method_verifier.h" #include "well_known_classes.h" namespace art { +static constexpr bool kSanityCheckObjects = kIsDebugBuild; + +// For b/21333911. +static constexpr bool kDuplicateClassesCheck = false; + static void ThrowNoClassDefFoundError(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2))) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -81,36 +95,77 @@ static void ThrowNoClassDefFoundError(const char* fmt, ...) { va_list args; va_start(args, fmt); Thread* self = Thread::Current(); - ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - self->ThrowNewExceptionV(throw_location, "Ljava/lang/NoClassDefFoundError;", fmt, args); + self->ThrowNewExceptionV("Ljava/lang/NoClassDefFoundError;", fmt, args); va_end(args); } -static void ThrowEarlierClassFailure(mirror::Class* c) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +bool ClassLinker::HasInitWithString( + Thread* self, ClassLinker* class_linker, const char* descriptor) { + ArtMethod* method = self->GetCurrentMethod(nullptr); + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(method != nullptr ? + method->GetDeclaringClass()->GetClassLoader() + : nullptr)); + mirror::Class* exception_class = class_linker->FindClass(self, descriptor, class_loader); + + if (exception_class == nullptr) { + // No exc class ~ no <init>-with-string. + CHECK(self->IsExceptionPending()); + self->ClearException(); + return false; + } + + ArtMethod* exception_init_method = exception_class->FindDeclaredDirectMethod( + "<init>", "(Ljava/lang/String;)V", image_pointer_size_); + return exception_init_method != nullptr; +} + +void ClassLinker::ThrowEarlierClassFailure(mirror::Class* c) { // The class failed to initialize on a previous attempt, so we want to throw // a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we // failed in verification, in which case v2 5.4.1 says we need to re-throw // the previous error. - if (!Runtime::Current()->IsCompiler()) { // Give info if this occurs at runtime. + Runtime* const runtime = Runtime::Current(); + if (!runtime->IsAotCompiler()) { // Give info if this occurs at runtime. LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c); } CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus(); Thread* self = Thread::Current(); - ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - if (c->GetVerifyErrorClass() != nullptr) { - // TODO: change the verifier to store an _instance_, with a useful detail message? - std::string temp; - self->ThrowNewException(throw_location, c->GetVerifyErrorClass()->GetDescriptor(&temp), - PrettyDescriptor(c).c_str()); + if (runtime->IsAotCompiler()) { + // At compile time, accurate errors and NCDFE are disabled to speed compilation. + mirror::Throwable* pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); } else { - self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;", - PrettyDescriptor(c).c_str()); + if (c->GetVerifyErrorClass() != nullptr) { + // TODO: change the verifier to store an _instance_, with a useful detail message? + // It's possible the exception doesn't have a <init>(String). + std::string temp; + const char* descriptor = c->GetVerifyErrorClass()->GetDescriptor(&temp); + + if (HasInitWithString(self, this, descriptor)) { + self->ThrowNewException(descriptor, PrettyDescriptor(c).c_str()); + } else { + self->ThrowNewException(descriptor, nullptr); + } + } else { + self->ThrowNewException("Ljava/lang/NoClassDefFoundError;", + PrettyDescriptor(c).c_str()); + } } } -static void WrapExceptionInInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static void VlogClassInitializationFailure(Handle<mirror::Class> klass) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (VLOG_IS_ON(class_linker)) { + std::string temp; + LOG(INFO) << "Failed to initialize class " << klass->GetDescriptor(&temp) << " from " + << klass->GetLocation() << "\n" << Thread::Current()->GetException()->Dump(); + } +} + +static void WrapExceptionInInitializer(Handle<mirror::Class> klass) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Thread* self = Thread::Current(); JNIEnv* env = self->GetJniEnv(); @@ -123,49 +178,89 @@ static void WrapExceptionInInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lo // We only wrap non-Error exceptions; an Error can just be used as-is. if (!is_error) { - ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - self->ThrowNewWrappedException(throw_location, "Ljava/lang/ExceptionInInitializerError;", - nullptr); - } -} - -const char* ClassLinker::class_roots_descriptors_[] = { - "Ljava/lang/Class;", - "Ljava/lang/Object;", - "[Ljava/lang/Class;", - "[Ljava/lang/Object;", - "Ljava/lang/String;", - "Ljava/lang/DexCache;", - "Ljava/lang/ref/Reference;", - "Ljava/lang/reflect/ArtField;", - "Ljava/lang/reflect/ArtMethod;", - "Ljava/lang/reflect/Proxy;", - "[Ljava/lang/String;", - "[Ljava/lang/reflect/ArtField;", - "[Ljava/lang/reflect/ArtMethod;", - "Ljava/lang/ClassLoader;", - "Ljava/lang/Throwable;", - "Ljava/lang/ClassNotFoundException;", - "Ljava/lang/StackTraceElement;", - "Z", - "B", - "C", - "D", - "F", - "I", - "J", - "S", - "V", - "[Z", - "[B", - "[C", - "[D", - "[F", - "[I", - "[J", - "[S", - "[Ljava/lang/StackTraceElement;", + self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", nullptr); + } + VlogClassInitializationFailure(klass); +} + +// Gap between two fields in object layout. +struct FieldGap { + uint32_t start_offset; // The offset from the start of the object. + uint32_t size; // The gap size of 1, 2, or 4 bytes. +}; +struct FieldGapsComparator { + explicit FieldGapsComparator() { + } + bool operator() (const FieldGap& lhs, const FieldGap& rhs) + NO_THREAD_SAFETY_ANALYSIS { + // Sort by gap size, largest first. Secondary sort by starting offset. + return lhs.size > rhs.size || (lhs.size == rhs.size && lhs.start_offset < rhs.start_offset); + } }; +typedef std::priority_queue<FieldGap, std::vector<FieldGap>, FieldGapsComparator> FieldGaps; + +// Adds largest aligned gaps to queue of gaps. +static void AddFieldGap(uint32_t gap_start, uint32_t gap_end, FieldGaps* gaps) { + DCHECK(gaps != nullptr); + + uint32_t current_offset = gap_start; + while (current_offset != gap_end) { + size_t remaining = gap_end - current_offset; + if (remaining >= sizeof(uint32_t) && IsAligned<4>(current_offset)) { + gaps->push(FieldGap {current_offset, sizeof(uint32_t)}); + current_offset += sizeof(uint32_t); + } else if (remaining >= sizeof(uint16_t) && IsAligned<2>(current_offset)) { + gaps->push(FieldGap {current_offset, sizeof(uint16_t)}); + current_offset += sizeof(uint16_t); + } else { + gaps->push(FieldGap {current_offset, sizeof(uint8_t)}); + current_offset += sizeof(uint8_t); + } + DCHECK_LE(current_offset, gap_end) << "Overran gap"; + } +} +// Shuffle fields forward, making use of gaps whenever possible. +template<int n> +static void ShuffleForward(size_t* current_field_idx, + MemberOffset* field_offset, + std::deque<ArtField*>* grouped_and_sorted_fields, + FieldGaps* gaps) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(current_field_idx != nullptr); + DCHECK(grouped_and_sorted_fields != nullptr); + DCHECK(gaps != nullptr); + DCHECK(field_offset != nullptr); + + DCHECK(IsPowerOfTwo(n)); + while (!grouped_and_sorted_fields->empty()) { + ArtField* field = grouped_and_sorted_fields->front(); + Primitive::Type type = field->GetTypeAsPrimitiveType(); + if (Primitive::ComponentSize(type) < n) { + break; + } + if (!IsAligned<n>(field_offset->Uint32Value())) { + MemberOffset old_offset = *field_offset; + *field_offset = MemberOffset(RoundUp(field_offset->Uint32Value(), n)); + AddFieldGap(old_offset.Uint32Value(), field_offset->Uint32Value(), gaps); + } + CHECK(type != Primitive::kPrimNot) << PrettyField(field); // should be primitive types + grouped_and_sorted_fields->pop_front(); + if (!gaps->empty() && gaps->top().size >= n) { + FieldGap gap = gaps->top(); + gaps->pop(); + DCHECK(IsAligned<n>(gap.start_offset)); + field->SetOffset(MemberOffset(gap.start_offset)); + if (gap.size > n) { + AddFieldGap(gap.start_offset + n, gap.start_offset + gap.size, gaps); + } + } else { + DCHECK(IsAligned<n>(field_offset->Uint32Value())); + field->SetOffset(*field_offset); + *field_offset = MemberOffset(field_offset->Uint32Value() + n); + } + ++(*current_field_idx); + } +} ClassLinker::ClassLinker(InternTable* intern_table) // dex_lock_ is recursive as it may be used in stack dumping. @@ -179,94 +274,101 @@ ClassLinker::ClassLinker(InternTable* intern_table) log_new_dex_caches_roots_(false), log_new_class_table_roots_(false), intern_table_(intern_table), - portable_resolution_trampoline_(nullptr), quick_resolution_trampoline_(nullptr), - portable_imt_conflict_trampoline_(nullptr), quick_imt_conflict_trampoline_(nullptr), quick_generic_jni_trampoline_(nullptr), quick_to_interpreter_bridge_trampoline_(nullptr), image_pointer_size_(sizeof(void*)) { - CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax)); - memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*)); + CHECK(intern_table_ != nullptr); + static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_), + "Array cache size wrong."); + std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr)); } -// To set a value for generic JNI. May be necessary in compiler tests. -extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*); -extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*); -extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*); -extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*); - -void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class_path) { +void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path) { VLOG(startup) << "ClassLinker::Init"; - CHECK(!Runtime::Current()->GetHeap()->HasImageSpace()) << "Runtime has image. We should use it."; + Thread* const self = Thread::Current(); + Runtime* const runtime = Runtime::Current(); + gc::Heap* const heap = runtime->GetHeap(); + + CHECK(!heap->HasImageSpace()) << "Runtime has image. We should use it."; CHECK(!init_done_); + // Use the pointer size from the runtime since we are probably creating the image. + image_pointer_size_ = InstructionSetPointerSize(runtime->GetInstructionSet()); + // java_lang_Class comes first, it's needed for AllocClass - Thread* self = Thread::Current(); - gc::Heap* heap = Runtime::Current()->GetHeap(); // The GC can't handle an object with a null class since we can't get the size of this object. heap->IncrementDisableMovingGC(self); StackHandleScope<64> hs(self); // 64 is picked arbitrarily. + auto class_class_size = mirror::Class::ClassClassSize(image_pointer_size_); Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>( - heap->AllocNonMovableObject<true>(self, nullptr, - mirror::Class::ClassClassSize(), - VoidFunctor())))); + heap->AllocNonMovableObject<true>(self, nullptr, class_class_size, VoidFunctor())))); CHECK(java_lang_Class.Get() != nullptr); mirror::Class::SetClassClass(java_lang_Class.Get()); java_lang_Class->SetClass(java_lang_Class.Get()); if (kUseBakerOrBrooksReadBarrier) { java_lang_Class->AssertReadBarrierPointer(); } - java_lang_Class->SetClassSize(mirror::Class::ClassClassSize()); + java_lang_Class->SetClassSize(class_class_size); + java_lang_Class->SetPrimitiveType(Primitive::kPrimNot); heap->DecrementDisableMovingGC(self); // AllocClass(mirror::Class*) can now be used // Class[] is used for reflection support. + auto class_array_class_size = mirror::ObjectArray<mirror::Class>::ClassSize(image_pointer_size_); Handle<mirror::Class> class_array_class(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray<mirror::Class>::ClassSize()))); + AllocClass(self, java_lang_Class.Get(), class_array_class_size))); class_array_class->SetComponentType(java_lang_Class.Get()); // java_lang_Object comes next so that object_array_class can be created. Handle<mirror::Class> java_lang_Object(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize()))); + AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize(image_pointer_size_)))); CHECK(java_lang_Object.Get() != nullptr); // backfill Object as the super class of Class. java_lang_Class->SetSuperClass(java_lang_Object.Get()); - java_lang_Object->SetStatus(mirror::Class::kStatusLoaded, self); + mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusLoaded, self); // Object[] next to hold class roots. Handle<mirror::Class> object_array_class(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray<mirror::Object>::ClassSize()))); + AllocClass(self, java_lang_Class.Get(), + mirror::ObjectArray<mirror::Object>::ClassSize(image_pointer_size_)))); object_array_class->SetComponentType(java_lang_Object.Get()); // Setup the char (primitive) class to be used for char[]. Handle<mirror::Class> char_class(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::Class::PrimitiveClassSize()))); + AllocClass(self, java_lang_Class.Get(), + mirror::Class::PrimitiveClassSize(image_pointer_size_)))); + // The primitive char class won't be initialized by + // InitializePrimitiveClass until line 459, but strings (and + // internal char arrays) will be allocated before that and the + // component size, which is computed from the primitive type, needs + // to be set here. + char_class->SetPrimitiveType(Primitive::kPrimChar); // Setup the char[] class to be used for String. Handle<mirror::Class> char_array_class(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), - mirror::Array::ClassSize()))); + AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); char_array_class->SetComponentType(char_class.Get()); mirror::CharArray::SetArrayClass(char_array_class.Get()); // Setup String. Handle<mirror::Class> java_lang_String(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize()))); + AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize(image_pointer_size_)))); mirror::String::SetClass(java_lang_String.Get()); - java_lang_String->SetObjectSize(mirror::String::InstanceSize()); - java_lang_String->SetStatus(mirror::Class::kStatusResolved, self); + mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusResolved, self); + java_lang_String->SetStringClass(); - // Setup Reference. + // Setup java.lang.ref.Reference. Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize()))); + AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize(image_pointer_size_)))); mirror::Reference::SetClass(java_lang_ref_Reference.Get()); java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize()); - java_lang_ref_Reference->SetStatus(mirror::Class::kStatusResolved, self); + mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusResolved, self); // Create storage for root classes, save away our work so far (requires descriptors). - class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class> >( + class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>( mirror::ObjectArray<mirror::Class>::Alloc(self, object_array_class.Get(), kClassRootsMax)); CHECK(!class_roots_.IsNull()); @@ -293,64 +395,47 @@ void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class // Create int array type for AllocDexCache (done in AppendToBootClassPath). Handle<mirror::Class> int_array_class(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize()))); + AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); int_array_class->SetComponentType(GetClassRoot(kPrimitiveInt)); mirror::IntArray::SetArrayClass(int_array_class.Get()); SetClassRoot(kIntArrayClass, int_array_class.Get()); + // Create long array type for AllocDexCache (done in AppendToBootClassPath). + Handle<mirror::Class> long_array_class(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); + long_array_class->SetComponentType(GetClassRoot(kPrimitiveLong)); + mirror::LongArray::SetArrayClass(long_array_class.Get()); + SetClassRoot(kLongArrayClass, long_array_class.Get()); + // now that these are registered, we can use AllocClass() and AllocObjectArray // Set up DexCache. This cannot be done later since AppendToBootClassPath calls AllocDexCache. Handle<mirror::Class> java_lang_DexCache(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize()))); + AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize(image_pointer_size_)))); SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get()); java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize()); - java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved, self); - - // Constructor, Field, Method, and AbstractMethod are necessary so - // that FindClass can link members. - Handle<mirror::Class> java_lang_reflect_ArtField(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::ArtField::ClassSize()))); - CHECK(java_lang_reflect_ArtField.Get() != nullptr); - java_lang_reflect_ArtField->SetObjectSize(mirror::ArtField::InstanceSize()); - SetClassRoot(kJavaLangReflectArtField, java_lang_reflect_ArtField.Get()); - java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusResolved, self); - mirror::ArtField::SetClass(java_lang_reflect_ArtField.Get()); - - Handle<mirror::Class> java_lang_reflect_ArtMethod(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::ArtMethod::ClassSize()))); - CHECK(java_lang_reflect_ArtMethod.Get() != nullptr); - java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*))); - SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get()); - java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self); - mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get()); + mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusResolved, self); // Set up array classes for string, field, method Handle<mirror::Class> object_array_string(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), - mirror::ObjectArray<mirror::String>::ClassSize()))); + mirror::ObjectArray<mirror::String>::ClassSize(image_pointer_size_)))); object_array_string->SetComponentType(java_lang_String.Get()); SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get()); - Handle<mirror::Class> object_array_art_method(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), - mirror::ObjectArray<mirror::ArtMethod>::ClassSize()))); - object_array_art_method->SetComponentType(java_lang_reflect_ArtMethod.Get()); - SetClassRoot(kJavaLangReflectArtMethodArrayClass, object_array_art_method.Get()); - - Handle<mirror::Class> object_array_art_field(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), - mirror::ObjectArray<mirror::ArtField>::ClassSize()))); - object_array_art_field->SetComponentType(java_lang_reflect_ArtField.Get()); - SetClassRoot(kJavaLangReflectArtFieldArrayClass, object_array_art_field.Get()); + // Create runtime resolution and imt conflict methods. + runtime->SetResolutionMethod(runtime->CreateResolutionMethod()); + runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod()); + runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod()); // Setup boot_class_path_ and register class_path now that we can use AllocObjectArray to create // DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses // these roots. CHECK_NE(0U, boot_class_path.size()); - for (const DexFile* dex_file : boot_class_path) { - CHECK(dex_file != nullptr); - AppendToBootClassPath(*dex_file); + for (auto& dex_file : boot_class_path) { + CHECK(dex_file.get() != nullptr); + AppendToBootClassPath(self, *dex_file); + opened_dex_files_.push_back(std::move(dex_file)); } // now we can use FindSystemClass @@ -359,39 +444,30 @@ void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class InitializePrimitiveClass(char_class.Get(), Primitive::kPrimChar); SetClassRoot(kPrimitiveChar, char_class.Get()); // needs descriptor - // Create runtime resolution and imt conflict methods. Also setup the default imt. - Runtime* runtime = Runtime::Current(); - runtime->SetResolutionMethod(runtime->CreateResolutionMethod()); - runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod()); - runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod()); - runtime->SetDefaultImt(runtime->CreateDefaultImt(this)); - // Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that // we do not need friend classes or a publicly exposed setter. - quick_generic_jni_trampoline_ = reinterpret_cast<void*>(art_quick_generic_jni_trampoline); - if (!runtime->IsCompiler()) { + quick_generic_jni_trampoline_ = GetQuickGenericJniStub(); + if (!runtime->IsAotCompiler()) { // We need to set up the generic trampolines since we don't have an image. - quick_resolution_trampoline_ = reinterpret_cast<void*>(art_quick_resolution_trampoline); - quick_imt_conflict_trampoline_ = reinterpret_cast<void*>(art_quick_imt_conflict_trampoline); - quick_to_interpreter_bridge_trampoline_ = reinterpret_cast<void*>(art_quick_to_interpreter_bridge); + quick_resolution_trampoline_ = GetQuickResolutionStub(); + quick_imt_conflict_trampoline_ = GetQuickImtConflictStub(); + quick_to_interpreter_bridge_trampoline_ = GetQuickToInterpreterBridge(); } // Object, String and DexCache need to be rerun through FindSystemClass to finish init - java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self); - mirror::Class* Object_class = FindSystemClass(self, "Ljava/lang/Object;"); - CHECK_EQ(java_lang_Object.Get(), Object_class); + mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusNotReady, self); + CHECK_EQ(java_lang_Object.Get(), FindSystemClass(self, "Ljava/lang/Object;")); CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize()); - java_lang_String->SetStatus(mirror::Class::kStatusNotReady, self); + mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusNotReady, self); mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;"); - std::ostringstream os1, os2; - java_lang_String->DumpClass(os1, mirror::Class::kDumpClassFullDetail); - String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail); - CHECK_EQ(java_lang_String.Get(), String_class) << os1.str() << "\n\n" << os2.str(); - CHECK_EQ(java_lang_String->GetObjectSize(), mirror::String::InstanceSize()); - java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady, self); - mirror::Class* DexCache_class = FindSystemClass(self, "Ljava/lang/DexCache;"); - CHECK_EQ(java_lang_String.Get(), String_class); - CHECK_EQ(java_lang_DexCache.Get(), DexCache_class); + if (java_lang_String.Get() != String_class) { + std::ostringstream os1, os2; + java_lang_String->DumpClass(os1, mirror::Class::kDumpClassFullDetail); + String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail); + LOG(FATAL) << os1.str() << "\n\n" << os2.str(); + } + mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusNotReady, self); + CHECK_EQ(java_lang_DexCache.Get(), FindSystemClass(self, "Ljava/lang/DexCache;")); CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize()); // Setup the primitive array type classes - can't be done until Object has a vtable. @@ -401,17 +477,14 @@ void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B")); mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); - mirror::Class* found_char_array_class = FindSystemClass(self, "[C"); - CHECK_EQ(char_array_class.Get(), found_char_array_class); + CHECK_EQ(char_array_class.Get(), FindSystemClass(self, "[C")); SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S")); mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); - mirror::Class* found_int_array_class = FindSystemClass(self, "[I"); - CHECK_EQ(int_array_class.Get(), found_int_array_class); + CHECK_EQ(int_array_class.Get(), FindSystemClass(self, "[I")); - SetClassRoot(kLongArrayClass, FindSystemClass(self, "[J")); - mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass)); + CHECK_EQ(long_array_class.Get(), FindSystemClass(self, "[J")); SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F")); mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); @@ -419,92 +492,97 @@ void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D")); mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); - mirror::Class* found_class_array_class = FindSystemClass(self, "[Ljava/lang/Class;"); - CHECK_EQ(class_array_class.Get(), found_class_array_class); + CHECK_EQ(class_array_class.Get(), FindSystemClass(self, "[Ljava/lang/Class;")); - mirror::Class* found_object_array_class = FindSystemClass(self, "[Ljava/lang/Object;"); - CHECK_EQ(object_array_class.Get(), found_object_array_class); + CHECK_EQ(object_array_class.Get(), FindSystemClass(self, "[Ljava/lang/Object;")); // Setup the single, global copy of "iftable". - mirror::Class* java_lang_Cloneable = FindSystemClass(self, "Ljava/lang/Cloneable;"); - CHECK(java_lang_Cloneable != nullptr); - mirror::Class* java_io_Serializable = FindSystemClass(self, "Ljava/io/Serializable;"); - CHECK(java_io_Serializable != nullptr); + auto java_lang_Cloneable = hs.NewHandle(FindSystemClass(self, "Ljava/lang/Cloneable;")); + CHECK(java_lang_Cloneable.Get() != nullptr); + auto java_io_Serializable = hs.NewHandle(FindSystemClass(self, "Ljava/io/Serializable;")); + CHECK(java_io_Serializable.Get() != nullptr); // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to // crawl up and explicitly list all of the supers as well. - { - mirror::IfTable* array_iftable = array_iftable_.Read(); - array_iftable->SetInterface(0, java_lang_Cloneable); - array_iftable->SetInterface(1, java_io_Serializable); - } - - // Sanity check Class[] and Object[]'s interfaces. - CHECK_EQ(java_lang_Cloneable, mirror::Class::GetDirectInterface(self, class_array_class, 0)); - CHECK_EQ(java_io_Serializable, mirror::Class::GetDirectInterface(self, class_array_class, 1)); - CHECK_EQ(java_lang_Cloneable, mirror::Class::GetDirectInterface(self, object_array_class, 0)); - CHECK_EQ(java_io_Serializable, mirror::Class::GetDirectInterface(self, object_array_class, 1)); + array_iftable_.Read()->SetInterface(0, java_lang_Cloneable.Get()); + array_iftable_.Read()->SetInterface(1, java_io_Serializable.Get()); + + // Sanity check Class[] and Object[]'s interfaces. GetDirectInterface may cause thread + // suspension. + CHECK_EQ(java_lang_Cloneable.Get(), + mirror::Class::GetDirectInterface(self, class_array_class, 0)); + CHECK_EQ(java_io_Serializable.Get(), + mirror::Class::GetDirectInterface(self, class_array_class, 1)); + CHECK_EQ(java_lang_Cloneable.Get(), + mirror::Class::GetDirectInterface(self, object_array_class, 0)); + CHECK_EQ(java_io_Serializable.Get(), + mirror::Class::GetDirectInterface(self, object_array_class, 1)); // Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their // dex_cache_ fields and register them in class_table_. - mirror::Class* Class_class = FindSystemClass(self, "Ljava/lang/Class;"); - CHECK_EQ(java_lang_Class.Get(), Class_class); - - java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusNotReady, self); - mirror::Class* Art_method_class = FindSystemClass(self, "Ljava/lang/reflect/ArtMethod;"); - CHECK_EQ(java_lang_reflect_ArtMethod.Get(), Art_method_class); - - java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusNotReady, self); - mirror::Class* Art_field_class = FindSystemClass(self, "Ljava/lang/reflect/ArtField;"); - CHECK_EQ(java_lang_reflect_ArtField.Get(), Art_field_class); - - mirror::Class* String_array_class = - FindSystemClass(self, class_roots_descriptors_[kJavaLangStringArrayClass]); - CHECK_EQ(object_array_string.Get(), String_array_class); + CHECK_EQ(java_lang_Class.Get(), FindSystemClass(self, "Ljava/lang/Class;")); - mirror::Class* Art_method_array_class = - FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]); - CHECK_EQ(object_array_art_method.Get(), Art_method_array_class); - - mirror::Class* Art_field_array_class = - FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]); - CHECK_EQ(object_array_art_field.Get(), Art_field_array_class); + CHECK_EQ(object_array_string.Get(), + FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass))); // End of special init trickery, subsequent classes may be loaded via FindSystemClass. // Create java.lang.reflect.Proxy root. - mirror::Class* java_lang_reflect_Proxy = FindSystemClass(self, "Ljava/lang/reflect/Proxy;"); - SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy); + SetClassRoot(kJavaLangReflectProxy, FindSystemClass(self, "Ljava/lang/reflect/Proxy;")); + + // Create java.lang.reflect.Field.class root. + auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectField, class_root); + mirror::Field::SetClass(class_root); + + // Create java.lang.reflect.Field array root. + class_root = FindSystemClass(self, "[Ljava/lang/reflect/Field;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectFieldArrayClass, class_root); + mirror::Field::SetArrayClass(class_root); + + // Create java.lang.reflect.Constructor.class root and array root. + class_root = FindSystemClass(self, "Ljava/lang/reflect/Constructor;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectConstructor, class_root); + mirror::Constructor::SetClass(class_root); + class_root = FindSystemClass(self, "[Ljava/lang/reflect/Constructor;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectConstructorArrayClass, class_root); + mirror::Constructor::SetArrayClass(class_root); + + // Create java.lang.reflect.Method.class root and array root. + class_root = FindSystemClass(self, "Ljava/lang/reflect/Method;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectMethod, class_root); + mirror::Method::SetClass(class_root); + class_root = FindSystemClass(self, "[Ljava/lang/reflect/Method;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectMethodArrayClass, class_root); + mirror::Method::SetArrayClass(class_root); // java.lang.ref classes need to be specially flagged, but otherwise are normal classes // finish initializing Reference class - java_lang_ref_Reference->SetStatus(mirror::Class::kStatusNotReady, self); - mirror::Class* Reference_class = FindSystemClass(self, "Ljava/lang/ref/Reference;"); - CHECK_EQ(java_lang_ref_Reference.Get(), Reference_class); + mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusNotReady, self); + CHECK_EQ(java_lang_ref_Reference.Get(), FindSystemClass(self, "Ljava/lang/ref/Reference;")); CHECK_EQ(java_lang_ref_Reference->GetObjectSize(), mirror::Reference::InstanceSize()); - CHECK_EQ(java_lang_ref_Reference->GetClassSize(), mirror::Reference::ClassSize()); - mirror::Class* java_lang_ref_FinalizerReference = - FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); - java_lang_ref_FinalizerReference->SetAccessFlags( - java_lang_ref_FinalizerReference->GetAccessFlags() | - kAccClassIsReference | kAccClassIsFinalizerReference); - mirror::Class* java_lang_ref_PhantomReference = - FindSystemClass(self, "Ljava/lang/ref/PhantomReference;"); - java_lang_ref_PhantomReference->SetAccessFlags( - java_lang_ref_PhantomReference->GetAccessFlags() | - kAccClassIsReference | kAccClassIsPhantomReference); - mirror::Class* java_lang_ref_SoftReference = - FindSystemClass(self, "Ljava/lang/ref/SoftReference;"); - java_lang_ref_SoftReference->SetAccessFlags( - java_lang_ref_SoftReference->GetAccessFlags() | kAccClassIsReference); - mirror::Class* java_lang_ref_WeakReference = - FindSystemClass(self, "Ljava/lang/ref/WeakReference;"); - java_lang_ref_WeakReference->SetAccessFlags( - java_lang_ref_WeakReference->GetAccessFlags() | - kAccClassIsReference | kAccClassIsWeakReference); + CHECK_EQ(java_lang_ref_Reference->GetClassSize(), + mirror::Reference::ClassSize(image_pointer_size_)); + class_root = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); + class_root->SetAccessFlags(class_root->GetAccessFlags() | + kAccClassIsReference | kAccClassIsFinalizerReference); + class_root = FindSystemClass(self, "Ljava/lang/ref/PhantomReference;"); + class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference | + kAccClassIsPhantomReference); + class_root = FindSystemClass(self, "Ljava/lang/ref/SoftReference;"); + class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference); + class_root = FindSystemClass(self, "Ljava/lang/ref/WeakReference;"); + class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference | + kAccClassIsWeakReference); // Setup the ClassLoader, verifying the object_size_. - mirror::Class* java_lang_ClassLoader = FindSystemClass(self, "Ljava/lang/ClassLoader;"); - CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), mirror::ClassLoader::InstanceSize()); - SetClassRoot(kJavaLangClassLoader, java_lang_ClassLoader); + class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;"); + CHECK_EQ(class_root->GetObjectSize(), mirror::ClassLoader::InstanceSize()); + SetClassRoot(kJavaLangClassLoader, class_root); // Set up java.lang.Throwable, java.lang.ClassNotFoundException, and // java.lang.StackTraceElement as a convenience. @@ -550,23 +628,23 @@ void ClassLinker::FinishInit(Thread* self) { mirror::Class* java_lang_ref_FinalizerReference = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); - mirror::ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0); + ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0); CHECK_STREQ(pendingNext->GetName(), "pendingNext"); CHECK_STREQ(pendingNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); - mirror::ArtField* queue = java_lang_ref_Reference->GetInstanceField(1); + ArtField* queue = java_lang_ref_Reference->GetInstanceField(1); CHECK_STREQ(queue->GetName(), "queue"); CHECK_STREQ(queue->GetTypeDescriptor(), "Ljava/lang/ref/ReferenceQueue;"); - mirror::ArtField* queueNext = java_lang_ref_Reference->GetInstanceField(2); + ArtField* queueNext = java_lang_ref_Reference->GetInstanceField(2); CHECK_STREQ(queueNext->GetName(), "queueNext"); CHECK_STREQ(queueNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); - mirror::ArtField* referent = java_lang_ref_Reference->GetInstanceField(3); + ArtField* referent = java_lang_ref_Reference->GetInstanceField(3); CHECK_STREQ(referent->GetName(), "referent"); CHECK_STREQ(referent->GetTypeDescriptor(), "Ljava/lang/Object;"); - mirror::ArtField* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2); + ArtField* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2); CHECK_STREQ(zombie->GetName(), "zombie"); CHECK_STREQ(zombie->GetTypeDescriptor(), "Ljava/lang/Object;"); @@ -596,83 +674,12 @@ void ClassLinker::RunRootClinits() { if (!c->IsArrayClass() && !c->IsPrimitive()) { StackHandleScope<1> hs(self); Handle<mirror::Class> h_class(hs.NewHandle(GetClassRoot(ClassRoot(i)))); - EnsureInitialized(h_class, true, true); + EnsureInitialized(self, h_class, true, true); self->AssertNoPendingException(); } } } -bool ClassLinker::GenerateOatFile(const char* dex_filename, - int oat_fd, - const char* oat_cache_filename, - std::string* error_msg) { - Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC. - std::string dex2oat(Runtime::Current()->GetCompilerExecutable()); - - gc::Heap* heap = Runtime::Current()->GetHeap(); - std::string boot_image_option("--boot-image="); - if (heap->GetImageSpace() == nullptr) { - // TODO If we get a dex2dex compiler working we could maybe use that, OTOH since we are likely - // out of space anyway it might not matter. - *error_msg = StringPrintf("Cannot create oat file for '%s' because we are running " - "without an image.", dex_filename); - return false; - } - boot_image_option += heap->GetImageSpace()->GetImageLocation(); - - std::string dex_file_option("--dex-file="); - dex_file_option += dex_filename; - - std::string oat_fd_option("--oat-fd="); - StringAppendF(&oat_fd_option, "%d", oat_fd); - - std::string oat_location_option("--oat-location="); - oat_location_option += oat_cache_filename; - - std::vector<std::string> argv; - argv.push_back(dex2oat); - argv.push_back("--runtime-arg"); - argv.push_back("-classpath"); - argv.push_back("--runtime-arg"); - argv.push_back(Runtime::Current()->GetClassPathString()); - - Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); - - if (!Runtime::Current()->IsVerificationEnabled()) { - argv.push_back("--compiler-filter=verify-none"); - } - - if (Runtime::Current()->MustRelocateIfPossible()) { - argv.push_back("--runtime-arg"); - argv.push_back("-Xrelocate"); - } else { - argv.push_back("--runtime-arg"); - argv.push_back("-Xnorelocate"); - } - - if (!kIsTargetBuild) { - argv.push_back("--host"); - } - - argv.push_back(boot_image_option); - argv.push_back(dex_file_option); - argv.push_back(oat_fd_option); - argv.push_back(oat_location_option); - const std::vector<std::string>& compiler_options = Runtime::Current()->GetCompilerOptions(); - for (size_t i = 0; i < compiler_options.size(); ++i) { - argv.push_back(compiler_options[i].c_str()); - } - - if (!Exec(argv, error_msg)) { - // Manually delete the file. Ensures there is no garbage left over if the process unexpectedly - // died. Ignore unlink failure, propagate the original error. - TEMP_FAILURE_RETRY(unlink(oat_cache_filename)); - return false; - } - - return true; -} - const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) { WriterMutexLock mu(Thread::Current(), dex_lock_); if (kIsDebugBuild) { @@ -693,903 +700,398 @@ OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) { return *oat_file; } -const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFileForDexFile(const DexFile& dex_file) { - const char* dex_location = dex_file.GetLocation().c_str(); - uint32_t dex_location_checksum = dex_file.GetLocationChecksum(); - return FindOpenedOatDexFile(nullptr, dex_location, &dex_location_checksum); -} - -const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFile(const char* oat_location, - const char* dex_location, - const uint32_t* dex_location_checksum) { - ReaderMutexLock mu(Thread::Current(), dex_lock_); - for (const OatFile* oat_file : oat_files_) { - DCHECK(oat_file != nullptr); +class DexFileAndClassPair : ValueObject { + public: + DexFileAndClassPair(const DexFile* dex_file, size_t current_class_index, bool from_loaded_oat) + : cached_descriptor_(GetClassDescriptor(dex_file, current_class_index)), + dex_file_(dex_file), + current_class_index_(current_class_index), + from_loaded_oat_(from_loaded_oat) {} - if (oat_location != nullptr) { - if (oat_file->GetLocation() != oat_location) { - continue; - } - } + DexFileAndClassPair(const DexFileAndClassPair&) = default; - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, - dex_location_checksum, - false); - if (oat_dex_file != nullptr) { - return oat_dex_file; - } + DexFileAndClassPair& operator=(const DexFileAndClassPair& rhs) { + cached_descriptor_ = rhs.cached_descriptor_; + dex_file_ = rhs.dex_file_; + current_class_index_ = rhs.current_class_index_; + from_loaded_oat_ = rhs.from_loaded_oat_; + return *this; } - return nullptr; -} - -// Loads all multi dex files from the given oat file returning true on success. -// -// Parameters: -// oat_file - the oat file to load from -// dex_location - the dex location used to generate the oat file -// dex_location_checksum - the checksum of the dex_location (may be null for pre-opted files) -// generated - whether or not the oat_file existed before or was just (re)generated -// error_msgs - any error messages will be appended here -// dex_files - the loaded dex_files will be appended here (only if the loading succeeds) -static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file, - const char* dex_location, - const uint32_t* dex_location_checksum, - bool generated, - std::vector<std::string>* error_msgs, - std::vector<const DexFile*>* dex_files) { - if (oat_file == nullptr) { - return false; + const char* GetCachedDescriptor() const { + return cached_descriptor_; } - size_t old_size = dex_files->size(); // To rollback on error. - - bool success = true; - for (size_t i = 0; success; ++i) { - std::string next_name_str = DexFile::GetMultiDexClassesDexName(i, dex_location); - const char* next_name = next_name_str.c_str(); - - uint32_t next_location_checksum; - uint32_t* next_location_checksum_pointer = &next_location_checksum; - std::string error_msg; - if ((i == 0) && (strcmp(next_name, dex_location) == 0)) { - // When i=0 the multidex name should be the same as the location name. We already have the - // checksum it so we don't need to recompute it. - if (dex_location_checksum == nullptr) { - next_location_checksum_pointer = nullptr; - } else { - next_location_checksum = *dex_location_checksum; - } - } else if (!DexFile::GetChecksum(next_name, next_location_checksum_pointer, &error_msg)) { - DCHECK_EQ(false, i == 0 && generated); - next_location_checksum_pointer = nullptr; + bool operator<(const DexFileAndClassPair& rhs) const { + const char* lhsDescriptor = cached_descriptor_; + const char* rhsDescriptor = rhs.cached_descriptor_; + int cmp = strcmp(lhsDescriptor, rhsDescriptor); + if (cmp != 0) { + // Note that the order must be reversed. We want to iterate over the classes in dex files. + // They are sorted lexicographically. Thus, the priority-queue must be a min-queue. + return cmp > 0; } - - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false); - - if (oat_dex_file == nullptr) { - if (i == 0 && generated) { - std::string error_msg; - error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out " - " file'%s'", dex_location, next_location_checksum, - oat_file->GetLocation().c_str()); - error_msgs->push_back(error_msg); - } - break; // Not found, done. - } - - // Checksum test. Test must succeed when generated. - success = !generated; - if (next_location_checksum_pointer != nullptr) { - success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum(); - } - - if (success) { - const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg); - if (dex_file == nullptr) { - success = false; - error_msgs->push_back(error_msg); - } else { - dex_files->push_back(dex_file); - } - } - - // When we generated the file, we expect success, or something is terribly wrong. - CHECK_EQ(false, generated && !success) - << "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str() - << std::hex << " dex_location_checksum=" << next_location_checksum - << " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum(); + return dex_file_ < rhs.dex_file_; } - if (dex_files->size() == old_size) { - success = false; // We did not even find classes.dex + bool DexFileHasMoreClasses() const { + return current_class_index_ + 1 < dex_file_->NumClassDefs(); } - if (success) { - return true; - } else { - // Free all the dex files we have loaded. - auto it = dex_files->begin() + old_size; - auto it_end = dex_files->end(); - for (; it != it_end; it++) { - delete *it; - } - dex_files->erase(dex_files->begin() + old_size, it_end); - - return false; + DexFileAndClassPair GetNext() const { + return DexFileAndClassPair(dex_file_, current_class_index_ + 1, from_loaded_oat_); } -} -// Multidex files make it possible that some, but not all, dex files can be broken/outdated. This -// complicates the loading process, as we should not use an iterative loading process, because that -// would register the oat file and dex files that come before the broken one. Instead, check all -// multidex ahead of time. -bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location, - std::vector<std::string>* error_msgs, - std::vector<const DexFile*>* dex_files) { - // 1) Check whether we have an open oat file. - // This requires a dex checksum, use the "primary" one. - uint32_t dex_location_checksum; - uint32_t* dex_location_checksum_pointer = &dex_location_checksum; - bool have_checksum = true; - std::string checksum_error_msg; - if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) { - // This happens for pre-opted files since the corresponding dex files are no longer on disk. - dex_location_checksum_pointer = nullptr; - have_checksum = false; - } - - bool needs_registering = false; - - const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFile(oat_location, dex_location, - dex_location_checksum_pointer); - std::unique_ptr<const OatFile> open_oat_file( - oat_dex_file != nullptr ? oat_dex_file->GetOatFile() : nullptr); - - // 2) If we do not have an open one, maybe there's one on disk already. - - // In case the oat file is not open, we play a locking game here so - // that if two different processes race to load and register or generate - // (or worse, one tries to open a partial generated file) we will be okay. - // This is actually common with apps that use DexClassLoader to work - // around the dex method reference limit and that have a background - // service running in a separate process. - ScopedFlock scoped_flock; - - if (open_oat_file.get() == nullptr) { - if (oat_location != nullptr) { - // Can only do this if we have a checksum, else error. - if (!have_checksum) { - error_msgs->push_back(checksum_error_msg); - return false; - } - - std::string error_msg; - - // We are loading or creating one in the future. Time to set up the file lock. - if (!scoped_flock.Init(oat_location, &error_msg)) { - error_msgs->push_back(error_msg); - return false; - } - - // TODO Caller specifically asks for this oat_location. We should honor it. Probably? - open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum, - oat_location, &error_msg)); - - if (open_oat_file.get() == nullptr) { - std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s", - dex_location, oat_location, error_msg.c_str()); - VLOG(class_linker) << compound_msg; - error_msgs->push_back(compound_msg); - } - } else { - // TODO: What to lock here? - bool obsolete_file_cleanup_failed; - open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location, - dex_location_checksum_pointer, - kRuntimeISA, error_msgs, - &obsolete_file_cleanup_failed)); - // There's no point in going forward and eventually try to regenerate the - // file if we couldn't remove the obsolete one. Mostly likely we will fail - // with the same error when trying to write the new file. - // TODO: should we maybe do this only when we get permission issues? (i.e. EACCESS). - if (obsolete_file_cleanup_failed) { - return false; - } - } - needs_registering = true; + size_t GetCurrentClassIndex() const { + return current_class_index_; } - // 3) If we have an oat file, check all contained multidex files for our dex_location. - // Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument. - bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, - dex_location_checksum_pointer, - false, error_msgs, dex_files); - if (success) { - const OatFile* oat_file = open_oat_file.release(); // Avoid deleting it. - if (needs_registering) { - // We opened the oat file, so we must register it. - RegisterOatFile(oat_file); - } - // If the file isn't executable we failed patchoat but did manage to get the dex files. - return oat_file->IsExecutable(); - } else { - if (needs_registering) { - // We opened it, delete it. - open_oat_file.reset(); - } else { - open_oat_file.release(); // Do not delete open oat files. - } + bool FromLoadedOat() const { + return from_loaded_oat_; } - // 4) If it's not the case (either no oat file or mismatches), regenerate and load. - - // Need a checksum, fail else. - if (!have_checksum) { - error_msgs->push_back(checksum_error_msg); - return false; - } - - // Look in cache location if no oat_location is given. - std::string cache_location; - if (oat_location == nullptr) { - // Use the dalvik cache. - const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA))); - cache_location = GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str()); - oat_location = cache_location.c_str(); + const DexFile* GetDexFile() const { + return dex_file_; } - bool has_flock = true; - // Definitely need to lock now. - if (!scoped_flock.HasFile()) { - std::string error_msg; - if (!scoped_flock.Init(oat_location, &error_msg)) { - error_msgs->push_back(error_msg); - has_flock = false; - } + void DeleteDexFile() { + delete dex_file_; + dex_file_ = nullptr; } - if (Runtime::Current()->IsDex2OatEnabled() && has_flock && scoped_flock.HasFile()) { - // Create the oat file. - open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(), - oat_location, error_msgs)); - } + private: + static const char* GetClassDescriptor(const DexFile* dex_file, size_t index) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(static_cast<uint16_t>(index)); + return dex_file->StringByTypeIdx(class_def.class_idx_); + } + + const char* cached_descriptor_; + const DexFile* dex_file_; + size_t current_class_index_; + bool from_loaded_oat_; // We only need to compare mismatches between what we load now + // and what was loaded before. Any old duplicates must have been + // OK, and any new "internal" duplicates are as well (they must + // be from multidex, which resolves correctly). +}; - // Failed, bail. - if (open_oat_file.get() == nullptr) { - std::string error_msg; - // dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress. - DexFile::Open(dex_location, dex_location, &error_msg, dex_files); - error_msgs->push_back(error_msg); - return false; +static void AddDexFilesFromOat(const OatFile* oat_file, bool already_loaded, + std::priority_queue<DexFileAndClassPair>* heap) { + const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles(); + for (const OatDexFile* oat_dex_file : oat_dex_files) { + std::string error; + std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error); + if (dex_file.get() == nullptr) { + LOG(WARNING) << "Could not create dex file from oat file: " << error; + } else { + if (dex_file->NumClassDefs() > 0U) { + heap->emplace(dex_file.release(), 0U, already_loaded); + } + } } +} - // Try to load again, but stronger checks. - success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, - dex_location_checksum_pointer, - true, error_msgs, dex_files); - if (success) { - RegisterOatFile(open_oat_file.release()); - return true; +static void AddNext(DexFileAndClassPair* original, + std::priority_queue<DexFileAndClassPair>* heap) { + if (original->DexFileHasMoreClasses()) { + heap->push(original->GetNext()); } else { - return false; + // Need to delete the dex file. + original->DeleteDexFile(); } } -const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_location, - uint32_t dex_location_checksum, - const char* oat_location, - std::string* error_msg) { - std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr, - !Runtime::Current()->IsCompiler(), - error_msg)); - if (oat_file.get() == nullptr) { - *error_msg = StringPrintf("Failed to find existing oat file at %s: %s", oat_location, - error_msg->c_str()); - return nullptr; - } - Runtime* runtime = Runtime::Current(); - const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace(); - if (image_space != nullptr) { - const ImageHeader& image_header = image_space->GetImageHeader(); - uint32_t expected_image_oat_checksum = image_header.GetOatChecksum(); - uint32_t actual_image_oat_checksum = oat_file->GetOatHeader().GetImageFileLocationOatChecksum(); - if (expected_image_oat_checksum != actual_image_oat_checksum) { - *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat checksum of " - "0x%x, found 0x%x", oat_location, expected_image_oat_checksum, - actual_image_oat_checksum); - return nullptr; - } - - uintptr_t expected_image_oat_offset = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()); - uint32_t actual_image_oat_offset = oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(); - if (expected_image_oat_offset != actual_image_oat_offset) { - *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat offset %" - PRIuPTR ", found %ud", oat_location, expected_image_oat_offset, - actual_image_oat_offset); - return nullptr; - } - int32_t expected_patch_delta = image_header.GetPatchDelta(); - int32_t actual_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta(); - if (expected_patch_delta != actual_patch_delta) { - *error_msg = StringPrintf("Failed to find oat file at '%s' with expected patch delta %d, " - " found %d", oat_location, expected_patch_delta, actual_patch_delta); - return nullptr; - } - } - - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, - &dex_location_checksum); - if (oat_dex_file == nullptr) { - *error_msg = StringPrintf("Failed to find oat file at '%s' containing '%s'", oat_location, - dex_location); - return nullptr; - } - uint32_t expected_dex_checksum = dex_location_checksum; - uint32_t actual_dex_checksum = oat_dex_file->GetDexFileLocationChecksum(); - if (expected_dex_checksum != actual_dex_checksum) { - *error_msg = StringPrintf("Failed to find oat file at '%s' with expected dex checksum of 0x%x, " - "found 0x%x", oat_location, expected_dex_checksum, - actual_dex_checksum); - return nullptr; - } - std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(error_msg)); - if (dex_file.get() != nullptr) { - return oat_file.release(); - } else { - return nullptr; +static void FreeDexFilesInHeap(std::priority_queue<DexFileAndClassPair>* heap) { + while (!heap->empty()) { + delete heap->top().GetDexFile(); + heap->pop(); } } -const OatFile* ClassLinker::CreateOatFileForDexLocation(const char* dex_location, - int fd, const char* oat_location, - std::vector<std::string>* error_msgs) { - // Generate the output oat file for the dex file - VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location; - std::string error_msg; - if (!GenerateOatFile(dex_location, fd, oat_location, &error_msg)) { - CHECK(!error_msg.empty()); - error_msgs->push_back(error_msg); - return nullptr; - } - std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr, - !Runtime::Current()->IsCompiler(), - &error_msg)); - if (oat_file.get() == nullptr) { - std::string compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s", - oat_location, error_msg.c_str()); - error_msgs->push_back(compound_msg); +const OatFile* ClassLinker::GetBootOatFile() { + gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); + if (image_space == nullptr) { return nullptr; } - - return oat_file.release(); + return image_space->GetOatFile(); } -bool ClassLinker::VerifyOatImageChecksum(const OatFile* oat_file, - const InstructionSet instruction_set) { - Runtime* runtime = Runtime::Current(); - const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace(); - if (image_space == nullptr) { - return false; - } - uint32_t image_oat_checksum = 0; - if (instruction_set == kRuntimeISA) { - const ImageHeader& image_header = image_space->GetImageHeader(); - image_oat_checksum = image_header.GetOatChecksum(); - } else { - std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie( - image_space->GetImageLocation().c_str(), instruction_set)); - image_oat_checksum = image_header->GetOatChecksum(); +const OatFile* ClassLinker::GetPrimaryOatFile() { + ReaderMutexLock mu(Thread::Current(), dex_lock_); + const OatFile* boot_oat_file = GetBootOatFile(); + if (boot_oat_file != nullptr) { + for (const OatFile* oat_file : oat_files_) { + if (oat_file != boot_oat_file) { + return oat_file; + } + } } - return oat_file->GetOatHeader().GetImageFileLocationOatChecksum() == image_oat_checksum; + return nullptr; } -bool ClassLinker::VerifyOatChecksums(const OatFile* oat_file, - const InstructionSet instruction_set, - std::string* error_msg) { - Runtime* runtime = Runtime::Current(); - const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace(); - if (image_space == nullptr) { - *error_msg = "No image space for verification against"; +// Check for class-def collisions in dex files. +// +// This works by maintaining a heap with one class from each dex file, sorted by the class +// descriptor. Then a dex-file/class pair is continually removed from the heap and compared +// against the following top element. If the descriptor is the same, it is now checked whether +// the two elements agree on whether their dex file was from an already-loaded oat-file or the +// new oat file. Any disagreement indicates a collision. +bool ClassLinker::HasCollisions(const OatFile* oat_file, std::string* error_msg) { + if (!kDuplicateClassesCheck) { return false; } - // If the requested instruction set is the same as the current runtime, - // we can use the checksums directly. If it isn't, we'll have to read the - // image header from the image for the right instruction set. - uint32_t image_oat_checksum = 0; - uintptr_t image_oat_data_begin = 0; - int32_t image_patch_delta = 0; - if (instruction_set == runtime->GetInstructionSet()) { - const ImageHeader& image_header = image_space->GetImageHeader(); - image_oat_checksum = image_header.GetOatChecksum(); - image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()); - image_patch_delta = image_header.GetPatchDelta(); - } else { - std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie( - image_space->GetImageLocation().c_str(), instruction_set)); - image_oat_checksum = image_header->GetOatChecksum(); - image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()); - image_patch_delta = image_header->GetPatchDelta(); - } - const OatHeader& oat_header = oat_file->GetOatHeader(); - bool ret = (oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum); - - // If the oat file is PIC, it doesn't care if/how image was relocated. Ignore these checks. - if (!oat_file->IsPic()) { - ret = ret && (oat_header.GetImagePatchDelta() == image_patch_delta) - && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin); - } - if (!ret) { - *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d, %d) with (0x%x, %" PRIdPTR ", %d)", - oat_file->GetLocation().c_str(), - oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), - oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), - oat_file->GetOatHeader().GetImagePatchDelta(), - image_oat_checksum, image_oat_data_begin, image_patch_delta); - } - return ret; -} - -bool ClassLinker::VerifyOatAndDexFileChecksums(const OatFile* oat_file, - const char* dex_location, - uint32_t dex_location_checksum, - const InstructionSet instruction_set, - std::string* error_msg) { - if (!VerifyOatChecksums(oat_file, instruction_set, error_msg)) { - return false; - } + // Dex files are registered late - once a class is actually being loaded. We have to compare + // against the open oat files. Take the dex_lock_ that protects oat_files_ accesses. + ReaderMutexLock mu(Thread::Current(), dex_lock_); - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, - &dex_location_checksum); - if (oat_dex_file == nullptr) { - *error_msg = StringPrintf("oat file '%s' does not contain contents for '%s' with checksum 0x%x", - oat_file->GetLocation().c_str(), dex_location, dex_location_checksum); - for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { - *error_msg += StringPrintf("\noat file '%s' contains contents for '%s' with checksum 0x%x", - oat_file->GetLocation().c_str(), - oat_dex_file->GetDexFileLocation().c_str(), - oat_dex_file->GetDexFileLocationChecksum()); + std::priority_queue<DexFileAndClassPair> queue; + + // Add dex files from already loaded oat files, but skip boot. + { + const OatFile* boot_oat = GetBootOatFile(); + for (const OatFile* loaded_oat_file : oat_files_) { + if (loaded_oat_file == boot_oat) { + continue; + } + AddDexFilesFromOat(loaded_oat_file, true, &queue); } - return false; } - if (dex_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) { - *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)", - oat_file->GetLocation().c_str(), - oat_dex_file->GetDexFileLocationChecksum(), - dex_location, dex_location_checksum); + if (queue.empty()) { + // No other oat files, return early. return false; } - return true; -} -bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file, - const char* dex_location, - const uint32_t* dex_location_checksum, - std::string* error_msg) { - CHECK(oat_file != nullptr); - CHECK(dex_location != nullptr); - std::unique_ptr<const DexFile> dex_file; - if (dex_location_checksum == nullptr) { - // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is - // up-to-date. This is the common case in user builds for jar's and apk's in the /system - // directory. - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, nullptr); - if (oat_dex_file == nullptr) { - *error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat " - "dex file '%s': %s", oat_file->GetLocation().c_str(), dex_location, - error_msg->c_str()); - return false; - } - dex_file.reset(oat_dex_file->OpenDexFile(error_msg)); - } else { - bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum, - kRuntimeISA, error_msg); - if (!verified) { - return false; + // Add dex files from the oat file to check. + AddDexFilesFromOat(oat_file, false, &queue); + + // Now drain the queue. + while (!queue.empty()) { + DexFileAndClassPair compare_pop = queue.top(); + queue.pop(); + + // Compare against the following elements. + while (!queue.empty()) { + DexFileAndClassPair top = queue.top(); + + if (strcmp(compare_pop.GetCachedDescriptor(), top.GetCachedDescriptor()) == 0) { + // Same descriptor. Check whether it's crossing old-oat-files to new-oat-files. + if (compare_pop.FromLoadedOat() != top.FromLoadedOat()) { + *error_msg = + StringPrintf("Found duplicated class when checking oat files: '%s' in %s and %s", + compare_pop.GetCachedDescriptor(), + compare_pop.GetDexFile()->GetLocation().c_str(), + top.GetDexFile()->GetLocation().c_str()); + FreeDexFilesInHeap(&queue); + return true; + } + // Pop it. + queue.pop(); + AddNext(&top, &queue); + } else { + // Something else. Done here. + break; + } } - dex_file.reset(oat_file->GetOatDexFile(dex_location, - dex_location_checksum)->OpenDexFile(error_msg)); - } - return dex_file.get() != nullptr; -} - -const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation( - const char* dex_location, - const uint32_t* dex_location_checksum, - InstructionSet isa, - std::vector<std::string>* error_msgs, - bool* obsolete_file_cleanup_failed) { - *obsolete_file_cleanup_failed = false; - bool already_opened = false; - std::string dex_location_str(dex_location); - std::unique_ptr<const OatFile> oat_file(OpenOatFileFromDexLocation(dex_location_str, isa, - &already_opened, - obsolete_file_cleanup_failed, - error_msgs)); - std::string error_msg; - if (oat_file.get() == nullptr) { - error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'", - dex_location)); - return nullptr; - } else if (oat_file->IsExecutable() && - !VerifyOatWithDexFile(oat_file.get(), dex_location, - dex_location_checksum, &error_msg)) { - error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location " - "'%s': %s", oat_file->GetLocation().c_str(), dex_location, - error_msg.c_str())); - return nullptr; - } else if (!oat_file->IsExecutable() && - Runtime::Current()->GetHeap()->HasImageSpace() && - !VerifyOatImageChecksum(oat_file.get(), isa)) { - error_msgs->push_back(StringPrintf("Failed to verify non-executable oat file '%s' found for " - "dex location '%s'. Image checksum incorrect.", - oat_file->GetLocation().c_str(), dex_location)); - return nullptr; - } else { - return oat_file.release(); + AddNext(&compare_pop, &queue); } + + return false; } -const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) { - ReaderMutexLock mu(Thread::Current(), dex_lock_); - for (size_t i = 0; i < oat_files_.size(); i++) { - const OatFile* oat_file = oat_files_[i]; - DCHECK(oat_file != nullptr); - if (oat_file->GetLocation() == oat_location) { - return oat_file; - } +std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat( + const char* dex_location, const char* oat_location, + std::vector<std::string>* error_msgs) { + CHECK(error_msgs != nullptr); + + // Verify we aren't holding the mutator lock, which could starve GC if we + // have to generate or relocate an oat file. + Locks::mutator_lock_->AssertNotHeld(Thread::Current()); + + OatFileAssistant oat_file_assistant(dex_location, oat_location, kRuntimeISA, + !Runtime::Current()->IsAotCompiler()); + + // Lock the target oat location to avoid races generating and loading the + // oat file. + std::string error_msg; + if (!oat_file_assistant.Lock(&error_msg)) { + // Don't worry too much if this fails. If it does fail, it's unlikely we + // can generate an oat file anyway. + VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg; } - return nullptr; -} -const OatFile* ClassLinker::OpenOatFileFromDexLocation(const std::string& dex_location, - InstructionSet isa, - bool *already_opened, - bool *obsolete_file_cleanup_failed, - std::vector<std::string>* error_msgs) { - // Find out if we've already opened the file - const OatFile* ret = nullptr; - std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa)); - ret = FindOpenedOatFileFromOatLocation(odex_filename); - if (ret != nullptr) { - *already_opened = true; - return ret; - } - - std::string dalvik_cache; - bool have_android_data = false; - bool have_dalvik_cache = false; - bool is_global_cache = false; - GetDalvikCache(GetInstructionSetString(kRuntimeISA), false, &dalvik_cache, - &have_android_data, &have_dalvik_cache, &is_global_cache); - std::string cache_filename; - if (have_dalvik_cache) { - cache_filename = GetDalvikCacheFilenameOrDie(dex_location.c_str(), dalvik_cache.c_str()); - ret = FindOpenedOatFileFromOatLocation(cache_filename); - if (ret != nullptr) { - *already_opened = true; - return ret; + // Check if we already have an up-to-date oat file open. + const OatFile* source_oat_file = nullptr; + { + ReaderMutexLock mu(Thread::Current(), dex_lock_); + for (const OatFile* oat_file : oat_files_) { + CHECK(oat_file != nullptr); + if (oat_file_assistant.GivenOatFileIsUpToDate(*oat_file)) { + source_oat_file = oat_file; + break; + } } - } else { - // If we need to relocate we should just place odex back where it started. - cache_filename = odex_filename; } - ret = nullptr; + // If we didn't have an up-to-date oat file open, try to load one from disk. + if (source_oat_file == nullptr) { + // Update the oat file on disk if we can. This may fail, but that's okay. + // Best effort is all that matters here. + if (!oat_file_assistant.MakeUpToDate(&error_msg)) { + LOG(WARNING) << error_msg; + } - // We know that neither the odex nor the cache'd version is already in use, if it even exists. - // - // Now we do the following: - // 1) Try and open the odex version - // 2) If present, checksum-verified & relocated correctly return it - // 3) Close the odex version to free up its address space. - // 4) Try and open the cache version - // 5) If present, checksum-verified & relocated correctly return it - // 6) Close the cache version to free up its address space. - // 7) If we should relocate: - // a) If we have opened and checksum-verified the odex version relocate it to - // 'cache_filename' and return it - // b) If we have opened and checksum-verified the cache version relocate it in place and return - // it. This should not happen often (I think only the run-test's will hit this case). - // 8) If the cache-version was present we should delete it since it must be obsolete if we get to - // this point. - // 9) Return nullptr - - *already_opened = false; - const Runtime* runtime = Runtime::Current(); - CHECK(runtime != nullptr); - bool executable = !runtime->IsCompiler(); - - std::string odex_error_msg; - bool should_patch_system = false; - bool odex_checksum_verified = false; - bool have_system_odex = false; - { - // There is a high probability that both these oat files map similar/the same address - // spaces so we must scope them like this so they each gets its turn. - std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr, - nullptr, - executable, &odex_error_msg)); - if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa, - &odex_checksum_verified, - &odex_error_msg)) { - error_msgs->push_back(odex_error_msg); - return odex_oat_file.release(); - } else { - if (odex_checksum_verified) { - // We can just relocate - should_patch_system = true; - odex_error_msg = "Image Patches are incorrect"; + // Get the oat file on disk. + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + if (oat_file.get() != nullptr) { + // Take the file only if it has no collisions, or we must take it because of preopting. + bool accept_oat_file = !HasCollisions(oat_file.get(), &error_msg); + if (!accept_oat_file) { + // Failed the collision check. Print warning. + if (Runtime::Current()->IsDexFileFallbackEnabled()) { + LOG(WARNING) << "Found duplicate classes, falling back to interpreter mode for " + << dex_location; + } else { + LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to " + " load classes for " << dex_location; + } + LOG(WARNING) << error_msg; + + // However, if the app was part of /system and preopted, there is no original dex file + // available. In that case grudgingly accept the oat file. + if (!DexFile::MaybeDex(dex_location)) { + accept_oat_file = true; + LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. " + << "Allow oat file use. This is potentially dangerous."; + } } - if (odex_oat_file.get() != nullptr) { - have_system_odex = true; + + if (accept_oat_file) { + source_oat_file = oat_file.release(); + RegisterOatFile(source_oat_file); } } } - std::string cache_error_msg; - bool should_patch_cache = false; - bool cache_checksum_verified = false; - if (have_dalvik_cache) { - std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr, - nullptr, - executable, &cache_error_msg)); - if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa, - &cache_checksum_verified, - &cache_error_msg)) { - error_msgs->push_back(cache_error_msg); - return cache_oat_file.release(); - } else if (cache_checksum_verified) { - // We can just relocate - should_patch_cache = true; - cache_error_msg = "Image Patches are incorrect"; + std::vector<std::unique_ptr<const DexFile>> dex_files; + + // Load the dex files from the oat file. + if (source_oat_file != nullptr) { + dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location); + if (dex_files.empty()) { + error_msgs->push_back("Failed to open dex files from " + + source_oat_file->GetLocation()); } - } else if (have_android_data) { - // dalvik_cache does not exist but android data does. This means we should be able to create - // it, so we should try. - GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA), true); } - ret = nullptr; - std::string error_msg; - if (runtime->CanRelocate()) { - // Run relocation - gc::space::ImageSpace* space = Runtime::Current()->GetHeap()->GetImageSpace(); - if (space != nullptr) { - const std::string& image_location = space->GetImageLocation(); - if (odex_checksum_verified && should_patch_system) { - ret = PatchAndRetrieveOat(odex_filename, cache_filename, image_location, isa, &error_msg); - } else if (cache_checksum_verified && should_patch_cache) { - CHECK(have_dalvik_cache); - ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg); + // Fall back to running out of the original dex file if we couldn't load any + // dex_files from the oat file. + if (dex_files.empty()) { + if (oat_file_assistant.HasOriginalDexFiles()) { + if (Runtime::Current()->IsDexFileFallbackEnabled()) { + if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) { + LOG(WARNING) << error_msg; + error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)); + } + } else { + error_msgs->push_back("Fallback mode disabled, skipping dex files."); } - } else if (have_system_odex) { - ret = GetInterpretedOnlyOat(odex_filename, isa, &error_msg); - } - } - if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) { - // implicitly: were able to fine where the cached version is but we were unable to use it, - // either as a destination for relocation or to open a file. We should delete it if it is - // there. - if (TEMP_FAILURE_RETRY(unlink(cache_filename.c_str())) != 0) { - std::string rm_error_msg = StringPrintf("Failed to remove obsolete file from %s when " - "searching for dex file %s: %s", - cache_filename.c_str(), dex_location.c_str(), - strerror(errno)); - error_msgs->push_back(rm_error_msg); - VLOG(class_linker) << rm_error_msg; - // Let the caller know that we couldn't remove the obsolete file. - // This is a good indication that further writes may fail as well. - *obsolete_file_cleanup_failed = true; - } - } - if (ret == nullptr) { - VLOG(class_linker) << error_msg; - error_msgs->push_back(error_msg); - std::string relocation_msg; - if (runtime->CanRelocate()) { - relocation_msg = StringPrintf(" and relocation failed"); - } - if (have_dalvik_cache && cache_checksum_verified) { - error_msg = StringPrintf("Failed to open oat file from %s (error %s) or %s " - "(error %s)%s.", odex_filename.c_str(), odex_error_msg.c_str(), - cache_filename.c_str(), cache_error_msg.c_str(), - relocation_msg.c_str()); } else { - error_msg = StringPrintf("Failed to open oat file from %s (error %s) (no " - "dalvik_cache availible)%s.", odex_filename.c_str(), - odex_error_msg.c_str(), relocation_msg.c_str()); + error_msgs->push_back("No original dex files found for dex location " + + std::string(dex_location)); } - VLOG(class_linker) << error_msg; - error_msgs->push_back(error_msg); } - return ret; + return dex_files; } -const OatFile* ClassLinker::GetInterpretedOnlyOat(const std::string& oat_path, - InstructionSet isa, - std::string* error_msg) { - // We open it non-executable - std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg)); - if (output.get() == nullptr) { - return nullptr; - } - if (!Runtime::Current()->GetHeap()->HasImageSpace() || - VerifyOatImageChecksum(output.get(), isa)) { - return output.release(); - } else { - *error_msg = StringPrintf("Could not use oat file '%s', image checksum failed to verify.", - oat_path.c_str()); - return nullptr; - } -} - -const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat, - const std::string& output_oat, - const std::string& image_location, - InstructionSet isa, - std::string* error_msg) { - Runtime* runtime = Runtime::Current(); - DCHECK(runtime != nullptr); - if (!runtime->GetHeap()->HasImageSpace()) { - // We don't have an image space so there is no point in trying to patchoat. - LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted because we are " - << "running without an image. Attempting to use oat file for interpretation."; - return GetInterpretedOnlyOat(input_oat, isa, error_msg); - } - if (!runtime->IsDex2OatEnabled()) { - // We don't have dex2oat so we can assume we don't have patchoat either. We should just use the - // input_oat but make sure we only do interpretation on it's dex files. - LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted due to dex2oat being " - << "disabled. Attempting to use oat file for interpretation"; - return GetInterpretedOnlyOat(input_oat, isa, error_msg); - } - Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC. - std::string patchoat(runtime->GetPatchoatExecutable()); - - std::string isa_arg("--instruction-set="); - isa_arg += GetInstructionSetString(isa); - std::string input_oat_filename_arg("--input-oat-file="); - input_oat_filename_arg += input_oat; - std::string output_oat_filename_arg("--output-oat-file="); - output_oat_filename_arg += output_oat; - std::string patched_image_arg("--patched-image-location="); - patched_image_arg += image_location; - - std::vector<std::string> argv; - argv.push_back(patchoat); - argv.push_back(isa_arg); - argv.push_back(input_oat_filename_arg); - argv.push_back(output_oat_filename_arg); - argv.push_back(patched_image_arg); - - std::string command_line(Join(argv, ' ')); - LOG(INFO) << "Relocate Oat File: " << command_line; - bool success = Exec(argv, error_msg); - if (success) { - std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr, - !runtime->IsCompiler(), error_msg)); - bool checksum_verified = false; - if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified, - error_msg)) { - return output.release(); - } else if (output.get() != nullptr) { - *error_msg = StringPrintf("Patching of oat file '%s' succeeded " - "but output file '%s' failed verifcation: %s", - input_oat.c_str(), output_oat.c_str(), error_msg->c_str()); - } else { - *error_msg = StringPrintf("Patching of oat file '%s' succeeded " - "but was unable to open output file '%s': %s", - input_oat.c_str(), output_oat.c_str(), error_msg->c_str()); - } - } else if (!runtime->IsCompiler()) { - // patchoat failed which means we probably don't have enough room to place the output oat file, - // instead of failing we should just run the interpreter from the dex files in the input oat. - LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file " - << "for interpretation. patchoat failure was: " << *error_msg; - return GetInterpretedOnlyOat(input_oat, isa, error_msg); - } else { - *error_msg = StringPrintf("Patching of oat file '%s to '%s' " - "failed: %s", input_oat.c_str(), output_oat.c_str(), - error_msg->c_str()); +const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) { + ReaderMutexLock mu(Thread::Current(), dex_lock_); + for (size_t i = 0; i < oat_files_.size(); i++) { + const OatFile* oat_file = oat_files_[i]; + DCHECK(oat_file != nullptr); + if (oat_file->GetLocation() == oat_location) { + return oat_file; + } } return nullptr; } -bool ClassLinker::CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa, - bool* checksum_verified, - std::string* error_msg) { - std::string compound_msg("Oat file failed to verify: "); - uint32_t real_image_checksum; - void* real_image_oat_offset; - int32_t real_patch_delta; - const gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); - if (image_space == nullptr) { - *error_msg = "No image space present"; - return false; - } - if (isa == Runtime::Current()->GetInstructionSet()) { - const ImageHeader& image_header = image_space->GetImageHeader(); - real_image_checksum = image_header.GetOatChecksum(); - real_image_oat_offset = image_header.GetOatDataBegin(); - real_patch_delta = image_header.GetPatchDelta(); - } else { - std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie( - image_space->GetImageLocation().c_str(), isa)); - real_image_checksum = image_header->GetOatChecksum(); - real_image_oat_offset = image_header->GetOatDataBegin(); - real_patch_delta = image_header->GetPatchDelta(); +static void SanityCheckArtMethod(ArtMethod* m, mirror::Class* expected_class, + gc::space::ImageSpace* space) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (m->IsRuntimeMethod()) { + CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m); + } else if (m->IsMiranda()) { + CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m); + } else if (expected_class != nullptr) { + CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m); } - - const OatHeader& oat_header = oat_file->GetOatHeader(); - - uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum(); - *checksum_verified = oat_image_checksum == real_image_checksum; - if (!*checksum_verified) { - compound_msg += StringPrintf(" Oat Image Checksum Incorrect (expected 0x%x, recieved 0x%x)", - real_image_checksum, oat_image_checksum); + if (space != nullptr) { + auto& header = space->GetImageHeader(); + auto& methods = header.GetMethodsSection(); + auto offset = reinterpret_cast<uint8_t*>(m) - space->Begin(); + CHECK(methods.Contains(offset)) << m << " not in " << methods; } +} - bool offset_verified; - bool patch_delta_verified; - - if (!oat_file->IsPic()) { - void* oat_image_oat_offset = - reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin()); - offset_verified = oat_image_oat_offset == real_image_oat_offset; - if (!offset_verified) { - compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)", - real_image_oat_offset, oat_image_oat_offset); +static void SanityCheckArtMethodPointerArray( + mirror::PointerArray* arr, mirror::Class* expected_class, size_t pointer_size, + gc::space::ImageSpace* space) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(arr != nullptr); + for (int32_t j = 0; j < arr->GetLength(); ++j) { + auto* method = arr->GetElementPtrSize<ArtMethod*>(j, pointer_size); + // expected_class == null means we are a dex cache. + if (expected_class != nullptr) { + CHECK(method != nullptr); } - - int32_t oat_patch_delta = oat_header.GetImagePatchDelta(); - patch_delta_verified = oat_patch_delta == real_patch_delta; - if (!patch_delta_verified) { - compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)", - real_patch_delta, oat_patch_delta); + if (method != nullptr) { + SanityCheckArtMethod(method, expected_class, space); } - } else { - // If an oat file is PIC, we ignore offset and patching delta. - offset_verified = true; - patch_delta_verified = true; - } - - bool ret = (*checksum_verified && offset_verified && patch_delta_verified); - if (ret) { - *error_msg = compound_msg; } - return ret; } -const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location, - std::string* error_msg) { - const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location); - if (oat_file != nullptr) { - return oat_file; - } - - return OatFile::Open(oat_location, oat_location, nullptr, nullptr, !Runtime::Current()->IsCompiler(), - error_msg); -} - -static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) +static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg); - DCHECK(obj != nullptr); - DCHECK(class_linker != nullptr); - - if (obj->IsArtMethod()) { - mirror::ArtMethod* method = obj->AsArtMethod(); - if (!method->IsNative()) { - method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge); - if (method != Runtime::Current()->GetResolutionMethod()) { - method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); -#if defined(ART_USE_PORTABLE_COMPILER) - method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge()); -#endif + CHECK(obj->GetClass() != nullptr) << "Null class in object " << obj; + CHECK(obj->GetClass()->GetClass() != nullptr) << "Null class class " << obj; + if (obj->IsClass()) { + auto klass = obj->AsClass(); + ArtField* fields[2] = { klass->GetSFields(), klass->GetIFields() }; + size_t num_fields[2] = { klass->NumStaticFields(), klass->NumInstanceFields() }; + for (size_t i = 0; i < 2; ++i) { + for (size_t j = 0; j < num_fields[i]; ++j) { + CHECK_EQ(fields[i][j].GetDeclaringClass(), klass); + } + } + auto* runtime = Runtime::Current(); + auto* image_space = runtime->GetHeap()->GetImageSpace(); + auto pointer_size = runtime->GetClassLinker()->GetImagePointerSize(); + for (auto& m : klass->GetDirectMethods(pointer_size)) { + SanityCheckArtMethod(&m, klass, image_space); + } + for (auto& m : klass->GetVirtualMethods(pointer_size)) { + SanityCheckArtMethod(&m, klass, image_space); + } + auto* vtable = klass->GetVTable(); + if (vtable != nullptr) { + SanityCheckArtMethodPointerArray(vtable, nullptr, pointer_size, image_space); + } + if (klass->ShouldHaveEmbeddedImtAndVTable()) { + for (size_t i = 0; i < mirror::Class::kImtSize; ++i) { + SanityCheckArtMethod(klass->GetEmbeddedImTableEntry(i, pointer_size), nullptr, image_space); + } + for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) { + SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_space); + } + } + auto* iftable = klass->GetIfTable(); + if (iftable != nullptr) { + for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { + if (iftable->GetMethodArrayCount(i) > 0) { + SanityCheckArtMethodPointerArray(iftable->GetMethodArray(i), nullptr, pointer_size, + image_space); + } } } } @@ -1599,20 +1101,20 @@ void ClassLinker::InitFromImage() { VLOG(startup) << "ClassLinker::InitFromImage entering"; CHECK(!init_done_); - Thread* self = Thread::Current(); - gc::Heap* heap = Runtime::Current()->GetHeap(); - gc::space::ImageSpace* space = heap->GetImageSpace(); - dex_cache_image_class_lookup_required_ = true; + Runtime* const runtime = Runtime::Current(); + Thread* const self = Thread::Current(); + gc::Heap* const heap = runtime->GetHeap(); + gc::space::ImageSpace* const space = heap->GetImageSpace(); CHECK(space != nullptr); + image_pointer_size_ = space->GetImageHeader().GetPointerSize(); + dex_cache_image_class_lookup_required_ = true; OatFile& oat_file = GetImageOatFile(space); CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U); CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatDataBegin(), 0U); const char* image_file_location = oat_file.GetOatHeader(). GetStoreValueByKey(OatHeader::kImageLocationKey); CHECK(image_file_location == nullptr || *image_file_location == 0); - portable_resolution_trampoline_ = oat_file.GetOatHeader().GetPortableResolutionTrampoline(); quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline(); - portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline(); quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline(); quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline(); quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge(); @@ -1633,47 +1135,75 @@ void ClassLinker::InitFromImage() { CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(), static_cast<uint32_t>(dex_caches->GetLength())); for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - StackHandleScope<1> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(dex_caches->Get(i))); + StackHandleScope<1> hs2(self); + Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i))); const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(), nullptr); CHECK(oat_dex_file != nullptr) << oat_file.GetLocation() << " " << dex_file_location; std::string error_msg; - const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg); - if (dex_file == nullptr) { + std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg); + if (dex_file.get() == nullptr) { LOG(FATAL) << "Failed to open dex file " << dex_file_location << " from within oat file " << oat_file.GetLocation() << " error '" << error_msg << "'"; + UNREACHABLE(); + } + + if (kSanityCheckObjects) { + SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(), nullptr, + image_pointer_size_, space); } CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum()); - AppendToBootClassPath(*dex_file, dex_cache); + AppendToBootClassPath(*dex_file.get(), dex_cache); + opened_dex_files_.push_back(std::move(dex_file)); } + CHECK(ValidPointerSize(image_pointer_size_)) << image_pointer_size_; + // Set classes on AbstractMethod early so that IsMethod tests can be performed during the live // bitmap walk. - mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod)); - size_t art_method_object_size = mirror::ArtMethod::GetJavaLangReflectArtMethod()->GetObjectSize(); - if (!Runtime::Current()->IsCompiler()) { - // Compiler supports having an image with a different pointer size than the runtime. This - // happens on the host for compile 32 bit tests since we use a 64 bit libart compiler. We may - // also use 32 bit dex2oat on a system with 64 bit apps. - CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(sizeof(void*))) - << sizeof(void*); - } - if (art_method_object_size == mirror::ArtMethod::InstanceSize(4)) { - image_pointer_size_ = 4; - } else { - CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(8)); - image_pointer_size_ = 8; + if (!runtime->IsAotCompiler()) { + // Only the Aot compiler supports having an image with a different pointer size than the + // runtime. This happens on the host for compile 32 bit tests since we use a 64 bit libart + // compiler. We may also use 32 bit dex2oat on a system with 64 bit apps. + CHECK_EQ(image_pointer_size_, sizeof(void*)); + } + + if (kSanityCheckObjects) { + for (int32_t i = 0; i < dex_caches->GetLength(); i++) { + auto* dex_cache = dex_caches->Get(i); + for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) { + auto* field = dex_cache->GetResolvedField(j, image_pointer_size_); + if (field != nullptr) { + CHECK(field->GetDeclaringClass()->GetClass() != nullptr); + } + } + } + heap->VisitObjects(SanityCheckObjectsCallback, nullptr); } // Set entry point to interpreter if in InterpretOnly mode. - if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) { - ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->VisitObjects(InitFromImageInterpretOnlyCallback, this); + if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) { + const auto& header = space->GetImageHeader(); + const auto& methods = header.GetMethodsSection(); + const auto art_method_size = ArtMethod::ObjectSize(image_pointer_size_); + for (uintptr_t pos = 0; pos < methods.Size(); pos += art_method_size) { + auto* method = reinterpret_cast<ArtMethod*>(space->Begin() + pos + methods.Offset()); + if (kIsDebugBuild && !method->IsRuntimeMethod()) { + CHECK(method->GetDeclaringClass() != nullptr); + } + if (!method->IsNative()) { + method->SetEntryPointFromInterpreterPtrSize( + artInterpreterToInterpreterBridge, image_pointer_size_); + if (!method->IsRuntimeMethod() && method != runtime->GetResolutionMethod()) { + method->SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), + image_pointer_size_); + } + } + } } // reinit class_roots_ @@ -1684,8 +1214,13 @@ void ClassLinker::InitFromImage() { array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable()); DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable()); // String class root was set above + mirror::Field::SetClass(GetClassRoot(kJavaLangReflectField)); + mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass)); + mirror::Constructor::SetClass(GetClassRoot(kJavaLangReflectConstructor)); + mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass)); + mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod)); + mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass)); mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); - mirror::ArtField::SetClass(GetClassRoot(kJavaLangReflectArtField)); mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); mirror::CharArray::SetArrayClass(GetClassRoot(kCharArrayClass)); @@ -1702,19 +1237,56 @@ void ClassLinker::InitFromImage() { VLOG(startup) << "ClassLinker::InitFromImage exiting"; } -void ClassLinker::VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags) { +bool ClassLinker::ClassInClassTable(mirror::Class* klass) { + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + auto it = class_table_.Find(GcRoot<mirror::Class>(klass)); + if (it == class_table_.end()) { + return false; + } + return it->Read() == klass; +} + +void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor( + visitor, RootInfo(kRootStickyClass)); if ((flags & kVisitRootFlagAllRoots) != 0) { + // Argument for how root visiting deals with ArtField and ArtMethod roots. + // There is 3 GC cases to handle: + // Non moving concurrent: + // This case is easy to handle since the reference members of ArtMethod and ArtFields are held + // live by the class and class roots. In this case we probably don't even need to call + // VisitNativeRoots. + // + // Moving non-concurrent: + // This case needs to call visit VisitNativeRoots in case the classes or dex cache arrays move. + // To prevent missing roots, this case needs to ensure that there is no + // suspend points between the point which we allocate ArtMethod arrays and place them in a + // class which is in the class table. + // + // Moving concurrent: + // Need to make sure to not copy ArtMethods without doing read barriers since the roots are + // marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy. for (GcRoot<mirror::Class>& root : class_table_) { - root.VisitRoot(callback, arg, RootInfo(kRootStickyClass)); + buffered_visitor.VisitRoot(root); + if ((flags & kVisitRootFlagNonMoving) == 0) { + // Don't bother visiting ArtField and ArtMethod if kVisitRootFlagNonMoving is set since + // these roots are all reachable from the class or dex cache. + root.Read()->VisitNativeRoots(buffered_visitor, image_pointer_size_); + } } + // PreZygote classes can't move so we won't need to update fields' declaring classes. for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) { - root.VisitRoot(callback, arg, RootInfo(kRootStickyClass)); + buffered_visitor.VisitRoot(root); + if ((flags & kVisitRootFlagNonMoving) == 0) { + root.Read()->VisitNativeRoots(buffered_visitor, image_pointer_size_); + } } } else if ((flags & kVisitRootFlagNewRoots) != 0) { for (auto& root : new_class_roots_) { mirror::Class* old_ref = root.Read<kWithoutReadBarrier>(); - root.VisitRoot(callback, arg, RootInfo(kRootStickyClass)); + old_ref->VisitNativeRoots(buffered_visitor, image_pointer_size_); + root.VisitRoot(visitor, RootInfo(kRootStickyClass)); mirror::Class* new_ref = root.Read<kWithoutReadBarrier>(); if (UNLIKELY(new_ref != old_ref)) { // Uh ohes, GC moved a root in the log. Need to search the class_table and update the @@ -1726,6 +1298,7 @@ void ClassLinker::VisitClassRoots(RootCallback* callback, void* arg, VisitRootFl } } } + buffered_visitor.Flush(); // Flush before clearing new_class_roots_. if ((flags & kVisitRootFlagClearRootLog) != 0) { new_class_roots_.clear(); } @@ -1741,18 +1314,18 @@ void ClassLinker::VisitClassRoots(RootCallback* callback, void* arg, VisitRootFl // Keep in sync with InitCallback. Anything we visit, we need to // reinit references to when reinitializing a ClassLinker from a // mapped image. -void ClassLinker::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) { - class_roots_.VisitRoot(callback, arg, RootInfo(kRootVMInternal)); - Thread* self = Thread::Current(); +void ClassLinker::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) { + class_roots_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + Thread* const self = Thread::Current(); { ReaderMutexLock mu(self, dex_lock_); if ((flags & kVisitRootFlagAllRoots) != 0) { for (GcRoot<mirror::DexCache>& dex_cache : dex_caches_) { - dex_cache.VisitRoot(callback, arg, RootInfo(kRootVMInternal)); + dex_cache.VisitRoot(visitor, RootInfo(kRootVMInternal)); } } else if ((flags & kVisitRootFlagNewRoots) != 0) { for (size_t index : new_dex_cache_roots_) { - dex_caches_[index].VisitRoot(callback, arg, RootInfo(kRootVMInternal)); + dex_caches_[index].VisitRoot(visitor, RootInfo(kRootVMInternal)); } } if ((flags & kVisitRootFlagClearRootLog) != 0) { @@ -1764,11 +1337,10 @@ void ClassLinker::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags f log_new_dex_caches_roots_ = false; } } - VisitClassRoots(callback, arg, flags); - array_iftable_.VisitRoot(callback, arg, RootInfo(kRootVMInternal)); - DCHECK(!array_iftable_.IsNull()); - for (size_t i = 0; i < kFindArrayCacheSize; ++i) { - find_array_class_cache_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal)); + VisitClassRoots(visitor, flags); + array_iftable_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + for (GcRoot<mirror::Class>& root : find_array_class_cache_) { + root.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); } } @@ -1829,7 +1401,7 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* ar } else { Thread* self = Thread::Current(); StackHandleScope<1> hs(self); - Handle<mirror::ObjectArray<mirror::Class>> classes = + MutableHandle<mirror::ObjectArray<mirror::Class>> classes = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr); GetClassesVisitorArrayArg local_arg; local_arg.classes = &classes; @@ -1839,7 +1411,7 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* ar while (!local_arg.success) { size_t class_table_size; { - ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); class_table_size = class_table_.Size() + pre_zygote_class_table_.Size(); } mirror::Class* class_type = mirror::Class::GetJavaLangClass(); @@ -1865,62 +1437,68 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* ar ClassLinker::~ClassLinker() { mirror::Class::ResetClass(); - mirror::String::ResetClass(); + mirror::Constructor::ResetClass(); + mirror::Field::ResetClass(); + mirror::Method::ResetClass(); mirror::Reference::ResetClass(); - mirror::ArtField::ResetClass(); - mirror::ArtMethod::ResetClass(); + mirror::StackTraceElement::ResetClass(); + mirror::String::ResetClass(); + mirror::Throwable::ResetClass(); mirror::BooleanArray::ResetArrayClass(); mirror::ByteArray::ResetArrayClass(); mirror::CharArray::ResetArrayClass(); + mirror::Constructor::ResetArrayClass(); mirror::DoubleArray::ResetArrayClass(); + mirror::Field::ResetArrayClass(); mirror::FloatArray::ResetArrayClass(); + mirror::Method::ResetArrayClass(); mirror::IntArray::ResetArrayClass(); mirror::LongArray::ResetArrayClass(); mirror::ShortArray::ResetArrayClass(); - mirror::Throwable::ResetClass(); - mirror::StackTraceElement::ResetClass(); - STLDeleteElements(&boot_class_path_); STLDeleteElements(&oat_files_); } +mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) { + return down_cast<mirror::PointerArray*>(image_pointer_size_ == 8u ? + static_cast<mirror::Array*>(mirror::LongArray::Alloc(self, length)) : + static_cast<mirror::Array*>(mirror::IntArray::Alloc(self, length))); +} + mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) { - gc::Heap* heap = Runtime::Current()->GetHeap(); - StackHandleScope<16> hs(self); - Handle<mirror::Class> dex_cache_class(hs.NewHandle(GetClassRoot(kJavaLangDexCache))); - Handle<mirror::DexCache> dex_cache( - hs.NewHandle(down_cast<mirror::DexCache*>( - heap->AllocObject<true>(self, dex_cache_class.Get(), dex_cache_class->GetObjectSize(), - VoidFunctor())))); + StackHandleScope<6> hs(self); + auto dex_cache(hs.NewHandle(down_cast<mirror::DexCache*>( + GetClassRoot(kJavaLangDexCache)->AllocObject(self)))); if (dex_cache.Get() == nullptr) { + self->AssertPendingOOMException(); return nullptr; } - Handle<mirror::String> - location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str()))); + auto location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str()))); if (location.Get() == nullptr) { + self->AssertPendingOOMException(); return nullptr; } - Handle<mirror::ObjectArray<mirror::String>> - strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds()))); + auto strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds()))); if (strings.Get() == nullptr) { + self->AssertPendingOOMException(); return nullptr; } - Handle<mirror::ObjectArray<mirror::Class>> - types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds()))); + auto types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds()))); if (types.Get() == nullptr) { + self->AssertPendingOOMException(); return nullptr; } - Handle<mirror::ObjectArray<mirror::ArtMethod>> - methods(hs.NewHandle(AllocArtMethodArray(self, dex_file.NumMethodIds()))); + auto methods(hs.NewHandle(AllocPointerArray(self, dex_file.NumMethodIds()))); if (methods.Get() == nullptr) { + self->AssertPendingOOMException(); return nullptr; } - Handle<mirror::ObjectArray<mirror::ArtField>> - fields(hs.NewHandle(AllocArtFieldArray(self, dex_file.NumFieldIds()))); + auto fields(hs.NewHandle(AllocPointerArray(self, dex_file.NumFieldIds()))); if (fields.Get() == nullptr) { + self->AssertPendingOOMException(); return nullptr; } dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(), - fields.Get()); + fields.Get(), image_pointer_size_); return dex_cache.Get(); } @@ -1933,7 +1511,7 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Cl heap->AllocObject<true>(self, java_lang_Class, class_size, visitor) : heap->AllocNonMovableObject<true>(self, java_lang_Class, class_size, visitor); if (UNLIKELY(k == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. + self->AssertPendingOOMException(); return nullptr; } return k->AsClass(); @@ -1943,16 +1521,6 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, uint32_t class_size) { return AllocClass(self, GetClassRoot(kJavaLangClass), class_size); } -mirror::ArtField* ClassLinker::AllocArtField(Thread* self) { - return down_cast<mirror::ArtField*>( - GetClassRoot(kJavaLangReflectArtField)->AllocNonMovableObject(self)); -} - -mirror::ArtMethod* ClassLinker::AllocArtMethod(Thread* self) { - return down_cast<mirror::ArtMethod*>( - GetClassRoot(kJavaLangReflectArtMethod)->AllocNonMovableObject(self)); -} - mirror::ObjectArray<mirror::StackTraceElement>* ClassLinker::AllocStackTraceElementArray( Thread* self, size_t length) { return mirror::ObjectArray<mirror::StackTraceElement>::Alloc( @@ -1983,7 +1551,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor, } CHECK(h_class->IsRetired()); // Get the updated class from class table. - klass = LookupClass(descriptor, ComputeModifiedUtf8Hash(descriptor), + klass = LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor), h_class.Get()->GetClassLoader()); } @@ -1995,7 +1563,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor, // Check for circular dependencies between classes. if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) { ThrowClassCircularityError(h_class.Get()); - h_class->SetStatus(mirror::Class::kStatusError, self); + mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self); return nullptr; } // Wait for the pending initialization to complete. @@ -2028,90 +1596,124 @@ ClassPathEntry FindInClassPath(const char* descriptor, return ClassPathEntry(nullptr, nullptr); } -mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, - Thread* self, const char* descriptor, - size_t hash, - Handle<mirror::ClassLoader> class_loader) { +static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + mirror::ClassLoader* class_loader) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return class_loader == nullptr || + class_loader->GetClass() == + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader); +} + +bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + Thread* self, const char* descriptor, + size_t hash, + Handle<mirror::ClassLoader> class_loader, + mirror::Class** result) { + // Termination case: boot class-loader. + if (IsBootClassLoader(soa, class_loader.Get())) { + // The boot class loader, search the boot class path. + ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); + if (pair.second != nullptr) { + mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr); + if (klass != nullptr) { + *result = EnsureResolved(self, descriptor, klass); + } else { + *result = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), + *pair.first, *pair.second); + } + if (*result == nullptr) { + CHECK(self->IsExceptionPending()) << descriptor; + self->ClearException(); + } + } else { + *result = nullptr; + } + return true; + } + + // Unsupported class-loader? if (class_loader->GetClass() != - soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) || - class_loader->GetParent()->GetClass() != - soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) { - return nullptr; + soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) { + *result = nullptr; + return false; } - ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); - // Check if this would be found in the parent boot class loader. - if (pair.second != nullptr) { - mirror::Class* klass = LookupClass(descriptor, hash, nullptr); - if (klass != nullptr) { - return EnsureResolved(self, descriptor, klass); - } - klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first, - *pair.second); - if (klass != nullptr) { - return klass; - } - CHECK(self->IsExceptionPending()) << descriptor; - self->ClearException(); - } else { - // RegisterDexFile may allocate dex caches (and cause thread suspension). - StackHandleScope<3> hs(self); - // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. - // We need to get the DexPathList and loop through it. - Handle<mirror::ArtField> cookie_field = - hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie)); - Handle<mirror::ArtField> dex_file_field = - hs.NewHandle( - soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList$Element_dexFile)); - mirror::Object* dex_path_list = - soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)-> - GetObject(class_loader.Get()); - if (dex_path_list != nullptr && dex_file_field.Get() != nullptr && - cookie_field.Get() != nullptr) { - // DexPathList has an array dexElements of Elements[] which each contain a dex file. - mirror::Object* dex_elements_obj = - soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> - GetObject(dex_path_list); - // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look - // at the mCookie which is a DexFile vector. - if (dex_elements_obj != nullptr) { - Handle<mirror::ObjectArray<mirror::Object>> dex_elements = - hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); - for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { - mirror::Object* element = dex_elements->GetWithoutChecks(i); - if (element == nullptr) { - // Should never happen, fall back to java code to throw a NPE. + + // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). + StackHandleScope<4> hs(self); + Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent())); + bool recursive_result = FindClassInPathClassLoader(soa, self, descriptor, hash, h_parent, result); + + if (!recursive_result) { + // Something wrong up the chain. + return false; + } + + if (*result != nullptr) { + // Found the class up the chain. + return true; + } + + // Handle this step. + // Handle as if this is the child PathClassLoader. + // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. + // We need to get the DexPathList and loop through it. + ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* const dex_file_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + mirror::Object* dex_path_list = + soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)-> + GetObject(class_loader.Get()); + if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) { + // DexPathList has an array dexElements of Elements[] which each contain a dex file. + mirror::Object* dex_elements_obj = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> + GetObject(dex_path_list); + // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look + // at the mCookie which is a DexFile vector. + if (dex_elements_obj != nullptr) { + Handle<mirror::ObjectArray<mirror::Object>> dex_elements = + hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); + for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { + mirror::Object* element = dex_elements->GetWithoutChecks(i); + if (element == nullptr) { + // Should never happen, fall back to java code to throw a NPE. + break; + } + mirror::Object* dex_file = dex_file_field->GetObject(element); + if (dex_file != nullptr) { + mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray(); + if (long_array == nullptr) { + // This should never happen so log a warning. + LOG(WARNING) << "Null DexFile::mCookie for " << descriptor; break; } - mirror::Object* dex_file = dex_file_field->GetObject(element); - if (dex_file != nullptr) { - const uint64_t cookie = cookie_field->GetLong(dex_file); - auto* dex_files = - reinterpret_cast<std::vector<const DexFile*>*>(static_cast<uintptr_t>(cookie)); - if (dex_files == nullptr) { - // This should never happen so log a warning. - LOG(WARNING) << "Null DexFile::mCookie for " << descriptor; - break; - } - for (const DexFile* dex_file : *dex_files) { - const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash); - if (dex_class_def != nullptr) { - RegisterDexFile(*dex_file); - mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader, *dex_file, - *dex_class_def); - if (klass == nullptr) { - CHECK(self->IsExceptionPending()) << descriptor; - self->ClearException(); - return nullptr; - } - return klass; + int32_t long_array_size = long_array->GetLength(); + for (int32_t j = 0; j < long_array_size; ++j) { + const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>( + long_array->GetWithoutChecks(j))); + const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash); + if (dex_class_def != nullptr) { + RegisterDexFile(*cp_dex_file); + mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader, + *cp_dex_file, *dex_class_def); + if (klass == nullptr) { + CHECK(self->IsExceptionPending()) << descriptor; + self->ClearException(); + // TODO: Is it really right to break here, and not check the other dex files? + return true; } + *result = klass; + return true; } } } } } + self->AssertNoPendingException(); } - return nullptr; + + // Result is still null from the parent call, no need to set it again... + return true; } mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, @@ -2126,7 +1728,7 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, } const size_t hash = ComputeModifiedUtf8Hash(descriptor); // Find the class in the loaded classes table. - mirror::Class* klass = LookupClass(descriptor, hash, class_loader.Get()); + mirror::Class* klass = LookupClass(self, descriptor, hash, class_loader.Get()); if (klass != nullptr) { return EnsureResolved(self, descriptor, klass); } @@ -2144,42 +1746,32 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to // trigger the chaining with a proper stack trace. mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); - self->SetException(ThrowLocation(), pre_allocated); + self->SetException(pre_allocated); return nullptr; } - } else if (Runtime::Current()->UseCompileTimeClassPath()) { - // First try with the bootstrap class loader. - if (class_loader.Get() != nullptr) { - klass = LookupClass(descriptor, hash, nullptr); - if (klass != nullptr) { - return EnsureResolved(self, descriptor, klass); - } - } - // If the lookup failed search the boot class path. We don't perform a recursive call to avoid - // a NoClassDefFoundError being allocated. - ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); - if (pair.second != nullptr) { - return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first, - *pair.second); - } - // Next try the compile time class path. - const std::vector<const DexFile*>* class_path; - { - ScopedObjectAccessUnchecked soa(self); - ScopedLocalRef<jobject> jclass_loader(soa.Env(), - soa.AddLocalReference<jobject>(class_loader.Get())); - class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get()); - } - pair = FindInClassPath(descriptor, hash, *class_path); - if (pair.second != nullptr) { - return DefineClass(self, descriptor, hash, class_loader, *pair.first, *pair.second); - } } else { ScopedObjectAccessUnchecked soa(self); - mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader); - if (klass != nullptr) { - return klass; + mirror::Class* cp_klass; + if (FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) { + // The chain was understood. So the value in cp_klass is either the class we were looking + // for, or not found. + if (cp_klass != nullptr) { + return cp_klass; + } + // TODO: We handle the boot classpath loader in FindClassInPathClassLoader. Try to unify this + // and the branch above. TODO: throw the right exception here. + + // We'll let the Java-side rediscover all this and throw the exception with the right stack + // trace. } + + if (Runtime::Current()->IsAotCompiler()) { + // Oops, compile-time, can't run actual class-loader code. + mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + return nullptr; + } + ScopedLocalRef<jobject> class_loader_object(soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get())); std::string class_name_string(DescriptorToDot(descriptor)); @@ -2202,17 +1794,15 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, return nullptr; } else if (result.get() == nullptr) { // broken loader - throw NPE to be compatible with Dalvik - ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s", - class_name_string.c_str()).c_str()); + ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s", + class_name_string.c_str()).c_str()); return nullptr; } else { // success, return mirror::Class* return soa.Decode<mirror::Class*>(result.get()); } } - - ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str()); - return nullptr; + UNREACHABLE(); } mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash, @@ -2235,10 +1825,6 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, si klass.Assign(GetClassRoot(kJavaLangRefReference)); } else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) { klass.Assign(GetClassRoot(kJavaLangDexCache)); - } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtField;") == 0) { - klass.Assign(GetClassRoot(kJavaLangReflectArtField)); - } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) { - klass.Assign(GetClassRoot(kJavaLangReflectArtMethod)); } } @@ -2254,16 +1840,17 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, si return nullptr; } klass->SetDexCache(FindDexCache(dex_file)); - LoadClass(dex_file, dex_class_def, klass, class_loader.Get()); - ObjectLock<mirror::Class> lock(self, klass); - if (self->IsExceptionPending()) { - // An exception occured during load, set status to erroneous while holding klass' lock in case - // notification is necessary. - if (!klass->IsErroneous()) { - klass->SetStatus(mirror::Class::kStatusError, self); + + SetupClass(dex_file, dex_class_def, klass, class_loader.Get()); + + // Mark the string class by setting its access flag. + if (UNLIKELY(!init_done_)) { + if (strcmp(descriptor, "Ljava/lang/String;") == 0) { + klass->SetStringClass(); } - return nullptr; } + + ObjectLock<mirror::Class> lock(self, klass); klass->SetClinitThreadId(self->GetTid()); // Add the newly loaded class to the loaded classes table. @@ -2274,12 +1861,26 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, si return EnsureResolved(self, descriptor, existing); } + // Load the fields and other things after we are inserted in the table. This is so that we don't + // end up allocating unfree-able linear alloc resources and then lose the race condition. The + // other reason is that the field roots are only visited from the class table. So we need to be + // inserted before we allocate / fill in these fields. + LoadClass(self, dex_file, dex_class_def, klass); + if (self->IsExceptionPending()) { + // An exception occured during load, set status to erroneous while holding klass' lock in case + // notification is necessary. + if (!klass->IsErroneous()) { + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + } + return nullptr; + } + // Finish loading (if necessary) by finding parents CHECK(!klass->IsLoaded()); if (!LoadSuperAndInterfaces(klass, dex_file)) { // Loading failed. if (!klass->IsErroneous()) { - klass->SetStatus(mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); } return nullptr; } @@ -2289,19 +1890,29 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, si // TODO: Use fast jobjects? auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr); - mirror::Class* new_class = nullptr; - if (!LinkClass(self, descriptor, klass, interfaces, &new_class)) { + MutableHandle<mirror::Class> h_new_class = hs.NewHandle<mirror::Class>(nullptr); + if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) { // Linking failed. if (!klass->IsErroneous()) { - klass->SetStatus(mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); } return nullptr; } self->AssertNoPendingException(); - CHECK(new_class != nullptr) << descriptor; - CHECK(new_class->IsResolved()) << descriptor; + CHECK(h_new_class.Get() != nullptr) << descriptor; + CHECK(h_new_class->IsResolved()) << descriptor; - Handle<mirror::Class> new_class_h(hs.NewHandle(new_class)); + // Instrumentation may have updated entrypoints for all methods of all + // classes. However it could not update methods of this class while we + // were loading it. Now the class is resolved, we can update entrypoints + // as required by instrumentation. + if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { + // We must be in the kRunnable state to prevent instrumentation from + // suspending all threads to update entrypoints while we are doing it + // for this class. + DCHECK_EQ(self->GetState(), kRunnable); + Runtime::Current()->GetInstrumentation()->InstallStubsForClass(h_new_class.Get()); + } /* * We send CLASS_PREPARE events to the debugger from here. The @@ -2314,15 +1925,17 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, si * The class has been prepared and resolved but possibly not yet verified * at this point. */ - Dbg::PostClassPrepare(new_class_h.Get()); + Dbg::PostClassPrepare(h_new_class.Get()); - return new_class_h.Get(); + return h_new_class.Get(); } uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { - const byte* class_data = dex_file.GetClassData(dex_class_def); + const uint8_t* class_data = dex_file.GetClassData(dex_class_def); size_t num_ref = 0; + size_t num_8 = 0; + size_t num_16 = 0; size_t num_32 = 0; size_t num_64 = 0; if (class_data != nullptr) { @@ -2330,35 +1943,53 @@ uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, const DexFile::FieldId& field_id = dex_file.GetFieldId(it.GetMemberIndex()); const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); char c = descriptor[0]; - if (c == 'L' || c == '[') { - num_ref++; - } else if (c == 'J' || c == 'D') { - num_64++; - } else { - num_32++; + switch (c) { + case 'L': + case '[': + num_ref++; + break; + case 'J': + case 'D': + num_64++; + break; + case 'I': + case 'F': + num_32++; + break; + case 'S': + case 'C': + num_16++; + break; + case 'B': + case 'Z': + num_8++; + break; + default: + LOG(FATAL) << "Unknown descriptor: " << c; + UNREACHABLE(); } } } - return mirror::Class::ComputeClassSize(false, 0, num_32, num_64, num_ref); + return mirror::Class::ComputeClassSize(false, 0, num_8, num_16, num_32, num_64, num_ref, + image_pointer_size_); } -bool ClassLinker::FindOatClass(const DexFile& dex_file, - uint16_t class_def_idx, - OatFile::OatClass* oat_class) { - DCHECK(oat_class != nullptr); +OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, + bool* found) { DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16); - const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFileForDexFile(dex_file); + const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr) { - return false; + *found = false; + return OatFile::OatClass::Invalid(); } - *oat_class = oat_dex_file->GetOatClass(class_def_idx); - return true; + *found = true; + return oat_dex_file->GetOatClass(class_def_idx); } static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); - const byte* class_data = dex_file.GetClassData(class_def); + const uint8_t* class_data = dex_file.GetClassData(class_def); CHECK(class_data != nullptr); ClassDataItemIterator it(dex_file, class_data); // Skip fields @@ -2386,11 +2017,10 @@ static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16 } DCHECK(!it.HasNext()); LOG(FATAL) << "Failed to find method index " << method_idx << " in " << dex_file.GetLocation(); - return 0; + UNREACHABLE(); } -bool ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method) { - DCHECK(oat_method != nullptr); +const OatFile::OatMethod ClassLinker::FindOatMethodFor(ArtMethod* method, bool* found) { // Although we overwrite the trampoline of non-static methods, we may get here via the resolution // method for direct methods (or virtual methods made direct). mirror::Class* declaring_class = method->GetDeclaringClass(); @@ -2403,141 +2033,99 @@ bool ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod // by search for its position in the declared virtual methods. oat_method_index = declaring_class->NumDirectMethods(); size_t end = declaring_class->NumVirtualMethods(); - bool found = false; + bool found_virtual = false; for (size_t i = 0; i < end; i++) { // Check method index instead of identity in case of duplicate method definitions. if (method->GetDexMethodIndex() == - declaring_class->GetVirtualMethod(i)->GetDexMethodIndex()) { - found = true; + declaring_class->GetVirtualMethod(i, image_pointer_size_)->GetDexMethodIndex()) { + found_virtual = true; break; } oat_method_index++; } - CHECK(found) << "Didn't find oat method index for virtual method: " << PrettyMethod(method); + CHECK(found_virtual) << "Didn't find oat method index for virtual method: " + << PrettyMethod(method); } DCHECK_EQ(oat_method_index, GetOatMethodIndexFromMethodIndex(*declaring_class->GetDexCache()->GetDexFile(), method->GetDeclaringClass()->GetDexClassDefIndex(), method->GetDexMethodIndex())); - OatFile::OatClass oat_class; - if (!FindOatClass(*declaring_class->GetDexCache()->GetDexFile(), - declaring_class->GetDexClassDefIndex(), - &oat_class)) { - return false; + OatFile::OatClass oat_class = FindOatClass(*declaring_class->GetDexCache()->GetDexFile(), + declaring_class->GetDexClassDefIndex(), + found); + if (!(*found)) { + return OatFile::OatMethod::Invalid(); } - - *oat_method = oat_class.GetOatMethod(oat_method_index); - return true; + return oat_class.GetOatMethod(oat_method_index); } // Special case to get oat code without overwriting a trampoline. -const void* ClassLinker::GetQuickOatCodeFor(mirror::ArtMethod* method) { +const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) { CHECK(!method->IsAbstract()) << PrettyMethod(method); if (method->IsProxyMethod()) { return GetQuickProxyInvokeHandler(); } - OatFile::OatMethod oat_method; - const void* result = nullptr; - if (FindOatMethodFor(method, &oat_method)) { - result = oat_method.GetQuickCode(); - } - - if (result == nullptr) { - if (method->IsNative()) { - // No code and native? Use generic trampoline. - result = GetQuickGenericJniTrampoline(); -#if defined(ART_USE_PORTABLE_COMPILER) - } else if (method->IsPortableCompiled()) { - // No code? Do we expect portable code? - result = GetQuickToPortableBridge(); -#endif - } else { - // No code? You must mean to go into the interpreter. - result = GetQuickToInterpreterBridge(); + bool found; + OatFile::OatMethod oat_method = FindOatMethodFor(method, &found); + if (found) { + auto* code = oat_method.GetQuickCode(); + if (code != nullptr) { + return code; } } - return result; -} - -#if defined(ART_USE_PORTABLE_COMPILER) -const void* ClassLinker::GetPortableOatCodeFor(mirror::ArtMethod* method, - bool* have_portable_code) { - CHECK(!method->IsAbstract()) << PrettyMethod(method); - *have_portable_code = false; - if (method->IsProxyMethod()) { - return GetPortableProxyInvokeHandler(); - } - OatFile::OatMethod oat_method; - const void* result = nullptr; - const void* quick_code = nullptr; - if (FindOatMethodFor(method, &oat_method)) { - result = oat_method.GetPortableCode(); - quick_code = oat_method.GetQuickCode(); - } - - if (result == nullptr) { - if (quick_code == nullptr) { - // No code? You must mean to go into the interpreter. - result = GetPortableToInterpreterBridge(); - } else { - // No code? But there's quick code, so use a bridge. - result = GetPortableToQuickBridge(); + jit::Jit* const jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + auto* code = jit->GetCodeCache()->GetCodeFor(method); + if (code != nullptr) { + return code; } - } else { - *have_portable_code = true; } - return result; + if (method->IsNative()) { + // No code and native? Use generic trampoline. + return GetQuickGenericJniStub(); + } + return GetQuickToInterpreterBridge(); } -#endif -const void* ClassLinker::GetOatMethodQuickCodeFor(mirror::ArtMethod* method) { +const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) { if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) { return nullptr; } - OatFile::OatMethod oat_method; - bool found = FindOatMethodFor(method, &oat_method); - return found ? oat_method.GetQuickCode() : nullptr; + bool found; + OatFile::OatMethod oat_method = FindOatMethodFor(method, &found); + if (found) { + return oat_method.GetQuickCode(); + } + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + auto* code = jit->GetCodeCache()->GetCodeFor(method); + if (code != nullptr) { + return code; + } + } + return nullptr; } const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { - OatFile::OatClass oat_class; - if (!FindOatClass(dex_file, class_def_idx, &oat_class)) { + bool found; + OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found); + if (!found) { return nullptr; } uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx); return oat_class.GetOatMethod(oat_method_idx).GetQuickCode(); } -#if defined(ART_USE_PORTABLE_COMPILER) -const void* ClassLinker::GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, - uint32_t method_idx) { - OatFile::OatClass oat_class; - if (!FindOatClass(dex_file, class_def_idx, &oat_class)) { - return nullptr; - } - uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx); - return oat_class.GetOatMethod(oat_method_idx).GetPortableCode(); -} -#endif - // Returns true if the method must run with interpreter, false otherwise. -static bool NeedsInterpreter( - mirror::ArtMethod* method, const void* quick_code, const void* portable_code) +static bool NeedsInterpreter(ArtMethod* method, const void* quick_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if ((quick_code == nullptr) && (portable_code == nullptr)) { + if (quick_code == nullptr) { // No code: need interpreter. // May return true for native code, in the case of generic JNI // DCHECK(!method->IsNative()); return true; } -#ifdef ART_SEA_IR_MODE - ScopedObjectAccess soa(Thread::Current()); - if (std::string::npos != PrettyMethod(method).find("fibonacci")) { - LOG(INFO) << "Found " << PrettyMethod(method); - return false; - } -#endif // If interpreter mode is enabled, every method (except native and proxy) must // be run with interpreter. return Runtime::Current()->GetInstrumentation()->InterpretOnly() && @@ -2550,8 +2138,8 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { return; // No direct methods => no static methods. } Runtime* runtime = Runtime::Current(); - if (!runtime->IsStarted() || runtime->UseCompileTimeClassPath()) { - if (runtime->IsCompiler() || runtime->GetHeap()->HasImageSpace()) { + if (!runtime->IsStarted()) { + if (runtime->IsAotCompiler() || runtime->GetHeap()->HasImageSpace()) { return; // OAT file unavailable. } } @@ -2559,7 +2147,7 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { const DexFile& dex_file = klass->GetDexFile(); const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); CHECK(dex_class_def != nullptr); - const byte* class_data = dex_file.GetClassData(*dex_class_def); + const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); // There should always be class data if there were direct methods. CHECK(class_data != nullptr) << PrettyDescriptor(klass); ClassDataItemIterator it(dex_file, class_data); @@ -2570,164 +2158,95 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { while (it.HasNextInstanceField()) { it.Next(); } - OatFile::OatClass oat_class; - bool has_oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class); + bool has_oat_class; + OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), + &has_oat_class); // Link the code of methods skipped by LinkCode. for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) { - mirror::ArtMethod* method = klass->GetDirectMethod(method_index); + ArtMethod* method = klass->GetDirectMethod(method_index, image_pointer_size_); if (!method->IsStatic()) { // Only update static methods. continue; } - const void* portable_code = nullptr; const void* quick_code = nullptr; if (has_oat_class) { OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index); - portable_code = oat_method.GetPortableCode(); quick_code = oat_method.GetQuickCode(); } - const bool enter_interpreter = NeedsInterpreter(method, quick_code, portable_code); - bool have_portable_code = false; + const bool enter_interpreter = NeedsInterpreter(method, quick_code); if (enter_interpreter) { // Use interpreter entry point. // Check whether the method is native, in which case it's generic JNI. - if (quick_code == nullptr && portable_code == nullptr && method->IsNative()) { - quick_code = GetQuickGenericJniTrampoline(); -#if defined(ART_USE_PORTABLE_COMPILER) - portable_code = GetPortableToQuickBridge(); -#endif - } else { -#if defined(ART_USE_PORTABLE_COMPILER) - portable_code = GetPortableToInterpreterBridge(); -#endif - quick_code = GetQuickToInterpreterBridge(); - } - } else { -#if defined(ART_USE_PORTABLE_COMPILER) - if (portable_code == nullptr) { - portable_code = GetPortableToQuickBridge(); + if (quick_code == nullptr && method->IsNative()) { + quick_code = GetQuickGenericJniStub(); } else { - have_portable_code = true; - } - if (quick_code == nullptr) { - quick_code = GetQuickToPortableBridge(); - } -#else - if (quick_code == nullptr) { quick_code = GetQuickToInterpreterBridge(); } -#endif } - runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code, portable_code, - have_portable_code); + runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code); } // Ignore virtual methods on the iterator. } -void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class, - const DexFile& dex_file, uint32_t dex_method_index, - uint32_t method_index) { - if (Runtime::Current()->IsCompiler()) { +void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class, + uint32_t class_def_method_index) { + Runtime* const runtime = Runtime::Current(); + if (runtime->IsAotCompiler()) { // The following code only applies to a non-compiler runtime. return; } // Method shouldn't have already been linked. DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr); -#if defined(ART_USE_PORTABLE_COMPILER) - DCHECK(method->GetEntryPointFromPortableCompiledCode() == nullptr); -#endif if (oat_class != nullptr) { // Every kind of method should at least get an invoke stub from the oat_method. // non-abstract methods also get their code pointers. - const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index); - oat_method.LinkMethod(method.Get()); + const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index); + oat_method.LinkMethod(method); } // Install entry point from interpreter. - bool enter_interpreter = NeedsInterpreter(method.Get(), - method->GetEntryPointFromQuickCompiledCode(), -#if defined(ART_USE_PORTABLE_COMPILER) - method->GetEntryPointFromPortableCompiledCode()); -#else - nullptr); -#endif + bool enter_interpreter = NeedsInterpreter(method, method->GetEntryPointFromQuickCompiledCode()); if (enter_interpreter && !method->IsNative()) { - method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge); + method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge); } else { method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); } if (method->IsAbstract()) { method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); -#if defined(ART_USE_PORTABLE_COMPILER) - method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge()); -#endif return; } - bool have_portable_code = false; if (method->IsStatic() && !method->IsConstructor()) { // For static methods excluding the class initializer, install the trampoline. // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines // after initializing class (see ClassLinker::InitializeClass method). - method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionTrampoline()); -#if defined(ART_USE_PORTABLE_COMPILER) - method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionTrampoline()); -#endif + method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub()); } else if (enter_interpreter) { if (!method->IsNative()) { // Set entry point from compiled code if there's no code or in interpreter only mode. method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); -#if defined(ART_USE_PORTABLE_COMPILER) - method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge()); -#endif } else { - method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniTrampoline()); -#if defined(ART_USE_PORTABLE_COMPILER) - method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge()); -#endif - } -#if defined(ART_USE_PORTABLE_COMPILER) - } else if (method->GetEntryPointFromPortableCompiledCode() != nullptr) { - DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr); - have_portable_code = true; - method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge()); -#endif - } else { - DCHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr); -#if defined(ART_USE_PORTABLE_COMPILER) - method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge()); -#endif + method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); + } } if (method->IsNative()) { // Unregistering restores the dlsym lookup stub. - method->UnregisterNative(Thread::Current()); + method->UnregisterNative(); if (enter_interpreter) { - // We have a native method here without code. Then it should have either the GenericJni - // trampoline as entrypoint (non-static), or the Resolution trampoline (static). - DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickResolutionTrampoline() - || method->GetEntryPointFromQuickCompiledCode() == GetQuickGenericJniTrampoline()); + // We have a native method here without code. Then it should have either the generic JNI + // trampoline as entrypoint (non-static), or the resolution trampoline (static). + // TODO: this doesn't handle all the cases where trampolines may be installed. + const void* entry_point = method->GetEntryPointFromQuickCompiledCode(); + DCHECK(IsQuickGenericJniStub(entry_point) || IsQuickResolutionStub(entry_point)); } } - - // Allow instrumentation its chance to hijack code. - Runtime* runtime = Runtime::Current(); - runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(), - method->GetEntryPointFromQuickCompiledCode(), -#if defined(ART_USE_PORTABLE_COMPILER) - method->GetEntryPointFromPortableCompiledCode(), -#else - nullptr, -#endif - have_portable_code); } -void ClassLinker::LoadClass(const DexFile& dex_file, - const DexFile::ClassDef& dex_class_def, - Handle<mirror::Class> klass, - mirror::ClassLoader* class_loader) { +void ClassLinker::SetupClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, + Handle<mirror::Class> klass, mirror::ClassLoader* class_loader) { CHECK(klass.Get() != nullptr); CHECK(klass->GetDexCache() != nullptr); CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus()); @@ -2735,163 +2254,140 @@ void ClassLinker::LoadClass(const DexFile& dex_file, CHECK(descriptor != nullptr); klass->SetClass(GetClassRoot(kJavaLangClass)); - if (kUseBakerOrBrooksReadBarrier) { - klass->AssertReadBarrierPointer(); - } uint32_t access_flags = dex_class_def.GetJavaAccessFlags(); CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U); klass->SetAccessFlags(access_flags); klass->SetClassLoader(class_loader); DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot); - klass->SetStatus(mirror::Class::kStatusIdx, nullptr); + mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, nullptr); klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def)); klass->SetDexTypeIndex(dex_class_def.class_idx_); CHECK(klass->GetDexCacheStrings() != nullptr); +} - const byte* class_data = dex_file.GetClassData(dex_class_def); +void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def, + Handle<mirror::Class> klass) { + const uint8_t* class_data = dex_file.GetClassData(dex_class_def); if (class_data == nullptr) { return; // no fields or methods - for example a marker interface } + bool has_oat_class = false; + if (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler()) { + OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), + &has_oat_class); + if (has_oat_class) { + LoadClassMembers(self, dex_file, class_data, klass, &oat_class); + } + } + if (!has_oat_class) { + LoadClassMembers(self, dex_file, class_data, klass, nullptr); + } +} - OatFile::OatClass oat_class; - if (Runtime::Current()->IsStarted() - && !Runtime::Current()->UseCompileTimeClassPath() - && FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) { - LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class); - } else { - LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr); +ArtField* ClassLinker::AllocArtFieldArray(Thread* self, size_t length) { + auto* const la = Runtime::Current()->GetLinearAlloc(); + auto* ptr = reinterpret_cast<ArtField*>(la->AllocArray<ArtField>(self, length)); + CHECK(ptr!= nullptr); + std::uninitialized_fill_n(ptr, length, ArtField()); + return ptr; +} + +ArtMethod* ClassLinker::AllocArtMethodArray(Thread* self, size_t length) { + const size_t method_size = ArtMethod::ObjectSize(image_pointer_size_); + uintptr_t ptr = reinterpret_cast<uintptr_t>( + Runtime::Current()->GetLinearAlloc()->Alloc(self, method_size * length)); + CHECK_NE(ptr, 0u); + for (size_t i = 0; i < length; ++i) { + new(reinterpret_cast<void*>(ptr + i * method_size)) ArtMethod; } + return reinterpret_cast<ArtMethod*>(ptr); } -void ClassLinker::LoadClassMembers(const DexFile& dex_file, - const byte* class_data, +void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file, + const uint8_t* class_data, Handle<mirror::Class> klass, - mirror::ClassLoader* class_loader, const OatFile::OatClass* oat_class) { - // Load fields. - ClassDataItemIterator it(dex_file, class_data); - Thread* self = Thread::Current(); - if (it.NumStaticFields() != 0) { - mirror::ObjectArray<mirror::ArtField>* statics = AllocArtFieldArray(self, it.NumStaticFields()); - if (UNLIKELY(statics == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return; - } - klass->SetSFields(statics); - } - if (it.NumInstanceFields() != 0) { - mirror::ObjectArray<mirror::ArtField>* fields = - AllocArtFieldArray(self, it.NumInstanceFields()); - if (UNLIKELY(fields == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return; - } - klass->SetIFields(fields); - } - for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) { - StackHandleScope<1> hs(self); - Handle<mirror::ArtField> sfield(hs.NewHandle(AllocArtField(self))); - if (UNLIKELY(sfield.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return; - } - klass->SetStaticField(i, sfield.Get()); - LoadField(dex_file, it, klass, sfield); - } - for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) { - StackHandleScope<1> hs(self); - Handle<mirror::ArtField> ifield(hs.NewHandle(AllocArtField(self))); - if (UNLIKELY(ifield.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return; - } - klass->SetInstanceField(i, ifield.Get()); - LoadField(dex_file, it, klass, ifield); - } - - // Load methods. - if (it.NumDirectMethods() != 0) { - // TODO: append direct methods to class object - mirror::ObjectArray<mirror::ArtMethod>* directs = - AllocArtMethodArray(self, it.NumDirectMethods()); - if (UNLIKELY(directs == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return; - } - klass->SetDirectMethods(directs); - } - if (it.NumVirtualMethods() != 0) { - // TODO: append direct methods to class object - mirror::ObjectArray<mirror::ArtMethod>* virtuals = - AllocArtMethodArray(self, it.NumVirtualMethods()); - if (UNLIKELY(virtuals == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return; - } - klass->SetVirtualMethods(virtuals); - } - size_t class_def_method_index = 0; - uint32_t last_dex_method_index = DexFile::kDexNoIndex; - size_t last_class_def_method_index = 0; - for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) { - StackHandleScope<1> hs(self); - Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass))); - if (UNLIKELY(method.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return; + { + // Note: We cannot have thread suspension until the field and method arrays are setup or else + // Class::VisitFieldRoots may miss some fields or methods. + ScopedAssertNoThreadSuspension nts(self, __FUNCTION__); + // Load static fields. + ClassDataItemIterator it(dex_file, class_data); + const size_t num_sfields = it.NumStaticFields(); + ArtField* sfields = num_sfields != 0 ? AllocArtFieldArray(self, num_sfields) : nullptr; + for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) { + CHECK_LT(i, num_sfields); + LoadField(it, klass, &sfields[i]); } - klass->SetDirectMethod(i, method.Get()); - LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index); - uint32_t it_method_index = it.GetMemberIndex(); - if (last_dex_method_index == it_method_index) { - // duplicate case - method->SetMethodIndex(last_class_def_method_index); - } else { - method->SetMethodIndex(class_def_method_index); - last_dex_method_index = it_method_index; - last_class_def_method_index = class_def_method_index; + klass->SetSFields(sfields); + klass->SetNumStaticFields(num_sfields); + DCHECK_EQ(klass->NumStaticFields(), num_sfields); + // Load instance fields. + const size_t num_ifields = it.NumInstanceFields(); + ArtField* ifields = num_ifields != 0 ? AllocArtFieldArray(self, num_ifields) : nullptr; + for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) { + CHECK_LT(i, num_ifields); + LoadField(it, klass, &ifields[i]); + } + klass->SetIFields(ifields); + klass->SetNumInstanceFields(num_ifields); + DCHECK_EQ(klass->NumInstanceFields(), num_ifields); + // Load methods. + if (it.NumDirectMethods() != 0) { + klass->SetDirectMethodsPtr(AllocArtMethodArray(self, it.NumDirectMethods())); + } + klass->SetNumDirectMethods(it.NumDirectMethods()); + if (it.NumVirtualMethods() != 0) { + klass->SetVirtualMethodsPtr(AllocArtMethodArray(self, it.NumVirtualMethods())); + } + klass->SetNumVirtualMethods(it.NumVirtualMethods()); + size_t class_def_method_index = 0; + uint32_t last_dex_method_index = DexFile::kDexNoIndex; + size_t last_class_def_method_index = 0; + for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) { + ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_); + LoadMethod(self, dex_file, it, klass, method); + LinkCode(method, oat_class, class_def_method_index); + uint32_t it_method_index = it.GetMemberIndex(); + if (last_dex_method_index == it_method_index) { + // duplicate case + method->SetMethodIndex(last_class_def_method_index); + } else { + method->SetMethodIndex(class_def_method_index); + last_dex_method_index = it_method_index; + last_class_def_method_index = class_def_method_index; + } + class_def_method_index++; } - class_def_method_index++; - } - for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) { - StackHandleScope<1> hs(self); - Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass))); - if (UNLIKELY(method.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return; + for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) { + ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); + LoadMethod(self, dex_file, it, klass, method); + DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i); + LinkCode(method, oat_class, class_def_method_index); + class_def_method_index++; } - klass->SetVirtualMethod(i, method.Get()); - DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i); - LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index); - class_def_method_index++; + DCHECK(!it.HasNext()); } - DCHECK(!it.HasNext()); + self->AllowThreadSuspension(); } -void ClassLinker::LoadField(const DexFile& /*dex_file*/, const ClassDataItemIterator& it, - Handle<mirror::Class> klass, Handle<mirror::ArtField> dst) { - uint32_t field_idx = it.GetMemberIndex(); +void ClassLinker::LoadField(const ClassDataItemIterator& it, Handle<mirror::Class> klass, + ArtField* dst) { + const uint32_t field_idx = it.GetMemberIndex(); dst->SetDexFieldIndex(field_idx); dst->SetDeclaringClass(klass.Get()); dst->SetAccessFlags(it.GetFieldAccessFlags()); } -mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, - const ClassDataItemIterator& it, - Handle<mirror::Class> klass) { +void ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it, + Handle<mirror::Class> klass, ArtMethod* dst) { uint32_t dex_method_idx = it.GetMemberIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_); - mirror::ArtMethod* dst = AllocArtMethod(self); - if (UNLIKELY(dst == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return nullptr; - } - DCHECK(dst->IsArtMethod()) << PrettyDescriptor(dst->GetClass()); - - const char* old_cause = self->StartAssertNoThreadSuspension("LoadMethod"); + ScopedAssertNoThreadSuspension ants(self, "LoadMethod"); dst->SetDexMethodIndex(dex_method_idx); dst->SetDeclaringClass(klass.Get()); dst->SetCodeItemOffset(it.GetMethodCodeItemOffset()); @@ -2936,13 +2432,9 @@ mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file } } dst->SetAccessFlags(access_flags); - - self->EndAssertNoThreadSuspension(old_cause); - return dst; } -void ClassLinker::AppendToBootClassPath(const DexFile& dex_file) { - Thread* self = Thread::Current(); +void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile& dex_file) { StackHandleScope<1> hs(self); Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(self, dex_file))); CHECK(dex_cache.Get() != nullptr) << "Failed to allocate dex cache for " @@ -3040,20 +2532,20 @@ mirror::DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) { LOG(ERROR) << "Registered dex file " << i << " = " << dex_cache->GetDexFile()->GetLocation(); } LOG(FATAL) << "Failed to find DexCache for DexFile " << location; - return nullptr; + UNREACHABLE(); } -void ClassLinker::FixupDexCaches(mirror::ArtMethod* resolution_method) { +void ClassLinker::FixupDexCaches(ArtMethod* resolution_method) { ReaderMutexLock mu(Thread::Current(), dex_lock_); - for (size_t i = 0; i != dex_caches_.size(); ++i) { - mirror::DexCache* dex_cache = GetDexCache(i); - dex_cache->Fixup(resolution_method); + for (auto& dex_cache : dex_caches_) { + dex_cache.Read()->Fixup(resolution_method, image_pointer_size_); } } mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) { - mirror::Class* klass = AllocClass(self, mirror::Class::PrimitiveClassSize()); + mirror::Class* klass = AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_)); if (UNLIKELY(klass == nullptr)) { + self->AssertPendingOOMException(); return nullptr; } return InitializePrimitiveClass(klass, type); @@ -3067,14 +2559,14 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(mirror::Class* primitive_cl StackHandleScope<1> hs(self); Handle<mirror::Class> h_class(hs.NewHandle(primitive_class)); ObjectLock<mirror::Class> lock(self, h_class); - primitive_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract); - primitive_class->SetPrimitiveType(type); - primitive_class->SetStatus(mirror::Class::kStatusInitialized, self); + h_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract); + h_class->SetPrimitiveType(type); + mirror::Class::SetStatus(h_class, mirror::Class::kStatusInitialized, self); const char* descriptor = Primitive::Descriptor(type); - mirror::Class* existing = InsertClass(descriptor, primitive_class, + mirror::Class* existing = InsertClass(descriptor, h_class.Get(), ComputeModifiedUtf8Hash(descriptor)); CHECK(existing == nullptr) << "InitPrimitiveClass(" << type << ") failed"; - return primitive_class; + return h_class.Get(); } // Create an array class (i.e. the class object for the array, not the @@ -3089,18 +2581,19 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(mirror::Class* primitive_cl // the right context. It does NOT become the class loader for the // array class; that always comes from the base element class. // -// Returns nullptr with an exception raised on failure. +// Returns null with an exception raised on failure. mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader) { // Identify the underlying component type CHECK_EQ('[', descriptor[0]); StackHandleScope<2> hs(self); - Handle<mirror::Class> component_type(hs.NewHandle(FindClass(self, descriptor + 1, class_loader))); + MutableHandle<mirror::Class> component_type(hs.NewHandle(FindClass(self, descriptor + 1, + class_loader))); if (component_type.Get() == nullptr) { DCHECK(self->IsExceptionPending()); // We need to accept erroneous classes as component types. const size_t component_hash = ComputeModifiedUtf8Hash(descriptor + 1); - component_type.Assign(LookupClass(descriptor + 1, component_hash, class_loader.Get())); + component_type.Assign(LookupClass(self, descriptor + 1, component_hash, class_loader.Get())); if (component_type.Get() == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -3130,7 +2623,7 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto // class to the hash table --- necessary because of possible races with // other threads.) if (class_loader.Get() != component_type->GetClassLoader()) { - mirror::Class* new_class = LookupClass(descriptor, hash, component_type->GetClassLoader()); + mirror::Class* new_class = LookupClass(self, descriptor, hash, component_type->GetClassLoader()); if (new_class != nullptr) { return new_class; } @@ -3151,23 +2644,20 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto new_class.Assign(GetClassRoot(kClassArrayClass)); } else if (strcmp(descriptor, "[Ljava/lang/Object;") == 0) { new_class.Assign(GetClassRoot(kObjectArrayClass)); - } else if (strcmp(descriptor, class_roots_descriptors_[kJavaLangStringArrayClass]) == 0) { + } else if (strcmp(descriptor, GetClassRootDescriptor(kJavaLangStringArrayClass)) == 0) { new_class.Assign(GetClassRoot(kJavaLangStringArrayClass)); - } else if (strcmp(descriptor, - class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]) == 0) { - new_class.Assign(GetClassRoot(kJavaLangReflectArtMethodArrayClass)); - } else if (strcmp(descriptor, - class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]) == 0) { - new_class.Assign(GetClassRoot(kJavaLangReflectArtFieldArrayClass)); } else if (strcmp(descriptor, "[C") == 0) { new_class.Assign(GetClassRoot(kCharArrayClass)); } else if (strcmp(descriptor, "[I") == 0) { new_class.Assign(GetClassRoot(kIntArrayClass)); + } else if (strcmp(descriptor, "[J") == 0) { + new_class.Assign(GetClassRoot(kLongArrayClass)); } } if (new_class.Get() == nullptr) { - new_class.Assign(AllocClass(self, mirror::Array::ClassSize())); + new_class.Assign(AllocClass(self, mirror::Array::ClassSize(image_pointer_size_))); if (new_class.Get() == nullptr) { + self->AssertPendingOOMException(); return nullptr; } new_class->SetComponentType(component_type.Get()); @@ -3179,13 +2669,13 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto new_class->SetVTable(java_lang_Object->GetVTable()); new_class->SetPrimitiveType(Primitive::kPrimNot); new_class->SetClassLoader(component_type->GetClassLoader()); - new_class->SetStatus(mirror::Class::kStatusLoaded, self); + mirror::Class::SetStatus(new_class, mirror::Class::kStatusLoaded, self); { - StackHandleScope<mirror::Class::kImtSize> hs(self, - Runtime::Current()->GetImtUnimplementedMethod()); - new_class->PopulateEmbeddedImtAndVTable(&hs); + ArtMethod* imt[mirror::Class::kImtSize]; + std::fill_n(imt, arraysize(imt), Runtime::Current()->GetImtUnimplementedMethod()); + new_class->PopulateEmbeddedImtAndVTable(imt, image_pointer_size_); } - new_class->SetStatus(mirror::Class::kStatusInitialized, self); + mirror::Class::SetStatus(new_class, mirror::Class::kStatusInitialized, self); // don't need to set new_class->SetObjectSize(..) // because Object::SizeOf delegates to Array::SizeOf @@ -3293,16 +2783,24 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k return nullptr; } +void ClassLinker::UpdateClassVirtualMethods(mirror::Class* klass, ArtMethod* new_methods, + size_t new_num_methods) { + // classlinker_classes_lock_ is used to guard against races between root marking and changing the + // direct and virtual method pointers. + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + klass->SetNumVirtualMethods(new_num_methods); + klass->SetVirtualMethodsPtr(new_methods); + if (log_new_class_table_roots_) { + new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); + } +} + mirror::Class* ClassLinker::UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash) { WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); auto existing_it = class_table_.FindWithHash(std::make_pair(descriptor, klass->GetClassLoader()), hash); - if (existing_it == class_table_.end()) { - CHECK(klass->IsProxyClass()); - return nullptr; - } - + CHECK(existing_it != class_table_.end()); mirror::Class* existing = existing_it->Read(); CHECK_NE(existing, klass) << descriptor; CHECK(!existing->IsResolved()) << descriptor; @@ -3345,10 +2843,10 @@ bool ClassLinker::RemoveClass(const char* descriptor, mirror::ClassLoader* class return false; } -mirror::Class* ClassLinker::LookupClass(const char* descriptor, size_t hash, +mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor, size_t hash, mirror::ClassLoader* class_loader) { { - ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash); if (result != nullptr) { return result; @@ -3402,8 +2900,7 @@ void ClassLinker::MoveImageClassesToClassTable() { if (!dex_cache_image_class_lookup_required_) { return; // All dex cache classes are already in the class table. } - const char* old_no_suspend_cause = - self->StartAssertNoThreadSuspension("Moving image classes to class table"); + ScopedAssertNoThreadSuspension ants(self, "Moving image classes to class table"); mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(); std::string temp; for (int32_t i = 0; i < dex_caches->GetLength(); i++) { @@ -3429,7 +2926,6 @@ void ClassLinker::MoveImageClassesToClassTable() { } } dex_cache_image_class_lookup_required_ = false; - self->EndAssertNoThreadSuspension(old_no_suspend_cause); } void ClassLinker::MoveClassTableToPreZygote() { @@ -3440,9 +2936,7 @@ void ClassLinker::MoveClassTableToPreZygote() { } mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) { - Thread* self = Thread::Current(); - const char* old_no_suspend_cause = - self->StartAssertNoThreadSuspension("Image class lookup"); + ScopedAssertNoThreadSuspension ants(Thread::Current(), "Image class lookup"); mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(); for (int32_t i = 0; i < dex_caches->GetLength(); ++i) { mirror::DexCache* dex_cache = dex_caches->Get(i); @@ -3456,13 +2950,11 @@ mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) { uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id); mirror::Class* klass = dex_cache->GetResolvedType(type_idx); if (klass != nullptr) { - self->EndAssertNoThreadSuspension(old_no_suspend_cause); return klass; } } } } - self->EndAssertNoThreadSuspension(old_no_suspend_cause); return nullptr; } @@ -3500,9 +2992,8 @@ void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Clas } } -void ClassLinker::VerifyClass(Handle<mirror::Class> klass) { +void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) { // TODO: assert that the monitor on the Class is held - Thread* self = Thread::Current(); ObjectLock<mirror::Class> lock(self, klass); // Don't attempt to re-verify if already sufficiently verified. @@ -3510,7 +3001,7 @@ void ClassLinker::VerifyClass(Handle<mirror::Class> klass) { EnsurePreverifiedMethods(klass); return; } - if (klass->IsCompileTimeVerified() && Runtime::Current()->IsCompiler()) { + if (klass->IsCompileTimeVerified() && Runtime::Current()->IsAotCompiler()) { return; } @@ -3522,17 +3013,17 @@ void ClassLinker::VerifyClass(Handle<mirror::Class> klass) { } if (klass->GetStatus() == mirror::Class::kStatusResolved) { - klass->SetStatus(mirror::Class::kStatusVerifying, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusVerifying, self); } else { CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime) << PrettyClass(klass.Get()); - CHECK(!Runtime::Current()->IsCompiler()); - klass->SetStatus(mirror::Class::kStatusVerifyingAtRuntime, self); + CHECK(!Runtime::Current()->IsAotCompiler()); + mirror::Class::SetStatus(klass, mirror::Class::kStatusVerifyingAtRuntime, self); } // Skip verification if disabled. if (!Runtime::Current()->IsVerificationEnabled()) { - klass->SetStatus(mirror::Class::kStatusVerified, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self); EnsurePreverifiedMethods(klass); return; } @@ -3542,30 +3033,30 @@ void ClassLinker::VerifyClass(Handle<mirror::Class> klass) { Handle<mirror::Class> super(hs.NewHandle(klass->GetSuperClass())); if (super.Get() != nullptr) { // Acquire lock to prevent races on verifying the super class. - ObjectLock<mirror::Class> lock(self, super); + ObjectLock<mirror::Class> super_lock(self, super); if (!super->IsVerified() && !super->IsErroneous()) { - VerifyClass(super); + VerifyClass(self, super); } if (!super->IsCompileTimeVerified()) { std::string error_msg( StringPrintf("Rejecting class %s that attempts to sub-class erroneous class %s", PrettyDescriptor(klass.Get()).c_str(), PrettyDescriptor(super.Get()).c_str())); - LOG(ERROR) << error_msg << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); - Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException(nullptr))); + LOG(WARNING) << error_msg << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); + Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException())); if (cause.Get() != nullptr) { self->ClearException(); } ThrowVerifyError(klass.Get(), "%s", error_msg.c_str()); if (cause.Get() != nullptr) { - self->GetException(nullptr)->SetCause(cause.Get()); + self->GetException()->SetCause(cause.Get()); } ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex()); - if (Runtime::Current()->IsCompiler()) { + if (Runtime::Current()->IsAotCompiler()) { Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref); } - klass->SetStatus(mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); return; } } @@ -3580,14 +3071,14 @@ void ClassLinker::VerifyClass(Handle<mirror::Class> klass) { << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); ThrowVerifyError(klass.Get(), "Rejecting class %s because it failed compile-time verification", PrettyDescriptor(klass.Get()).c_str()); - klass->SetStatus(mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); return; } verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure; std::string error_msg; if (!preverified) { - verifier_failure = verifier::MethodVerifier::VerifyClass(klass.Get(), - Runtime::Current()->IsCompiler(), + verifier_failure = verifier::MethodVerifier::VerifyClass(self, klass.Get(), + Runtime::Current()->IsAotCompiler(), &error_msg); } if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) { @@ -3603,10 +3094,10 @@ void ClassLinker::VerifyClass(Handle<mirror::Class> klass) { // Even though there were no verifier failures we need to respect whether the super-class // was verified or requiring runtime reverification. if (super.Get() == nullptr || super->IsVerified()) { - klass->SetStatus(mirror::Class::kStatusVerified, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self); } else { CHECK_EQ(super->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime); - klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusRetryVerificationAtRuntime, self); // Pretend a soft failure occured so that we don't consider the class verified below. verifier_failure = verifier::MethodVerifier::kSoftFailure; } @@ -3615,22 +3106,22 @@ void ClassLinker::VerifyClass(Handle<mirror::Class> klass) { // Soft failures at compile time should be retried at runtime. Soft // failures at runtime will be handled by slow paths in the generated // code. Set status accordingly. - if (Runtime::Current()->IsCompiler()) { - klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime, self); + if (Runtime::Current()->IsAotCompiler()) { + mirror::Class::SetStatus(klass, mirror::Class::kStatusRetryVerificationAtRuntime, self); } else { - klass->SetStatus(mirror::Class::kStatusVerified, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self); // As this is a fake verified status, make sure the methods are _not_ marked preverified // later. - klass->SetAccessFlags(klass->GetAccessFlags() | kAccPreverified); + klass->SetPreverified(); } } } else { - LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(klass.Get()) + LOG(WARNING) << "Verification failed on class " << PrettyDescriptor(klass.Get()) << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8() << " because: " << error_msg; self->AssertNoPendingException(); ThrowVerifyError(klass.Get(), "%s", error_msg.c_str()); - klass->SetStatus(mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); } if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) { // Class is verified so we don't need to do any access check on its methods. @@ -3644,9 +3135,9 @@ void ClassLinker::VerifyClass(Handle<mirror::Class> klass) { } void ClassLinker::EnsurePreverifiedMethods(Handle<mirror::Class> klass) { - if ((klass->GetAccessFlags() & kAccPreverified) == 0) { - klass->SetPreverifiedFlagOnAllMethods(); - klass->SetAccessFlags(klass->GetAccessFlags() | kAccPreverified); + if (!klass->IsPreverified()) { + klass->SetPreverifiedFlagOnAllMethods(image_pointer_size_); + klass->SetPreverified(); } } @@ -3656,9 +3147,9 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class // we are not compiling the image or if the class we're verifying is not part of // the app. In other words, we will only check for preverification of bootclasspath // classes. - if (Runtime::Current()->IsCompiler()) { + if (Runtime::Current()->IsAotCompiler()) { // Are we compiling the bootclasspath? - if (!Runtime::Current()->UseCompileTimeClassPath()) { + if (Runtime::Current()->GetCompilerCallbacks()->IsBootImage()) { return false; } // We are compiling an app (not the image). @@ -3669,7 +3160,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class } } - const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFileForDexFile(dex_file); + const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); // In case we run without an image there won't be a backing oat file. if (oat_dex_file == nullptr) { return false; @@ -3678,8 +3169,13 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class // We may be running with a preopted oat file but without image. In this case, // we don't skip verification of preverified classes to ensure we initialize // dex caches with all types resolved during verification. - if (!Runtime::Current()->IsCompiler() && - !Runtime::Current()->GetHeap()->HasImageSpace()) { + // We need to trust image classes, as these might be coming out of a pre-opted, quickened boot + // image (that we just failed loading), and the verifier can't be run on quickened opcodes when + // the runtime isn't started. On the other hand, app classes can be re-verified even if they are + // already pre-opted, as then the runtime is started. + if (!Runtime::Current()->IsAotCompiler() && + !Runtime::Current()->GetHeap()->HasImageSpace() && + klass->GetClassLoader() != nullptr) { return false; } @@ -3725,22 +3221,21 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class LOG(FATAL) << "Unexpected class status: " << oat_file_class_status << " " << dex_file.GetLocation() << " " << PrettyClass(klass) << " " << klass->GetDescriptor(&temp); - - return false; + UNREACHABLE(); } void ClassLinker::ResolveClassExceptionHandlerTypes(const DexFile& dex_file, Handle<mirror::Class> klass) { for (size_t i = 0; i < klass->NumDirectMethods(); i++) { - ResolveMethodExceptionHandlerTypes(dex_file, klass->GetDirectMethod(i)); + ResolveMethodExceptionHandlerTypes(dex_file, klass->GetDirectMethod(i, image_pointer_size_)); } for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { - ResolveMethodExceptionHandlerTypes(dex_file, klass->GetVirtualMethod(i)); + ResolveMethodExceptionHandlerTypes(dex_file, klass->GetVirtualMethod(i, image_pointer_size_)); } } void ClassLinker::ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, - mirror::ArtMethod* method) { + ArtMethod* method) { // similar to DexVerifier::ScanTryCatchBlocks and dex2oat's ResolveExceptionsForMethod. const DexFile::CodeItem* code_item = dex_file.GetCodeItem(method->GetCodeItemOffset()); if (code_item == nullptr) { @@ -3749,7 +3244,7 @@ void ClassLinker::ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, if (code_item->tries_size_ == 0) { return; // nothing to process } - const byte* handlers_ptr = DexFile::GetCatchHandlerData(*code_item, 0); + const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item, 0); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); ClassLinker* linker = Runtime::Current()->GetClassLinker(); for (uint32_t idx = 0; idx < handlers_size; idx++) { @@ -3769,16 +3264,12 @@ void ClassLinker::ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, } } -static void CheckProxyConstructor(mirror::ArtMethod* constructor); -static void CheckProxyMethod(Handle<mirror::ArtMethod> method, - Handle<mirror::ArtMethod> prototype); - mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, jstring name, jobjectArray interfaces, jobject loader, jobjectArray methods, jobjectArray throws) { Thread* self = soa.Self(); - StackHandleScope<8> hs(self); - Handle<mirror::Class> klass(hs.NewHandle( + StackHandleScope<10> hs(self); + MutableHandle<mirror::Class> klass(hs.NewHandle( AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class)))); if (klass.Get() == nullptr) { CHECK(self->IsExceptionPending()); // OOME. @@ -3791,131 +3282,120 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& klass->SetClassLoader(soa.Decode<mirror::ClassLoader*>(loader)); DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot); klass->SetName(soa.Decode<mirror::String*>(name)); - mirror::Class* proxy_class = GetClassRoot(kJavaLangReflectProxy); - klass->SetDexCache(proxy_class->GetDexCache()); - klass->SetStatus(mirror::Class::kStatusIdx, self); + klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache()); + mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self); + std::string descriptor(GetDescriptorForProxy(klass.Get())); + size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str()); + + // Insert the class before loading the fields as the field roots + // (ArtField::declaring_class_) are only visited from the class + // table. There can't be any suspend points between inserting the + // class and setting the field arrays below. + mirror::Class* existing = InsertClass(descriptor.c_str(), klass.Get(), hash); + CHECK(existing == nullptr); // Instance fields are inherited, but we add a couple of static fields... - { - mirror::ObjectArray<mirror::ArtField>* sfields = AllocArtFieldArray(self, 2); - if (UNLIKELY(sfields == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return nullptr; - } - klass->SetSFields(sfields); - } + const size_t num_fields = 2; + ArtField* sfields = AllocArtFieldArray(self, num_fields); + klass->SetSFields(sfields); + klass->SetNumStaticFields(num_fields); + // 1. Create a static field 'interfaces' that holds the _declared_ interfaces implemented by // our proxy, so Class.getInterfaces doesn't return the flattened set. - Handle<mirror::ArtField> interfaces_sfield(hs.NewHandle(AllocArtField(self))); - if (UNLIKELY(interfaces_sfield.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return nullptr; - } - klass->SetStaticField(0, interfaces_sfield.Get()); + ArtField* interfaces_sfield = &sfields[0]; interfaces_sfield->SetDexFieldIndex(0); interfaces_sfield->SetDeclaringClass(klass.Get()); interfaces_sfield->SetAccessFlags(kAccStatic | kAccPublic | kAccFinal); + // 2. Create a static field 'throws' that holds exceptions thrown by our methods. - Handle<mirror::ArtField> throws_sfield(hs.NewHandle(AllocArtField(self))); - if (UNLIKELY(throws_sfield.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return nullptr; - } - klass->SetStaticField(1, throws_sfield.Get()); + ArtField* throws_sfield = &sfields[1]; throws_sfield->SetDexFieldIndex(1); throws_sfield->SetDeclaringClass(klass.Get()); throws_sfield->SetAccessFlags(kAccStatic | kAccPublic | kAccFinal); // Proxies have 1 direct method, the constructor - { - mirror::ObjectArray<mirror::ArtMethod>* directs = AllocArtMethodArray(self, 1); - if (UNLIKELY(directs == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return nullptr; - } - klass->SetDirectMethods(directs); - mirror::ArtMethod* constructor = CreateProxyConstructor(self, klass, proxy_class); - if (UNLIKELY(constructor == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return nullptr; - } - klass->SetDirectMethod(0, constructor); + auto* directs = AllocArtMethodArray(self, 1); + // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we + // want to throw OOM in the future. + if (UNLIKELY(directs == nullptr)) { + self->AssertPendingOOMException(); + return nullptr; } + klass->SetDirectMethodsPtr(directs); + klass->SetNumDirectMethods(1u); + CreateProxyConstructor(klass, klass->GetDirectMethodUnchecked(0, image_pointer_size_)); // Create virtual method using specified prototypes. - size_t num_virtual_methods = - soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods)->GetLength(); - { - mirror::ObjectArray<mirror::ArtMethod>* virtuals = AllocArtMethodArray(self, - num_virtual_methods); - if (UNLIKELY(virtuals == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return nullptr; - } - klass->SetVirtualMethods(virtuals); + auto h_methods = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Method>*>(methods)); + DCHECK_EQ(h_methods->GetClass(), mirror::Method::ArrayClass()) + << PrettyClass(h_methods->GetClass()); + const size_t num_virtual_methods = h_methods->GetLength(); + auto* virtuals = AllocArtMethodArray(self, num_virtual_methods); + // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we + // want to throw OOM in the future. + if (UNLIKELY(virtuals == nullptr)) { + self->AssertPendingOOMException(); + return nullptr; } + klass->SetVirtualMethodsPtr(virtuals); + klass->SetNumVirtualMethods(num_virtual_methods); for (size_t i = 0; i < num_virtual_methods; ++i) { - StackHandleScope<1> hs(self); - mirror::ObjectArray<mirror::ArtMethod>* decoded_methods = - soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods); - Handle<mirror::ArtMethod> prototype(hs.NewHandle(decoded_methods->Get(i))); - mirror::ArtMethod* clone = CreateProxyMethod(self, klass, prototype); - if (UNLIKELY(clone == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return nullptr; - } - klass->SetVirtualMethod(i, clone); + auto* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); + auto* prototype = h_methods->Get(i)->GetArtMethod(); + CreateProxyMethod(klass, prototype, virtual_method); + DCHECK(virtual_method->GetDeclaringClass() != nullptr); + DCHECK(prototype->GetDeclaringClass() != nullptr); } - klass->SetSuperClass(proxy_class); // The super class is java.lang.reflect.Proxy - klass->SetStatus(mirror::Class::kStatusLoaded, self); // Now effectively in the loaded state. + // The super class is java.lang.reflect.Proxy + klass->SetSuperClass(GetClassRoot(kJavaLangReflectProxy)); + // Now effectively in the loaded state. + mirror::Class::SetStatus(klass, mirror::Class::kStatusLoaded, self); self->AssertNoPendingException(); - std::string descriptor(GetDescriptorForProxy(klass.Get())); - mirror::Class* new_class = nullptr; + MutableHandle<mirror::Class> new_class = hs.NewHandle<mirror::Class>(nullptr); { // Must hold lock on object when resolved. ObjectLock<mirror::Class> resolution_lock(self, klass); - // Link the fields and virtual methods, creating vtable and iftables - Handle<mirror::ObjectArray<mirror::Class> > h_interfaces( + // Link the fields and virtual methods, creating vtable and iftables. + // The new class will replace the old one in the class table. + Handle<mirror::ObjectArray<mirror::Class>> h_interfaces( hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces))); if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, &new_class)) { - klass->SetStatus(mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); return nullptr; } } - CHECK(klass->IsRetired()); - CHECK_NE(klass.Get(), new_class); - klass.Assign(new_class); + CHECK_NE(klass.Get(), new_class.Get()); + klass.Assign(new_class.Get()); - CHECK_EQ(interfaces_sfield->GetDeclaringClass(), new_class); + CHECK_EQ(interfaces_sfield->GetDeclaringClass(), klass.Get()); interfaces_sfield->SetObject<false>(klass.Get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)); - CHECK_EQ(throws_sfield->GetDeclaringClass(), new_class); + CHECK_EQ(throws_sfield->GetDeclaringClass(), klass.Get()); throws_sfield->SetObject<false>(klass.Get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws)); { // Lock on klass is released. Lock new class object. ObjectLock<mirror::Class> initialization_lock(self, klass); - klass->SetStatus(mirror::Class::kStatusInitialized, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusInitialized, self); } // sanity checks if (kIsDebugBuild) { CHECK(klass->GetIFields() == nullptr); - CheckProxyConstructor(klass->GetDirectMethod(0)); + CheckProxyConstructor(klass->GetDirectMethod(0, image_pointer_size_)); + for (size_t i = 0; i < num_virtual_methods; ++i) { - StackHandleScope<2> hs(self); - mirror::ObjectArray<mirror::ArtMethod>* decoded_methods = - soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods); - Handle<mirror::ArtMethod> prototype(hs.NewHandle(decoded_methods->Get(i))); - Handle<mirror::ArtMethod> virtual_method(hs.NewHandle(klass->GetVirtualMethod(i))); + auto* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); + auto* prototype = h_methods->Get(i++)->GetArtMethod(); CheckProxyMethod(virtual_method, prototype); } - mirror::String* decoded_name = soa.Decode<mirror::String*>(name); + StackHandleScope<1> hs2(self); + Handle<mirror::String> decoded_name = hs2.NewHandle(soa.Decode<mirror::String*>(name)); std::string interfaces_field_name(StringPrintf("java.lang.Class[] %s.interfaces", decoded_name->ToModifiedUtf8().c_str())); CHECK_EQ(PrettyField(klass->GetStaticField(0)), interfaces_field_name); @@ -3929,9 +3409,6 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& CHECK_EQ(klass.Get()->GetThrows(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws)); } - mirror::Class* existing = InsertClass(descriptor.c_str(), klass.Get(), - ComputeModifiedUtf8Hash(descriptor.c_str())); - CHECK(existing == nullptr); return klass.Get(); } @@ -3942,97 +3419,82 @@ std::string ClassLinker::GetDescriptorForProxy(mirror::Class* proxy_class) { return DotToDescriptor(name->ToModifiedUtf8().c_str()); } -mirror::ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, - mirror::ArtMethod* proxy_method) { +ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, + ArtMethod* proxy_method) { DCHECK(proxy_class->IsProxyClass()); DCHECK(proxy_method->IsProxyMethod()); - // Locate the dex cache of the original interface/Object - mirror::DexCache* dex_cache = nullptr; { ReaderMutexLock mu(Thread::Current(), dex_lock_); - for (size_t i = 0; i != dex_caches_.size(); ++i) { - mirror::DexCache* a_dex_cache = GetDexCache(i); - if (proxy_method->HasSameDexCacheResolvedTypes(a_dex_cache->GetResolvedTypes())) { - dex_cache = a_dex_cache; - break; + // Locate the dex cache of the original interface/Object + for (const GcRoot<mirror::DexCache>& root : dex_caches_) { + auto* dex_cache = root.Read(); + if (proxy_method->HasSameDexCacheResolvedTypes(dex_cache->GetResolvedTypes())) { + ArtMethod* resolved_method = dex_cache->GetResolvedMethod( + proxy_method->GetDexMethodIndex(), image_pointer_size_); + CHECK(resolved_method != nullptr); + return resolved_method; } } } - CHECK(dex_cache != nullptr); - uint32_t method_idx = proxy_method->GetDexMethodIndex(); - mirror::ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_idx); - CHECK(resolved_method != nullptr); - return resolved_method; + LOG(FATAL) << "Didn't find dex cache for " << PrettyClass(proxy_class) << " " + << PrettyMethod(proxy_method); + UNREACHABLE(); } - -mirror::ArtMethod* ClassLinker::CreateProxyConstructor(Thread* self, - Handle<mirror::Class> klass, - mirror::Class* proxy_class) { - // Create constructor for Proxy that must initialize h - mirror::ObjectArray<mirror::ArtMethod>* proxy_direct_methods = - proxy_class->GetDirectMethods(); - CHECK_EQ(proxy_direct_methods->GetLength(), 16); - mirror::ArtMethod* proxy_constructor = proxy_direct_methods->Get(2); +void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) { + // Create constructor for Proxy that must initialize the method. + CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 16u); + ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->GetDirectMethodUnchecked( + 2, image_pointer_size_); // Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden // constructor method. - proxy_class->GetDexCache()->SetResolvedMethod(proxy_constructor->GetDexMethodIndex(), - proxy_constructor); + GetClassRoot(kJavaLangReflectProxy)->GetDexCache()->SetResolvedMethod( + proxy_constructor->GetDexMethodIndex(), proxy_constructor, image_pointer_size_); // Clone the existing constructor of Proxy (our constructor would just invoke it so steal its // code_ too) - mirror::ArtMethod* constructor = down_cast<mirror::ArtMethod*>(proxy_constructor->Clone(self)); - if (constructor == nullptr) { - CHECK(self->IsExceptionPending()); // OOME. - return nullptr; - } + DCHECK(out != nullptr); + out->CopyFrom(proxy_constructor, image_pointer_size_); // Make this constructor public and fix the class to be our Proxy version - constructor->SetAccessFlags((constructor->GetAccessFlags() & ~kAccProtected) | kAccPublic); - constructor->SetDeclaringClass(klass.Get()); - return constructor; + out->SetAccessFlags((out->GetAccessFlags() & ~kAccProtected) | kAccPublic); + out->SetDeclaringClass(klass.Get()); } -static void CheckProxyConstructor(mirror::ArtMethod* constructor) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +void ClassLinker::CheckProxyConstructor(ArtMethod* constructor) const { CHECK(constructor->IsConstructor()); - CHECK_STREQ(constructor->GetName(), "<init>"); - CHECK_STREQ(constructor->GetSignature().ToString().c_str(), - "(Ljava/lang/reflect/InvocationHandler;)V"); + auto* np = constructor->GetInterfaceMethodIfProxy(image_pointer_size_); + CHECK_STREQ(np->GetName(), "<init>"); + CHECK_STREQ(np->GetSignature().ToString().c_str(), "(Ljava/lang/reflect/InvocationHandler;)V"); DCHECK(constructor->IsPublic()); } -mirror::ArtMethod* ClassLinker::CreateProxyMethod(Thread* self, - Handle<mirror::Class> klass, - Handle<mirror::ArtMethod> prototype) { +void ClassLinker::CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype, + ArtMethod* out) { // Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden // prototype method - prototype->GetDeclaringClass()->GetDexCache()->SetResolvedMethod(prototype->GetDexMethodIndex(), - prototype.Get()); + auto* dex_cache = prototype->GetDeclaringClass()->GetDexCache(); + // Avoid dirtying the dex cache unless we need to. + if (dex_cache->GetResolvedMethod(prototype->GetDexMethodIndex(), image_pointer_size_) != + prototype) { + dex_cache->SetResolvedMethod( + prototype->GetDexMethodIndex(), prototype, image_pointer_size_); + } // We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialize // as necessary - mirror::ArtMethod* method = down_cast<mirror::ArtMethod*>(prototype->Clone(self)); - if (UNLIKELY(method == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return nullptr; - } + DCHECK(out != nullptr); + out->CopyFrom(prototype, image_pointer_size_); // Set class to be the concrete proxy class and clear the abstract flag, modify exceptions to // the intersection of throw exceptions as defined in Proxy - method->SetDeclaringClass(klass.Get()); - method->SetAccessFlags((method->GetAccessFlags() & ~kAccAbstract) | kAccFinal); + out->SetDeclaringClass(klass.Get()); + out->SetAccessFlags((out->GetAccessFlags() & ~kAccAbstract) | kAccFinal); // At runtime the method looks like a reference and argument saving method, clone the code // related parameters from this method. - method->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()); -#if defined(ART_USE_PORTABLE_COMPILER) - method->SetEntryPointFromPortableCompiledCode(GetPortableProxyInvokeHandler()); -#endif - method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); - - return method; + out->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()); + out->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); } -static void CheckProxyMethod(Handle<mirror::ArtMethod> method, Handle<mirror::ArtMethod> prototype) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) const { // Basic sanity CHECK(!prototype->IsFinal()); CHECK(method->IsFinal()); @@ -4040,27 +3502,26 @@ static void CheckProxyMethod(Handle<mirror::ArtMethod> method, Handle<mirror::Ar // The proxy method doesn't have its own dex cache or dex file and so it steals those of its // interface prototype. The exception to this are Constructors and the Class of the Proxy itself. - CHECK(prototype->HasSameDexCacheResolvedMethods(method.Get())); - CHECK(prototype->HasSameDexCacheResolvedTypes(method.Get())); + CHECK(prototype->HasSameDexCacheResolvedMethods(method)); + CHECK(prototype->HasSameDexCacheResolvedTypes(method)); + auto* np = method->GetInterfaceMethodIfProxy(image_pointer_size_); + CHECK_EQ(prototype->GetDeclaringClass()->GetDexCache(), np->GetDexCache()); CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex()); - MethodHelper mh(method); - MethodHelper mh2(prototype); - CHECK_STREQ(method->GetName(), prototype->GetName()); - CHECK_STREQ(method->GetShorty(), prototype->GetShorty()); + CHECK_STREQ(np->GetName(), prototype->GetName()); + CHECK_STREQ(np->GetShorty(), prototype->GetShorty()); // More complex sanity - via dex cache - CHECK_EQ(mh.GetReturnType(), mh2.GetReturnType()); + CHECK_EQ(np->GetReturnType(), prototype->GetReturnType()); } -static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, - bool can_init_parents) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +bool ClassLinker::CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, + bool can_init_parents) { if (can_init_statics && can_init_parents) { return true; } if (!can_init_statics) { // Check if there's a class initializer. - mirror::ArtMethod* clinit = klass->FindClassInitializer(); + ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_); if (clinit != nullptr) { return false; } @@ -4073,25 +3534,18 @@ static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, } } } - if (!klass->IsInterface() && klass->HasSuperClass()) { - mirror::Class* super_class = klass->GetSuperClass(); - if (!can_init_parents && !super_class->IsInitialized()) { - return false; - } else { - if (!CanWeInitializeClass(super_class, can_init_statics, can_init_parents)) { - return false; - } - } + if (klass->IsInterface() || !klass->HasSuperClass()) { + return true; } - return true; -} - -bool ClassLinker::IsInitialized() const { - return init_done_; + mirror::Class* super_class = klass->GetSuperClass(); + if (!can_init_parents && !super_class->IsInitialized()) { + return false; + } + return CanWeInitializeClass(super_class, can_init_statics, can_init_parents); } -bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_statics, - bool can_init_parents) { +bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, + bool can_init_statics, bool can_init_parents) { // see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol // Are we already initialized and therefore done? @@ -4106,7 +3560,7 @@ bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_sta return false; } - Thread* self = Thread::Current(); + self->AllowThreadSuspension(); uint64_t t0; { ObjectLock<mirror::Class> lock(self, klass); @@ -4119,20 +3573,22 @@ bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_sta // Was the class already found to be erroneous? Done under the lock to match the JLS. if (klass->IsErroneous()) { ThrowEarlierClassFailure(klass.Get()); + VlogClassInitializationFailure(klass); return false; } CHECK(klass->IsResolved()) << PrettyClass(klass.Get()) << ": state=" << klass->GetStatus(); if (!klass->IsVerified()) { - VerifyClass(klass); + VerifyClass(self, klass); if (!klass->IsVerified()) { // We failed to verify, expect either the klass to be erroneous or verification failed at // compile time. if (klass->IsErroneous()) { CHECK(self->IsExceptionPending()); + VlogClassInitializationFailure(klass); } else { - CHECK(Runtime::Current()->IsCompiler()); + CHECK(Runtime::Current()->IsAotCompiler()); CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime); } return false; @@ -4149,6 +3605,7 @@ bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_sta if (klass->GetStatus() == mirror::Class::kStatusInitializing) { // Could have got an exception during verification. if (self->IsExceptionPending()) { + VlogClassInitializationFailure(klass); return false; } // We caught somebody else in the act; was it us? @@ -4161,16 +3618,17 @@ bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_sta } if (!ValidateSuperClassDescriptors(klass)) { - klass->SetStatus(mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); return false; } + self->AllowThreadSuspension(); CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusVerified) << PrettyClass(klass.Get()); // From here out other threads may observe that we're initializing and so changes of state // require the a notification. klass->SetClinitThreadId(self->GetTid()); - klass->SetStatus(mirror::Class::kStatusInitializing, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusInitializing, self); t0 = NanoTime(); } @@ -4183,7 +3641,7 @@ bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_sta CHECK(can_init_parents); StackHandleScope<1> hs(self); Handle<mirror::Class> handle_scope_super(hs.NewHandle(super_class)); - bool super_initialized = InitializeClass(handle_scope_super, can_init_statics, true); + bool super_initialized = InitializeClass(self, handle_scope_super, can_init_statics, true); if (!super_initialized) { // The super class was verified ahead of entering initializing, we should only be here if // the super class became erroneous due to initialization. @@ -4192,10 +3650,10 @@ bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_sta << PrettyDescriptor(handle_scope_super.Get()) << " that has unexpected status " << handle_scope_super->GetStatus() << "\nPending exception:\n" - << (self->GetException(nullptr) != nullptr ? self->GetException(nullptr)->Dump() : ""); + << (self->GetException() != nullptr ? self->GetException()->Dump() : ""); ObjectLock<mirror::Class> lock(self, klass); // Initialization failed because the super-class is erroneous. - klass->SetStatus(mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); return false; } } @@ -4206,48 +3664,51 @@ bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_sta const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); CHECK(dex_class_def != nullptr); const DexFile& dex_file = klass->GetDexFile(); - StackHandleScope<2> hs(self); + StackHandleScope<3> hs(self); Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); // Eagerly fill in static fields so that the we don't have to do as many expensive // Class::FindStaticField in ResolveField. for (size_t i = 0; i < num_static_fields; ++i) { - mirror::ArtField* field = klass->GetStaticField(i); + ArtField* field = klass->GetStaticField(i); const uint32_t field_idx = field->GetDexFieldIndex(); - mirror::ArtField* resolved_field = dex_cache->GetResolvedField(field_idx); + ArtField* resolved_field = dex_cache->GetResolvedField(field_idx, image_pointer_size_); if (resolved_field == nullptr) { - dex_cache->SetResolvedField(field_idx, field); + dex_cache->SetResolvedField(field_idx, field, image_pointer_size_); } else { DCHECK_EQ(field, resolved_field); } } - EncodedStaticFieldValueIterator it(dex_file, &dex_cache, &class_loader, - this, *dex_class_def); - if (it.HasNext()) { + EncodedStaticFieldValueIterator value_it(dex_file, &dex_cache, &class_loader, + this, *dex_class_def); + const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); + ClassDataItemIterator field_it(dex_file, class_data); + if (value_it.HasNext()) { + DCHECK(field_it.HasNextStaticField()); CHECK(can_init_statics); - // We reordered the fields, so we need to be able to map the - // field indexes to the right fields. - SafeMap<uint32_t, mirror::ArtField*> field_map; - ConstructFieldMap(dex_file, *dex_class_def, klass.Get(), field_map); - for (size_t i = 0; it.HasNext(); i++, it.Next()) { + for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) { + ArtField* field = ResolveField( + dex_file, field_it.GetMemberIndex(), dex_cache, class_loader, true); if (Runtime::Current()->IsActiveTransaction()) { - it.ReadValueToField<true>(field_map.Get(i)); + value_it.ReadValueToField<true>(field); } else { - it.ReadValueToField<false>(field_map.Get(i)); + value_it.ReadValueToField<false>(field); } + DCHECK(!value_it.HasNext() || field_it.HasNextStaticField()); } } } - mirror::ArtMethod* clinit = klass->FindClassInitializer(); + ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_); if (clinit != nullptr) { CHECK(can_init_statics); JValue result; clinit->Invoke(self, nullptr, 0, &result, "V"); } + self->AllowThreadSuspension(); uint64_t t1 = NanoTime(); bool success = true; @@ -4255,8 +3716,16 @@ bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_sta ObjectLock<mirror::Class> lock(self, klass); if (self->IsExceptionPending()) { - WrapExceptionInInitializer(); - klass->SetStatus(mirror::Class::kStatusError, self); + WrapExceptionInInitializer(klass); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + success = false; + } else if (Runtime::Current()->IsTransactionAborted()) { + // The exception thrown when the transaction aborted has been caught and cleared + // so we need to throw it again now. + VLOG(compiler) << "Return from class initializer of " << PrettyDescriptor(klass.Get()) + << " without exception while transaction was aborted: re-throw it now."; + Runtime::Current()->ThrowTransactionAbortError(self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); success = false; } else { RuntimeStats* global_stats = Runtime::Current()->GetStats(); @@ -4266,7 +3735,7 @@ bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_sta global_stats->class_init_time_ns += (t1 - t0); thread_stats->class_init_time_ns += (t1 - t0); // Set the class as initialized except if failed to initialize static fields. - klass->SetStatus(mirror::Class::kStatusInitialized, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusInitialized, self); if (VLOG_IS_ON(class_linker)) { std::string temp; LOG(INFO) << "Initialized class " << klass->GetDescriptor(&temp) << " from " << @@ -4289,17 +3758,18 @@ bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, Thread* se // When we wake up, repeat the test for init-in-progress. If // there's an exception pending (only possible if - // "interruptShouldThrow" was set), bail out. + // we were not using WaitIgnoringInterrupts), bail out. if (self->IsExceptionPending()) { - WrapExceptionInInitializer(); - klass->SetStatus(mirror::Class::kStatusError, self); + WrapExceptionInInitializer(klass); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); return false; } // Spurious wakeup? Go back to waiting. if (klass->GetStatus() == mirror::Class::kStatusInitializing) { continue; } - if (klass->GetStatus() == mirror::Class::kStatusVerified && Runtime::Current()->IsCompiler()) { + if (klass->GetStatus() == mirror::Class::kStatusVerified && + Runtime::Current()->IsAotCompiler()) { // Compile time initialization failed. return false; } @@ -4308,6 +3778,7 @@ bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, Thread* se // different thread. Synthesize one here. ThrowNoClassDefFoundError("<clinit> failed for class %s; see exception in other thread", PrettyDescriptor(klass.Get()).c_str()); + VlogClassInitializationFailure(klass); return false; } if (klass->IsInitialized()) { @@ -4316,47 +3787,191 @@ bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, Thread* se LOG(FATAL) << "Unexpected class status. " << PrettyClass(klass.Get()) << " is " << klass->GetStatus(); } - LOG(FATAL) << "Not Reached" << PrettyClass(klass.Get()); + UNREACHABLE(); } +static void ThrowSignatureCheckResolveReturnTypeException(Handle<mirror::Class> klass, + Handle<mirror::Class> super_klass, + ArtMethod* method, + ArtMethod* m) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(Thread::Current()->IsExceptionPending()); + DCHECK(!m->IsProxyMethod()); + const DexFile* dex_file = m->GetDexFile(); + const DexFile::MethodId& method_id = dex_file->GetMethodId(m->GetDexMethodIndex()); + const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); + uint16_t return_type_idx = proto_id.return_type_idx_; + std::string return_type = PrettyType(return_type_idx, *dex_file); + std::string class_loader = PrettyTypeOf(m->GetDeclaringClass()->GetClassLoader()); + ThrowWrappedLinkageError(klass.Get(), + "While checking class %s method %s signature against %s %s: " + "Failed to resolve return type %s with %s", + PrettyDescriptor(klass.Get()).c_str(), + PrettyMethod(method).c_str(), + super_klass->IsInterface() ? "interface" : "superclass", + PrettyDescriptor(super_klass.Get()).c_str(), + return_type.c_str(), class_loader.c_str()); +} + +static void ThrowSignatureCheckResolveArgException(Handle<mirror::Class> klass, + Handle<mirror::Class> super_klass, + ArtMethod* method, + ArtMethod* m, + uint32_t index, uint32_t arg_type_idx) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(Thread::Current()->IsExceptionPending()); + DCHECK(!m->IsProxyMethod()); + const DexFile* dex_file = m->GetDexFile(); + std::string arg_type = PrettyType(arg_type_idx, *dex_file); + std::string class_loader = PrettyTypeOf(m->GetDeclaringClass()->GetClassLoader()); + ThrowWrappedLinkageError(klass.Get(), + "While checking class %s method %s signature against %s %s: " + "Failed to resolve arg %u type %s with %s", + PrettyDescriptor(klass.Get()).c_str(), + PrettyMethod(method).c_str(), + super_klass->IsInterface() ? "interface" : "superclass", + PrettyDescriptor(super_klass.Get()).c_str(), + index, arg_type.c_str(), class_loader.c_str()); +} + +static void ThrowSignatureMismatch(Handle<mirror::Class> klass, + Handle<mirror::Class> super_klass, + ArtMethod* method, + const std::string& error_msg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ThrowLinkageError(klass.Get(), + "Class %s method %s resolves differently in %s %s: %s", + PrettyDescriptor(klass.Get()).c_str(), + PrettyMethod(method).c_str(), + super_klass->IsInterface() ? "interface" : "superclass", + PrettyDescriptor(super_klass.Get()).c_str(), + error_msg.c_str()); +} + +static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, + Handle<mirror::Class> klass, + Handle<mirror::Class> super_klass, + ArtMethod* method1, + ArtMethod* method2) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + { + StackHandleScope<1> hs(self); + Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType())); + if (UNLIKELY(return_type.Get() == nullptr)) { + ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1); + return false; + } + mirror::Class* other_return_type = method2->GetReturnType(); + if (UNLIKELY(other_return_type == nullptr)) { + ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2); + return false; + } + if (UNLIKELY(other_return_type != return_type.Get())) { + ThrowSignatureMismatch(klass, super_klass, method1, + StringPrintf("Return types mismatch: %s(%p) vs %s(%p)", + PrettyClassAndClassLoader(return_type.Get()).c_str(), + return_type.Get(), + PrettyClassAndClassLoader(other_return_type).c_str(), + other_return_type)); + return false; + } + } + const DexFile::TypeList* types1 = method1->GetParameterTypeList(); + const DexFile::TypeList* types2 = method2->GetParameterTypeList(); + if (types1 == nullptr) { + if (types2 != nullptr && types2->Size() != 0) { + ThrowSignatureMismatch(klass, super_klass, method1, + StringPrintf("Type list mismatch with %s", + PrettyMethod(method2, true).c_str())); + return false; + } + return true; + } else if (UNLIKELY(types2 == nullptr)) { + if (types1->Size() != 0) { + ThrowSignatureMismatch(klass, super_klass, method1, + StringPrintf("Type list mismatch with %s", + PrettyMethod(method2, true).c_str())); + return false; + } + return true; + } + uint32_t num_types = types1->Size(); + if (UNLIKELY(num_types != types2->Size())) { + ThrowSignatureMismatch(klass, super_klass, method1, + StringPrintf("Type list mismatch with %s", + PrettyMethod(method2, true).c_str())); + return false; + } + for (uint32_t i = 0; i < num_types; ++i) { + StackHandleScope<1> hs(self); + uint32_t param_type_idx = types1->GetTypeItem(i).type_idx_; + Handle<mirror::Class> param_type(hs.NewHandle( + method1->GetClassFromTypeIndex(param_type_idx, true))); + if (UNLIKELY(param_type.Get() == nullptr)) { + ThrowSignatureCheckResolveArgException(klass, super_klass, method1, + method1, i, param_type_idx); + return false; + } + uint32_t other_param_type_idx = types2->GetTypeItem(i).type_idx_; + mirror::Class* other_param_type = + method2->GetClassFromTypeIndex(other_param_type_idx, true); + if (UNLIKELY(other_param_type == nullptr)) { + ThrowSignatureCheckResolveArgException(klass, super_klass, method1, + method2, i, other_param_type_idx); + return false; + } + if (UNLIKELY(param_type.Get() != other_param_type)) { + ThrowSignatureMismatch(klass, super_klass, method1, + StringPrintf("Parameter %u type mismatch: %s(%p) vs %s(%p)", + i, + PrettyClassAndClassLoader(param_type.Get()).c_str(), + param_type.Get(), + PrettyClassAndClassLoader(other_param_type).c_str(), + other_param_type)); + return false; + } + } + return true; +} + + bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { if (klass->IsInterface()) { return true; } // Begin with the methods local to the superclass. - StackHandleScope<2> hs(Thread::Current()); - MethodHelper mh(hs.NewHandle<mirror::ArtMethod>(nullptr)); - MethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr)); + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + MutableHandle<mirror::Class> super_klass(hs.NewHandle<mirror::Class>(nullptr)); if (klass->HasSuperClass() && klass->GetClassLoader() != klass->GetSuperClass()->GetClassLoader()) { + super_klass.Assign(klass->GetSuperClass()); for (int i = klass->GetSuperClass()->GetVTableLength() - 1; i >= 0; --i) { - mh.ChangeMethod(klass->GetVTableEntry(i)); - super_mh.ChangeMethod(klass->GetSuperClass()->GetVTableEntry(i)); - if (mh.GetMethod() != super_mh.GetMethod() && - !mh.HasSameSignatureWithDifferentClassLoaders(&super_mh)) { - ThrowLinkageError(klass.Get(), - "Class %s method %s resolves differently in superclass %s", - PrettyDescriptor(klass.Get()).c_str(), - PrettyMethod(mh.GetMethod()).c_str(), - PrettyDescriptor(klass->GetSuperClass()).c_str()); - return false; + auto* m = klass->GetVTableEntry(i, image_pointer_size_); + auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_); + if (m != super_m) { + if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, klass, super_klass, + m, super_m))) { + self->AssertPendingException(); + return false; + } } } } for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { - if (klass->GetClassLoader() != klass->GetIfTable()->GetInterface(i)->GetClassLoader()) { - uint32_t num_methods = klass->GetIfTable()->GetInterface(i)->NumVirtualMethods(); + super_klass.Assign(klass->GetIfTable()->GetInterface(i)); + if (klass->GetClassLoader() != super_klass->GetClassLoader()) { + uint32_t num_methods = super_klass->NumVirtualMethods(); for (uint32_t j = 0; j < num_methods; ++j) { - mh.ChangeMethod(klass->GetIfTable()->GetMethodArray(i)->GetWithoutChecks(j)); - super_mh.ChangeMethod(klass->GetIfTable()->GetInterface(i)->GetVirtualMethod(j)); - if (mh.GetMethod() != super_mh.GetMethod() && - !mh.HasSameSignatureWithDifferentClassLoaders(&super_mh)) { - ThrowLinkageError(klass.Get(), - "Class %s method %s resolves differently in interface %s", - PrettyDescriptor(klass.Get()).c_str(), - PrettyMethod(mh.GetMethod()).c_str(), - PrettyDescriptor(klass->GetIfTable()->GetInterface(i)).c_str()); - return false; + auto* m = klass->GetIfTable()->GetMethodArray(i)->GetElementPtrSize<ArtMethod*>( + j, image_pointer_size_); + auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_); + if (m != super_m) { + if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, klass, super_klass, + m, super_m))) { + self->AssertPendingException(); + return false; + } } } } @@ -4364,15 +3979,14 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { return true; } -bool ClassLinker::EnsureInitialized(Handle<mirror::Class> c, bool can_init_fields, +bool ClassLinker::EnsureInitialized(Thread* self, Handle<mirror::Class> c, bool can_init_fields, bool can_init_parents) { DCHECK(c.Get() != nullptr); if (c->IsInitialized()) { EnsurePreverifiedMethods(c); return true; } - const bool success = InitializeClass(c, can_init_fields, can_init_parents); - Thread* self = Thread::Current(); + const bool success = InitializeClass(self, c, can_init_fields, can_init_parents); if (!success) { if (can_init_fields && can_init_parents) { CHECK(self->IsExceptionPending()) << PrettyClass(c.Get()); @@ -4383,80 +3997,60 @@ bool ClassLinker::EnsureInitialized(Handle<mirror::Class> c, bool can_init_field return success; } -void ClassLinker::ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, - mirror::Class* c, - SafeMap<uint32_t, mirror::ArtField*>& field_map) { - const byte* class_data = dex_file.GetClassData(dex_class_def); - ClassDataItemIterator it(dex_file, class_data); - StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(c->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(c->GetClassLoader())); - CHECK(!kMovingFields); - for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) { - field_map.Put(i, ResolveField(dex_file, it.GetMemberIndex(), dex_cache, class_loader, true)); - } -} - -void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class) { - mirror::ObjectArray<mirror::ArtField>* fields = new_class->GetIFields(); - if (fields != nullptr) { - for (int index = 0; index < fields->GetLength(); index ++) { - if (fields->Get(index)->GetDeclaringClass() == temp_class) { - fields->Get(index)->SetDeclaringClass(new_class); - } +void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, + mirror::Class* new_class) { + ArtField* fields = new_class->GetIFields(); + DCHECK_EQ(temp_class->NumInstanceFields(), new_class->NumInstanceFields()); + for (size_t i = 0, count = new_class->NumInstanceFields(); i < count; i++) { + if (fields[i].GetDeclaringClass() == temp_class) { + fields[i].SetDeclaringClass(new_class); } } fields = new_class->GetSFields(); - if (fields != nullptr) { - for (int index = 0; index < fields->GetLength(); index ++) { - if (fields->Get(index)->GetDeclaringClass() == temp_class) { - fields->Get(index)->SetDeclaringClass(new_class); - } + DCHECK_EQ(temp_class->NumStaticFields(), new_class->NumStaticFields()); + for (size_t i = 0, count = new_class->NumStaticFields(); i < count; i++) { + if (fields[i].GetDeclaringClass() == temp_class) { + fields[i].SetDeclaringClass(new_class); } } - mirror::ObjectArray<mirror::ArtMethod>* methods = new_class->GetDirectMethods(); - if (methods != nullptr) { - for (int index = 0; index < methods->GetLength(); index ++) { - if (methods->Get(index)->GetDeclaringClass() == temp_class) { - methods->Get(index)->SetDeclaringClass(new_class); - } + DCHECK_EQ(temp_class->NumDirectMethods(), new_class->NumDirectMethods()); + for (auto& method : new_class->GetDirectMethods(image_pointer_size_)) { + if (method.GetDeclaringClass() == temp_class) { + method.SetDeclaringClass(new_class); } } - methods = new_class->GetVirtualMethods(); - if (methods != nullptr) { - for (int index = 0; index < methods->GetLength(); index ++) { - if (methods->Get(index)->GetDeclaringClass() == temp_class) { - methods->Get(index)->SetDeclaringClass(new_class); - } + DCHECK_EQ(temp_class->NumVirtualMethods(), new_class->NumVirtualMethods()); + for (auto& method : new_class->GetVirtualMethods(image_pointer_size_)) { + if (method.GetDeclaringClass() == temp_class) { + method.SetDeclaringClass(new_class); } } } bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces, - mirror::Class** new_class) { + MutableHandle<mirror::Class>* h_new_class_out) { CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus()); if (!LinkSuperClass(klass)) { return false; } - StackHandleScope<mirror::Class::kImtSize> imt_handle_scope( - self, Runtime::Current()->GetImtUnimplementedMethod()); - if (!LinkMethods(self, klass, interfaces, &imt_handle_scope)) { + ArtMethod* imt[mirror::Class::kImtSize]; + std::fill_n(imt, arraysize(imt), Runtime::Current()->GetImtUnimplementedMethod()); + if (!LinkMethods(self, klass, interfaces, imt)) { return false; } - if (!LinkInstanceFields(klass)) { + if (!LinkInstanceFields(self, klass)) { return false; } size_t class_size; - if (!LinkStaticFields(klass, &class_size)) { + if (!LinkStaticFields(self, klass, &class_size)) { return false; } CreateReferenceInstanceOffsets(klass); - CreateReferenceStaticOffsets(klass); CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus()); if (!klass->IsTemp() || (!init_done_ && klass->GetClassSize() == class_size)) { @@ -4465,42 +4059,41 @@ bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror: CHECK_EQ(klass->GetClassSize(), class_size) << PrettyDescriptor(klass.Get()); if (klass->ShouldHaveEmbeddedImtAndVTable()) { - klass->PopulateEmbeddedImtAndVTable(&imt_handle_scope); + klass->PopulateEmbeddedImtAndVTable(imt, image_pointer_size_); } // This will notify waiters on klass that saw the not yet resolved // class in the class_table_ during EnsureResolved. - klass->SetStatus(mirror::Class::kStatusResolved, self); - *new_class = klass.Get(); + mirror::Class::SetStatus(klass, mirror::Class::kStatusResolved, self); + h_new_class_out->Assign(klass.Get()); } else { CHECK(!klass->IsResolved()); // Retire the temporary class and create the correctly sized resolved class. - *new_class = klass->CopyOf(self, class_size, &imt_handle_scope); - if (UNLIKELY(*new_class == nullptr)) { - CHECK(self->IsExceptionPending()); // Expect an OOME. - klass->SetStatus(mirror::Class::kStatusError, self); + StackHandleScope<1> hs(self); + auto h_new_class = hs.NewHandle(klass->CopyOf(self, class_size, imt, image_pointer_size_)); + if (UNLIKELY(h_new_class.Get() == nullptr)) { + self->AssertPendingOOMException(); + mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); return false; } - CHECK_EQ((*new_class)->GetClassSize(), class_size); - StackHandleScope<1> hs(self); - auto new_class_h = hs.NewHandleWrapper<mirror::Class>(new_class); - ObjectLock<mirror::Class> lock(self, new_class_h); - - FixupTemporaryDeclaringClass(klass.Get(), new_class_h.Get()); - - mirror::Class* existing = UpdateClass(descriptor, new_class_h.Get(), + CHECK_EQ(h_new_class->GetClassSize(), class_size); + ObjectLock<mirror::Class> lock(self, h_new_class); + FixupTemporaryDeclaringClass(klass.Get(), h_new_class.Get()); + mirror::Class* existing = UpdateClass(descriptor, h_new_class.Get(), ComputeModifiedUtf8Hash(descriptor)); CHECK(existing == nullptr || existing == klass.Get()); // This will notify waiters on temp class that saw the not yet resolved class in the // class_table_ during EnsureResolved. - klass->SetStatus(mirror::Class::kStatusRetired, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusRetired, self); - CHECK_EQ(new_class_h->GetStatus(), mirror::Class::kStatusResolving); + CHECK_EQ(h_new_class->GetStatus(), mirror::Class::kStatusResolving); // This will notify waiters on new_class that saw the not yet resolved // class in the class_table_ during EnsureResolved. - new_class_h->SetStatus(mirror::Class::kStatusResolved, self); + mirror::Class::SetStatus(h_new_class, mirror::Class::kStatusResolved, self); + // Return the new class. + h_new_class_out->Assign(h_new_class.Get()); } return true; } @@ -4633,9 +4226,19 @@ static bool CheckSuperClassChange(Handle<mirror::Class> klass, // Now comes the expensive part: things can be broken if (a) the klass' dex file has a // definition for the super-class, and (b) the files are in separate oat files. The oat files // are referenced from the dex file, so do (b) first. Only relevant if we have oat files. - const OatFile* class_oat_file = dex_file.GetOatFile(); + const OatDexFile* class_oat_dex_file = dex_file.GetOatDexFile(); + const OatFile* class_oat_file = nullptr; + if (class_oat_dex_file != nullptr) { + class_oat_file = class_oat_dex_file->GetOatFile(); + } + if (class_oat_file != nullptr) { - const OatFile* loaded_super_oat_file = super_class->GetDexFile().GetOatFile(); + const OatDexFile* loaded_super_oat_dex_file = super_class->GetDexFile().GetOatDexFile(); + const OatFile* loaded_super_oat_file = nullptr; + if (loaded_super_oat_dex_file != nullptr) { + loaded_super_oat_file = loaded_super_oat_dex_file->GetOatFile(); + } + if (loaded_super_oat_file != nullptr && class_oat_file != loaded_super_oat_file) { // Now check (a). const DexFile::ClassDef* super_class_def = dex_file.FindClassDef(class_def.superclass_idx_); @@ -4715,7 +4318,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexF } } // Mark the class as loaded. - klass->SetStatus(mirror::Class::kStatusLoaded, nullptr); + mirror::Class::SetStatus(klass, mirror::Class::kStatusLoaded, nullptr); return true; } @@ -4781,16 +4384,17 @@ bool ClassLinker::LinkSuperClass(Handle<mirror::Class> klass) { // Populate the class vtable and itable. Compute return type indices. bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces, - StackHandleScope<mirror::Class::kImtSize>* out_imt) { + ArtMethod** out_imt) { + self->AllowThreadSuspension(); if (klass->IsInterface()) { // No vtable. size_t count = klass->NumVirtualMethods(); - if (!IsUint(16, count)) { + if (!IsUint<16>(count)) { ThrowClassFormatError(klass.Get(), "Too many methods on interface: %zd", count); return false; } for (size_t i = 0; i < count; ++i) { - klass->GetVirtualMethodDuringLinking(i)->SetMethodIndex(i); + klass->GetVirtualMethodDuringLinking(i, image_pointer_size_)->SetMethodIndex(i); } } else if (!LinkVirtualMethods(self, klass)) { // Link virtual methods first. return false; @@ -4801,9 +4405,9 @@ bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass, // Comparator for name and signature of a method, used in finding overriding methods. Implementation // avoids the use of handles, if it didn't then rather than compare dex files we could compare dex // caches in the implementation below. -class MethodNameAndSignatureComparator FINAL { +class MethodNameAndSignatureComparator FINAL : public ValueObject { public: - explicit MethodNameAndSignatureComparator(mirror::ArtMethod* method) + explicit MethodNameAndSignatureComparator(ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : dex_file_(method->GetDexFile()), mid_(&dex_file_->GetMethodId(method->GetDexMethodIndex())), name_(nullptr), name_len_(0) { @@ -4817,7 +4421,7 @@ class MethodNameAndSignatureComparator FINAL { return name_; } - bool HasSameNameAndSignature(mirror::ArtMethod* other) + bool HasSameNameAndSignature(ArtMethod* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(!other->IsProxyMethod()) << PrettyMethod(other); const DexFile* other_dex_file = other->GetDexFile(); @@ -4848,13 +4452,16 @@ class MethodNameAndSignatureComparator FINAL { class LinkVirtualHashTable { public: - LinkVirtualHashTable(Handle<mirror::Class> klass, size_t hash_size, uint32_t* hash_table) - : klass_(klass), hash_size_(hash_size), hash_table_(hash_table) { + LinkVirtualHashTable(Handle<mirror::Class> klass, size_t hash_size, uint32_t* hash_table, + size_t image_pointer_size) + : klass_(klass), hash_size_(hash_size), hash_table_(hash_table), + image_pointer_size_(image_pointer_size) { std::fill(hash_table_, hash_table_ + hash_size_, invalid_index_); } void Add(uint32_t virtual_method_index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ArtMethod* local_method = klass_->GetVirtualMethodDuringLinking(virtual_method_index); - const char* name = local_method->GetName(); + ArtMethod* local_method = klass_->GetVirtualMethodDuringLinking( + virtual_method_index, image_pointer_size_); + const char* name = local_method->GetInterfaceMethodIfProxy(image_pointer_size_)->GetName(); uint32_t hash = ComputeModifiedUtf8Hash(name); uint32_t index = hash % hash_size_; // Linear probe until we have an empty slot. @@ -4878,9 +4485,10 @@ class LinkVirtualHashTable { break; } if (value != removed_index_) { // This signifies not already overriden. - mirror::ArtMethod* virtual_method = - klass_->GetVirtualMethodDuringLinking(value); - if (comparator->HasSameNameAndSignature(virtual_method->GetInterfaceMethodIfProxy())) { + ArtMethod* virtual_method = + klass_->GetVirtualMethodDuringLinking(value, image_pointer_size_); + if (comparator->HasSameNameAndSignature( + virtual_method->GetInterfaceMethodIfProxy(image_pointer_size_))) { hash_table_[index] = removed_index_; return value; } @@ -4902,6 +4510,7 @@ class LinkVirtualHashTable { Handle<mirror::Class> klass_; const size_t hash_size_; uint32_t* const hash_table_; + const size_t image_pointer_size_; }; const uint32_t LinkVirtualHashTable::invalid_index_ = std::numeric_limits<uint32_t>::max(); @@ -4914,30 +4523,32 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) const size_t max_count = num_virtual_methods + super_vtable_length; StackHandleScope<2> hs(self); Handle<mirror::Class> super_class(hs.NewHandle(klass->GetSuperClass())); - Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable; + MutableHandle<mirror::PointerArray> vtable; if (super_class->ShouldHaveEmbeddedImtAndVTable()) { - vtable = hs.NewHandle(AllocArtMethodArray(self, max_count)); + vtable = hs.NewHandle(AllocPointerArray(self, max_count)); if (UNLIKELY(vtable.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. + self->AssertPendingOOMException(); return false; } for (size_t i = 0; i < super_vtable_length; i++) { - vtable->SetWithoutChecks<false>(i, super_class->GetEmbeddedVTableEntry(i)); + vtable->SetElementPtrSize( + i, super_class->GetEmbeddedVTableEntry(i, image_pointer_size_), image_pointer_size_); } if (num_virtual_methods == 0) { klass->SetVTable(vtable.Get()); return true; } } else { - mirror::ObjectArray<mirror::ArtMethod>* super_vtable = super_class->GetVTable(); + auto* super_vtable = super_class->GetVTable(); CHECK(super_vtable != nullptr) << PrettyClass(super_class.Get()); if (num_virtual_methods == 0) { klass->SetVTable(super_vtable); return true; } - vtable = hs.NewHandle(super_vtable->CopyOf(self, max_count)); + vtable = hs.NewHandle(down_cast<mirror::PointerArray*>( + super_vtable->CopyOf(self, max_count))); if (UNLIKELY(vtable.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. + self->AssertPendingOOMException(); return false; } } @@ -4961,21 +4572,24 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) hash_heap_storage.reset(new uint32_t[hash_table_size]); hash_table_ptr = hash_heap_storage.get(); } - LinkVirtualHashTable hash_table(klass, hash_table_size, hash_table_ptr); + LinkVirtualHashTable hash_table(klass, hash_table_size, hash_table_ptr, image_pointer_size_); // Add virtual methods to the hash table. for (size_t i = 0; i < num_virtual_methods; ++i) { + DCHECK(klass->GetVirtualMethodDuringLinking( + i, image_pointer_size_)->GetDeclaringClass() != nullptr); hash_table.Add(i); } // Loop through each super vtable method and see if they are overriden by a method we added to // the hash table. for (size_t j = 0; j < super_vtable_length; ++j) { // Search the hash table to see if we are overidden by any method. - mirror::ArtMethod* super_method = vtable->GetWithoutChecks(j); + ArtMethod* super_method = vtable->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_); MethodNameAndSignatureComparator super_method_name_comparator( - super_method->GetInterfaceMethodIfProxy()); + super_method->GetInterfaceMethodIfProxy(image_pointer_size_)); uint32_t hash_index = hash_table.FindAndRemove(&super_method_name_comparator); if (hash_index != hash_table.GetNotFoundIndex()) { - mirror::ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(hash_index); + ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking( + hash_index, image_pointer_size_); if (klass->CanAccessMember(super_method->GetDeclaringClass(), super_method->GetAccessFlags())) { if (super_method->IsFinal()) { @@ -4984,7 +4598,7 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) super_method->GetDeclaringClassDescriptor()); return false; } - vtable->SetWithoutChecks<false>(j, virtual_method); + vtable->SetElementPtrSize(j, virtual_method, image_pointer_size_); virtual_method->SetMethodIndex(j); } else { LOG(WARNING) << "Before Android 4.1, method " << PrettyMethod(virtual_method) @@ -4996,45 +4610,45 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) // Add the non overridden methods at the end. size_t actual_count = super_vtable_length; for (size_t i = 0; i < num_virtual_methods; ++i) { - mirror::ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i); + ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_); size_t method_idx = local_method->GetMethodIndexDuringLinking(); if (method_idx < super_vtable_length && - local_method == vtable->GetWithoutChecks(method_idx)) { + local_method == vtable->GetElementPtrSize<ArtMethod*>(method_idx, image_pointer_size_)) { continue; } - vtable->SetWithoutChecks<false>(actual_count, local_method); + vtable->SetElementPtrSize(actual_count, local_method, image_pointer_size_); local_method->SetMethodIndex(actual_count); ++actual_count; } - if (!IsUint(16, actual_count)) { + if (!IsUint<16>(actual_count)) { ThrowClassFormatError(klass.Get(), "Too many methods defined on class: %zd", actual_count); return false; } // Shrink vtable if possible CHECK_LE(actual_count, max_count); if (actual_count < max_count) { - vtable.Assign(vtable->CopyOf(self, actual_count)); + vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, actual_count))); if (UNLIKELY(vtable.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. + self->AssertPendingOOMException(); return false; } } klass->SetVTable(vtable.Get()); } else { CHECK_EQ(klass.Get(), GetClassRoot(kJavaLangObject)); - if (!IsUint(16, num_virtual_methods)) { + if (!IsUint<16>(num_virtual_methods)) { ThrowClassFormatError(klass.Get(), "Too many methods: %d", static_cast<int>(num_virtual_methods)); return false; } - mirror::ObjectArray<mirror::ArtMethod>* vtable = AllocArtMethodArray(self, num_virtual_methods); + auto* vtable = AllocPointerArray(self, num_virtual_methods); if (UNLIKELY(vtable == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. + self->AssertPendingOOMException(); return false; } for (size_t i = 0; i < num_virtual_methods; ++i) { - mirror::ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i); - vtable->SetWithoutChecks<false>(i, virtual_method); + ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_); + vtable->SetElementPtrSize(i, virtual_method, image_pointer_size_); virtual_method->SetMethodIndex(i & 0xFFFF); } klass->SetVTable(vtable); @@ -5044,14 +4658,15 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces, - StackHandleScope<mirror::Class::kImtSize>* out_imt) { - StackHandleScope<2> hs(self); + ArtMethod** out_imt) { + StackHandleScope<3> hs(self); Runtime* const runtime = Runtime::Current(); const bool has_superclass = klass->HasSuperClass(); const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U; const bool have_interfaces = interfaces.Get() != nullptr; const size_t num_interfaces = have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces(); + const size_t method_size = ArtMethod::ObjectSize(image_pointer_size_); if (num_interfaces == 0) { if (super_ifcount == 0) { // Class implements no interfaces. @@ -5088,9 +4703,9 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass } ifcount += interface->GetIfTableCount(); } - Handle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount))); + MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount))); if (UNLIKELY(iftable.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. + self->AssertPendingOOMException(); return false; } if (super_ifcount != 0) { @@ -5100,6 +4715,7 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass iftable->SetInterface(i, super_interface); } } + self->AllowThreadSuspension(); // Flatten the interface inheritance hierarchy. size_t idx = super_ifcount; for (size_t i = 0; i < num_interfaces; i++) { @@ -5134,12 +4750,14 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass } } } + self->AllowThreadSuspension(); // Shrink iftable in case duplicates were found if (idx < ifcount) { DCHECK_NE(num_interfaces, 0U); - iftable.Assign(down_cast<mirror::IfTable*>(iftable->CopyOf(self, idx * mirror::IfTable::kMax))); + iftable.Assign(down_cast<mirror::IfTable*>( + iftable->CopyOf(self, idx * mirror::IfTable::kMax))); if (UNLIKELY(iftable.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. + self->AssertPendingOOMException(); return false; } ifcount = idx; @@ -5151,9 +4769,18 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass if (klass->IsInterface()) { return true; } - Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable( - hs.NewHandle(klass->GetVTableDuringLinking())); - std::vector<mirror::ArtMethod*> miranda_list; + // These are allocated on the heap to begin, we then transfer to linear alloc when we re-create + // the virtual methods array. + // Need to use low 4GB arenas for compiler or else the pointers wont fit in 32 bit method array + // during cross compilation. + // Use the linear alloc pool since this one is in the low 4gb for the compiler. + ArenaStack stack(runtime->GetLinearAlloc()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); + ScopedArenaVector<ArtMethod*> miranda_methods(allocator.Adapter()); + + MutableHandle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking())); + ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod(); + ArtMethod* const conflict_method = runtime->GetImtConflictMethod(); // Copy the IMT from the super class if possible. bool extend_super_iftable = false; if (has_superclass) { @@ -5161,12 +4788,11 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass extend_super_iftable = true; if (super_class->ShouldHaveEmbeddedImtAndVTable()) { for (size_t i = 0; i < mirror::Class::kImtSize; ++i) { - out_imt->SetReference(i, super_class->GetEmbeddedImTableEntry(i)); + out_imt[i] = super_class->GetEmbeddedImTableEntry(i, image_pointer_size_); } } else { // No imt in the super class, need to reconstruct from the iftable. mirror::IfTable* if_table = super_class->GetIfTable(); - mirror::ArtMethod* conflict_method = runtime->GetImtConflictMethod(); const size_t length = super_class->GetIfTableCount(); for (size_t i = 0; i < length; ++i) { mirror::Class* interface = iftable->GetInterface(i); @@ -5176,62 +4802,84 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass if (method_array_count == 0) { continue; } - mirror::ObjectArray<mirror::ArtMethod>* method_array = if_table->GetMethodArray(i); + auto* method_array = if_table->GetMethodArray(i); for (size_t j = 0; j < num_virtuals; ++j) { - mirror::ArtMethod* method = method_array->GetWithoutChecks(j); + auto method = method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_); + DCHECK(method != nullptr) << PrettyClass(super_class); if (method->IsMiranda()) { continue; } - mirror::ArtMethod* interface_method = interface->GetVirtualMethod(j); + ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_); uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize; - mirror::ArtMethod* imt_ref = out_imt->GetReference(imt_index)->AsArtMethod(); - if (imt_ref == runtime->GetImtUnimplementedMethod()) { - out_imt->SetReference(imt_index, method); + auto*& imt_ref = out_imt[imt_index]; + if (imt_ref == unimplemented_method) { + imt_ref = method; } else if (imt_ref != conflict_method) { - out_imt->SetReference(imt_index, conflict_method); + imt_ref = conflict_method; } } } } } + // Allocate method arrays before since we don't want miss visiting miranda method roots due to + // thread suspension. for (size_t i = 0; i < ifcount; ++i) { size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods(); if (num_methods > 0) { - StackHandleScope<2> hs(self); const bool is_super = i < super_ifcount; const bool super_interface = is_super && extend_super_iftable; - Handle<mirror::ObjectArray<mirror::ArtMethod>> method_array; - Handle<mirror::ObjectArray<mirror::ArtMethod>> input_array; + mirror::PointerArray* method_array; if (super_interface) { mirror::IfTable* if_table = klass->GetSuperClass()->GetIfTable(); DCHECK(if_table != nullptr); DCHECK(if_table->GetMethodArray(i) != nullptr); // If we are working on a super interface, try extending the existing method array. - method_array = hs.NewHandle(if_table->GetMethodArray(i)->Clone(self)-> - AsObjectArray<mirror::ArtMethod>()); + method_array = down_cast<mirror::PointerArray*>(if_table->GetMethodArray(i)->Clone(self)); + } else { + method_array = AllocPointerArray(self, num_methods); + } + if (UNLIKELY(method_array == nullptr)) { + self->AssertPendingOOMException(); + return false; + } + iftable->SetMethodArray(i, method_array); + } + } + + auto* old_cause = self->StartAssertNoThreadSuspension( + "Copying ArtMethods for LinkInterfaceMethods"); + for (size_t i = 0; i < ifcount; ++i) { + size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods(); + if (num_methods > 0) { + StackHandleScope<2> hs2(self); + const bool is_super = i < super_ifcount; + const bool super_interface = is_super && extend_super_iftable; + auto method_array(hs2.NewHandle(iftable->GetMethodArray(i))); + + ArtMethod* input_virtual_methods = nullptr; + Handle<mirror::PointerArray> input_vtable_array = NullHandle<mirror::PointerArray>(); + int32_t input_array_length = 0; + if (super_interface) { // We are overwriting a super class interface, try to only virtual methods instead of the // whole vtable. - input_array = hs.NewHandle(klass->GetVirtualMethods()); + input_virtual_methods = klass->GetVirtualMethodsPtr(); + input_array_length = klass->NumVirtualMethods(); } else { - method_array = hs.NewHandle(AllocArtMethodArray(self, num_methods)); - // A new interface, we need the whole vtable incase a new interface method is implemented + // A new interface, we need the whole vtable in case a new interface method is implemented // in the whole superclass. - input_array = vtable; - } - if (UNLIKELY(method_array.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return false; + input_vtable_array = vtable; + input_array_length = input_vtable_array->GetLength(); } - iftable->SetMethodArray(i, method_array.Get()); - if (input_array.Get() == nullptr) { + if (input_array_length == 0) { // If the added virtual methods is empty, do nothing. DCHECK(super_interface); continue; } for (size_t j = 0; j < num_methods; ++j) { - mirror::ArtMethod* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j); + auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod( + j, image_pointer_size_); MethodNameAndSignatureComparator interface_name_comparator( - interface_method->GetInterfaceMethodIfProxy()); + interface_method->GetInterfaceMethodIfProxy(image_pointer_size_)); int32_t k; // For each method listed in the interface's method list, find the // matching method in our class's method list. We want to favor the @@ -5241,127 +4889,197 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass // it -- otherwise it would use the same vtable slot. In .dex files // those don't end up in the virtual method table, so it shouldn't // matter which direction we go. We walk it backward anyway.) - for (k = input_array->GetLength() - 1; k >= 0; --k) { - mirror::ArtMethod* vtable_method = input_array->GetWithoutChecks(k); - mirror::ArtMethod* vtable_method_for_name_comparison = - vtable_method->GetInterfaceMethodIfProxy(); + for (k = input_array_length - 1; k >= 0; --k) { + ArtMethod* vtable_method = input_virtual_methods != nullptr ? + reinterpret_cast<ArtMethod*>( + reinterpret_cast<uintptr_t>(input_virtual_methods) + method_size * k) : + input_vtable_array->GetElementPtrSize<ArtMethod*>(k, image_pointer_size_); + ArtMethod* vtable_method_for_name_comparison = + vtable_method->GetInterfaceMethodIfProxy(image_pointer_size_); if (interface_name_comparator.HasSameNameAndSignature( vtable_method_for_name_comparison)) { if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) { - ThrowIllegalAccessError( - klass.Get(), + // Must do EndAssertNoThreadSuspension before throw since the throw can cause + // allocations. + self->EndAssertNoThreadSuspension(old_cause); + ThrowIllegalAccessError(klass.Get(), "Method '%s' implementing interface method '%s' is not public", - PrettyMethod(vtable_method).c_str(), - PrettyMethod(interface_method).c_str()); + PrettyMethod(vtable_method).c_str(), PrettyMethod(interface_method).c_str()); return false; } - method_array->SetWithoutChecks<false>(j, vtable_method); + method_array->SetElementPtrSize(j, vtable_method, image_pointer_size_); // Place method in imt if entry is empty, place conflict otherwise. uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize; - mirror::ArtMethod* imt_ref = out_imt->GetReference(imt_index)->AsArtMethod(); - mirror::ArtMethod* conflict_method = runtime->GetImtConflictMethod(); - if (imt_ref == runtime->GetImtUnimplementedMethod()) { - out_imt->SetReference(imt_index, vtable_method); - } else if (imt_ref != conflict_method) { + auto** imt_ref = &out_imt[imt_index]; + if (*imt_ref == unimplemented_method) { + *imt_ref = vtable_method; + } else if (*imt_ref != conflict_method) { // If we are not a conflict and we have the same signature and name as the imt entry, // it must be that we overwrote a superclass vtable entry. - MethodNameAndSignatureComparator imt_ref_name_comparator( - imt_ref->GetInterfaceMethodIfProxy()); - if (imt_ref_name_comparator.HasSameNameAndSignature( - vtable_method_for_name_comparison)) { - out_imt->SetReference(imt_index, vtable_method); - } else { - out_imt->SetReference(imt_index, conflict_method); - } + MethodNameAndSignatureComparator imt_comparator( + (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size_)); + *imt_ref = imt_comparator.HasSameNameAndSignature(vtable_method_for_name_comparison) ? + vtable_method : conflict_method; } break; } } if (k < 0 && !super_interface) { - mirror::ArtMethod* miranda_method = nullptr; - for (mirror::ArtMethod* mir_method : miranda_list) { - if (interface_name_comparator.HasSameNameAndSignature( - mir_method->GetInterfaceMethodIfProxy())) { + ArtMethod* miranda_method = nullptr; + for (auto& mir_method : miranda_methods) { + if (interface_name_comparator.HasSameNameAndSignature(mir_method)) { miranda_method = mir_method; break; } } if (miranda_method == nullptr) { + miranda_method = reinterpret_cast<ArtMethod*>(allocator.Alloc(method_size)); + CHECK(miranda_method != nullptr); // Point the interface table at a phantom slot. - miranda_method = down_cast<mirror::ArtMethod*>(interface_method->Clone(self)); - if (UNLIKELY(miranda_method == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. - return false; - } - // TODO: If a methods move then the miranda_list may hold stale references. - miranda_list.push_back(miranda_method); + new(miranda_method) ArtMethod(*interface_method, image_pointer_size_); + miranda_methods.push_back(miranda_method); } - method_array->SetWithoutChecks<false>(j, miranda_method); + method_array->SetElementPtrSize(j, miranda_method, image_pointer_size_); } } } } - if (!miranda_list.empty()) { - int old_method_count = klass->NumVirtualMethods(); - int new_method_count = old_method_count + miranda_list.size(); - mirror::ObjectArray<mirror::ArtMethod>* virtuals; - if (old_method_count == 0) { - virtuals = AllocArtMethodArray(self, new_method_count); - } else { - virtuals = klass->GetVirtualMethods()->CopyOf(self, new_method_count); - } + if (!miranda_methods.empty()) { + const size_t old_method_count = klass->NumVirtualMethods(); + const size_t new_method_count = old_method_count + miranda_methods.size(); + // Attempt to realloc to save RAM if possible. + ArtMethod* old_virtuals = klass->GetVirtualMethodsPtr(); + // The Realloced virtual methods aren't visiblef from the class roots, so there is no issue + // where GCs could attempt to mark stale pointers due to memcpy. And since we overwrite the + // realloced memory with out->CopyFrom, we are guaranteed to have objects in the to space since + // CopyFrom has internal read barriers. + auto* virtuals = reinterpret_cast<ArtMethod*>(runtime->GetLinearAlloc()->Realloc( + self, old_virtuals, old_method_count * method_size, new_method_count * method_size)); if (UNLIKELY(virtuals == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. + self->AssertPendingOOMException(); return false; } - klass->SetVirtualMethods(virtuals); - - int old_vtable_count = vtable->GetLength(); - int new_vtable_count = old_vtable_count + miranda_list.size(); - vtable.Assign(vtable->CopyOf(self, new_vtable_count)); + ScopedArenaUnorderedMap<ArtMethod*, ArtMethod*> move_table(allocator.Adapter()); + if (virtuals != old_virtuals) { + // Maps from heap allocated miranda method to linear alloc miranda method. + StrideIterator<ArtMethod> out(reinterpret_cast<uintptr_t>(virtuals), method_size); + // Copy over the old methods + miranda methods. + for (auto& m : klass->GetVirtualMethods(image_pointer_size_)) { + move_table.emplace(&m, &*out); + // The CopyFrom is only necessary to not miss read barriers since Realloc won't do read + // barriers when it copies. + out->CopyFrom(&m, image_pointer_size_); + ++out; + } + } + StrideIterator<ArtMethod> out( + reinterpret_cast<uintptr_t>(virtuals) + old_method_count * method_size, method_size); + // Copy over miranda methods before copying vtable since CopyOf may cause thread suspension and + // we want the roots of the miranda methods to get visited. + for (ArtMethod* mir_method : miranda_methods) { + out->CopyFrom(mir_method, image_pointer_size_); + out->SetAccessFlags(out->GetAccessFlags() | kAccMiranda); + move_table.emplace(mir_method, &*out); + ++out; + } + UpdateClassVirtualMethods(klass.Get(), virtuals, new_method_count); + // Done copying methods, they are all roots in the class now, so we can end the no thread + // suspension assert. + self->EndAssertNoThreadSuspension(old_cause); + + const size_t old_vtable_count = vtable->GetLength(); + const size_t new_vtable_count = old_vtable_count + miranda_methods.size(); + miranda_methods.clear(); + vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, new_vtable_count))); if (UNLIKELY(vtable.Get() == nullptr)) { - CHECK(self->IsExceptionPending()); // OOME. + self->AssertPendingOOMException(); return false; } - for (size_t i = 0; i < miranda_list.size(); ++i) { - mirror::ArtMethod* method = miranda_list[i]; + out = StrideIterator<ArtMethod>( + reinterpret_cast<uintptr_t>(virtuals) + old_method_count * method_size, method_size); + size_t vtable_pos = old_vtable_count; + for (size_t i = old_method_count; i < new_method_count; ++i) { // Leave the declaring class alone as type indices are relative to it - method->SetAccessFlags(method->GetAccessFlags() | kAccMiranda); - method->SetMethodIndex(0xFFFF & (old_vtable_count + i)); - klass->SetVirtualMethod(old_method_count + i, method); - vtable->SetWithoutChecks<false>(old_vtable_count + i, method); + out->SetMethodIndex(0xFFFF & vtable_pos); + vtable->SetElementPtrSize(vtable_pos, &*out, image_pointer_size_); + ++out; + ++vtable_pos; + } + CHECK_EQ(vtable_pos, new_vtable_count); + // Update old vtable methods. + for (size_t i = 0; i < old_vtable_count; ++i) { + auto* m = vtable->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_); + DCHECK(m != nullptr) << PrettyClass(klass.Get()); + auto it = move_table.find(m); + if (it != move_table.end()) { + auto* new_m = it->second; + DCHECK(new_m != nullptr) << PrettyClass(klass.Get()); + vtable->SetElementPtrSize(i, new_m, image_pointer_size_); + } } - // TODO: do not assign to the vtable field until it is fully constructed. klass->SetVTable(vtable.Get()); + // Go fix up all the stale miranda pointers. + for (size_t i = 0; i < ifcount; ++i) { + for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) { + auto* method_array = iftable->GetMethodArray(i); + auto* m = method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_); + DCHECK(m != nullptr) << PrettyClass(klass.Get()); + auto it = move_table.find(m); + if (it != move_table.end()) { + auto* new_m = it->second; + DCHECK(new_m != nullptr) << PrettyClass(klass.Get()); + method_array->SetElementPtrSize(j, new_m, image_pointer_size_); + } + } + } + // Fix up IMT in case it has any miranda methods in it. + for (size_t i = 0; i < mirror::Class::kImtSize; ++i) { + auto it = move_table.find(out_imt[i]); + if (it != move_table.end()) { + out_imt[i] = it->second; + } + } + // Check that there are no stale methods are in the dex cache array. + if (kIsDebugBuild) { + auto* resolved_methods = klass->GetDexCache()->GetResolvedMethods(); + for (size_t i = 0, count = resolved_methods->GetLength(); i < count; ++i) { + auto* m = resolved_methods->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_); + CHECK(move_table.find(m) == move_table.end()) << PrettyMethod(m); + } + } + // Put some random garbage in old virtuals to help find stale pointers. + if (virtuals != old_virtuals) { + memset(old_virtuals, 0xFEu, ArtMethod::ObjectSize(image_pointer_size_) * old_method_count); + } + } else { + self->EndAssertNoThreadSuspension(old_cause); } - if (kIsDebugBuild) { - mirror::ObjectArray<mirror::ArtMethod>* vtable = klass->GetVTableDuringLinking(); - for (int i = 0; i < vtable->GetLength(); ++i) { - CHECK(vtable->GetWithoutChecks(i) != nullptr); + auto* check_vtable = klass->GetVTableDuringLinking(); + for (int i = 0; i < check_vtable->GetLength(); ++i) { + CHECK(check_vtable->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_) != nullptr); } } - return true; } -bool ClassLinker::LinkInstanceFields(Handle<mirror::Class> klass) { +bool ClassLinker::LinkInstanceFields(Thread* self, Handle<mirror::Class> klass) { CHECK(klass.Get() != nullptr); - return LinkFields(klass, false, nullptr); + return LinkFields(self, klass, false, nullptr); } -bool ClassLinker::LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size) { +bool ClassLinker::LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size) { CHECK(klass.Get() != nullptr); - return LinkFields(klass, true, class_size); + return LinkFields(self, klass, true, class_size); } struct LinkFieldsComparator { explicit LinkFieldsComparator() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { } // No thread safety analysis as will be called from STL. Checked lock held in constructor. - bool operator()(mirror::ArtField* field1, mirror::ArtField* field2) + bool operator()(ArtField* field1, ArtField* field2) NO_THREAD_SAFETY_ANALYSIS { - // First come reference fields, then 64-bit, and finally 32-bit + // First come reference fields, then 64-bit, then 32-bit, and then 16-bit, then finally 8-bit. Primitive::Type type1 = field1->GetTypeAsPrimitiveType(); Primitive::Type type2 = field2->GetTypeAsPrimitiveType(); if (type1 != type2) { @@ -5389,17 +5107,16 @@ struct LinkFieldsComparator { } }; -bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static, size_t* class_size) { - size_t num_fields = - is_static ? klass->NumStaticFields() : klass->NumInstanceFields(); - - mirror::ObjectArray<mirror::ArtField>* fields = - is_static ? klass->GetSFields() : klass->GetIFields(); +bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_static, + size_t* class_size) { + self->AllowThreadSuspension(); + const size_t num_fields = is_static ? klass->NumStaticFields() : klass->NumInstanceFields(); + ArtField* const fields = is_static ? klass->GetSFields() : klass->GetIFields(); // Initialize field_offset MemberOffset field_offset(0); if (is_static) { - field_offset = klass->GetFirstReferenceStaticFieldOffsetDuringLinking(); + field_offset = klass->GetFirstReferenceStaticFieldOffsetDuringLinking(image_pointer_size_); } else { mirror::Class* super_class = klass->GetSuperClass(); if (super_class != nullptr) { @@ -5413,11 +5130,11 @@ bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static, size_t // we want a relatively stable order so that adding new fields // minimizes disruption of C++ version such as Class and Method. - std::deque<mirror::ArtField*> grouped_and_sorted_fields; + std::deque<ArtField*> grouped_and_sorted_fields; + const char* old_no_suspend_cause = self->StartAssertNoThreadSuspension( + "Naked ArtField references in deque"); for (size_t i = 0; i < num_fields; i++) { - mirror::ArtField* f = fields->Get(i); - CHECK(f != nullptr) << PrettyClass(klass.Get()); - grouped_and_sorted_fields.push_back(f); + grouped_and_sorted_fields.push_back(&fields[i]); } std::sort(grouped_and_sorted_fields.begin(), grouped_and_sorted_fields.end(), LinkFieldsComparator()); @@ -5425,65 +5142,44 @@ bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static, size_t // References should be at the front. size_t current_field = 0; size_t num_reference_fields = 0; + FieldGaps gaps; + for (; current_field < num_fields; current_field++) { - mirror::ArtField* field = grouped_and_sorted_fields.front(); + ArtField* field = grouped_and_sorted_fields.front(); Primitive::Type type = field->GetTypeAsPrimitiveType(); bool isPrimitive = type != Primitive::kPrimNot; if (isPrimitive) { break; // past last reference, move on to the next phase } + if (UNLIKELY(!IsAligned<sizeof(mirror::HeapReference<mirror::Object>)>( + field_offset.Uint32Value()))) { + MemberOffset old_offset = field_offset; + field_offset = MemberOffset(RoundUp(field_offset.Uint32Value(), 4)); + AddFieldGap(old_offset.Uint32Value(), field_offset.Uint32Value(), &gaps); + } + DCHECK(IsAligned<sizeof(mirror::HeapReference<mirror::Object>)>(field_offset.Uint32Value())); grouped_and_sorted_fields.pop_front(); num_reference_fields++; field->SetOffset(field_offset); field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(mirror::HeapReference<mirror::Object>)); } - - // Now we want to pack all of the double-wide fields together. If - // we're not aligned, though, we want to shuffle one 32-bit field - // into place. If we can't find one, we'll have to pad it. - if (current_field != num_fields && !IsAligned<8>(field_offset.Uint32Value())) { - for (size_t i = 0; i < grouped_and_sorted_fields.size(); i++) { - mirror::ArtField* field = grouped_and_sorted_fields[i]; - Primitive::Type type = field->GetTypeAsPrimitiveType(); - CHECK(type != Primitive::kPrimNot) << PrettyField(field); // should be primitive types - if (type == Primitive::kPrimLong || type == Primitive::kPrimDouble) { - continue; - } - current_field++; - field->SetOffset(field_offset); - // drop the consumed field - grouped_and_sorted_fields.erase(grouped_and_sorted_fields.begin() + i); - break; - } - // whether we found a 32-bit field for padding or not, we advance - field_offset = MemberOffset(field_offset.Uint32Value() + - sizeof(mirror::HeapReference<mirror::Object>)); - } - - // Alignment is good, shuffle any double-wide fields forward, and - // finish assigning field offsets to all fields. - DCHECK(current_field == num_fields || IsAligned<8>(field_offset.Uint32Value())) - << PrettyClass(klass.Get()); - while (!grouped_and_sorted_fields.empty()) { - mirror::ArtField* field = grouped_and_sorted_fields.front(); - grouped_and_sorted_fields.pop_front(); - Primitive::Type type = field->GetTypeAsPrimitiveType(); - CHECK(type != Primitive::kPrimNot) << PrettyField(field); // should be primitive types - field->SetOffset(field_offset); - field_offset = MemberOffset(field_offset.Uint32Value() + - ((type == Primitive::kPrimLong || type == Primitive::kPrimDouble) - ? sizeof(uint64_t) - : sizeof(uint32_t))); - current_field++; - } + // Gaps are stored as a max heap which means that we must shuffle from largest to smallest + // otherwise we could end up with suboptimal gap fills. + ShuffleForward<8>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps); + ShuffleForward<4>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps); + ShuffleForward<2>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps); + ShuffleForward<1>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps); + CHECK(grouped_and_sorted_fields.empty()) << "Missed " << grouped_and_sorted_fields.size() << + " fields."; + self->EndAssertNoThreadSuspension(old_no_suspend_cause); // We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it. if (!is_static && klass->DescriptorEquals("Ljava/lang/ref/Reference;")) { // We know there are no non-reference fields in the Reference classes, and we know // that 'referent' is alphabetically last, so this is easy... CHECK_EQ(num_reference_fields, num_fields) << PrettyClass(klass.Get()); - CHECK_STREQ(fields->Get(num_fields - 1)->GetName(), "referent") << PrettyClass(klass.Get()); + CHECK_STREQ(fields[num_fields - 1].GetName(), "referent") << PrettyClass(klass.Get()); --num_reference_fields; } @@ -5495,18 +5191,14 @@ bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static, size_t } else { klass->SetNumReferenceInstanceFields(num_reference_fields); if (!klass->IsVariableSize()) { - if (klass->DescriptorEquals("Ljava/lang/reflect/ArtMethod;")) { - klass->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*))); - } else { - std::string temp; - DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp); - size_t previous_size = klass->GetObjectSize(); - if (previous_size != 0) { - // Make sure that we didn't originally have an incorrect size. - CHECK_EQ(previous_size, size) << klass->GetDescriptor(&temp); - } - klass->SetObjectSize(size); + std::string temp; + DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp); + size_t previous_size = klass->GetObjectSize(); + if (previous_size != 0) { + // Make sure that we didn't originally have an incorrect size. + CHECK_EQ(previous_size, size) << klass->GetDescriptor(&temp); } + klass->SetObjectSize(size); } } @@ -5514,23 +5206,19 @@ bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static, size_t // Make sure that the fields array is ordered by name but all reference // offsets are at the beginning as far as alignment allows. MemberOffset start_ref_offset = is_static - ? klass->GetFirstReferenceStaticFieldOffsetDuringLinking() + ? klass->GetFirstReferenceStaticFieldOffsetDuringLinking(image_pointer_size_) : klass->GetFirstReferenceInstanceFieldOffset(); MemberOffset end_ref_offset(start_ref_offset.Uint32Value() + num_reference_fields * sizeof(mirror::HeapReference<mirror::Object>)); MemberOffset current_ref_offset = start_ref_offset; for (size_t i = 0; i < num_fields; i++) { - mirror::ArtField* field = fields->Get(i); - if (false) { // enable to debug field layout - LOG(INFO) << "LinkFields: " << (is_static ? "static" : "instance") - << " class=" << PrettyClass(klass.Get()) - << " field=" << PrettyField(field) - << " offset=" - << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset())); - } + ArtField* field = &fields[i]; + VLOG(class_linker) << "LinkFields: " << (is_static ? "static" : "instance") + << " class=" << PrettyClass(klass.Get()) << " field=" << PrettyField(field) << " offset=" + << field->GetOffset(); if (i != 0) { - mirror::ArtField* prev_field = fields->Get(i - 1u); + ArtField* const prev_field = &fields[i - 1]; // NOTE: The field names can be the same. This is not possible in the Java language // but it's valid Java/dex bytecode and for example proguard can generate such bytecode. CHECK_LE(strcmp(prev_field->GetName(), field->GetName()), 0); @@ -5566,52 +5254,29 @@ bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static, size_t void ClassLinker::CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) { uint32_t reference_offsets = 0; mirror::Class* super_class = klass->GetSuperClass(); + // Leave the reference offsets as 0 for mirror::Object (the class field is handled specially). if (super_class != nullptr) { reference_offsets = super_class->GetReferenceInstanceOffsets(); - // If our superclass overflowed, we don't stand a chance. - if (reference_offsets == CLASS_WALK_SUPER) { - klass->SetReferenceInstanceOffsets(reference_offsets); - return; - } - } - CreateReferenceOffsets(klass, false, reference_offsets); -} - -void ClassLinker::CreateReferenceStaticOffsets(Handle<mirror::Class> klass) { - CreateReferenceOffsets(klass, true, 0); -} - -void ClassLinker::CreateReferenceOffsets(Handle<mirror::Class> klass, bool is_static, - uint32_t reference_offsets) { - size_t num_reference_fields = - is_static ? klass->NumReferenceStaticFieldsDuringLinking() - : klass->NumReferenceInstanceFieldsDuringLinking(); - if (num_reference_fields != 0u) { - // All of the fields that contain object references are guaranteed be grouped in memory - // starting at an appropriately aligned address after super class object data for instances - // and after the basic class data for classes. - uint32_t start_offset = - !is_static - ? klass->GetFirstReferenceInstanceFieldOffset().Uint32Value() - // Can't use klass->GetFirstReferenceStaticFieldOffset() yet. - : klass->ShouldHaveEmbeddedImtAndVTable() - ? mirror::Class::ComputeClassSize( - true, klass->GetVTableDuringLinking()->GetLength(), 0, 0, 0) - : sizeof(mirror::Class); - uint32_t start_bit = start_offset / sizeof(mirror::HeapReference<mirror::Object>); - if (start_bit + num_reference_fields > 32) { - reference_offsets = CLASS_WALK_SUPER; - } else { - reference_offsets |= (0xffffffffu >> start_bit) & - (0xffffffffu << (32 - (start_bit + num_reference_fields))); + // Compute reference offsets unless our superclass overflowed. + if (reference_offsets != mirror::Class::kClassWalkSuper) { + size_t num_reference_fields = klass->NumReferenceInstanceFieldsDuringLinking(); + if (num_reference_fields != 0u) { + // All of the fields that contain object references are guaranteed be grouped in memory + // starting at an appropriately aligned address after super class object data. + uint32_t start_offset = RoundUp(super_class->GetObjectSize(), + sizeof(mirror::HeapReference<mirror::Object>)); + uint32_t start_bit = (start_offset - mirror::kObjectHeaderSize) / + sizeof(mirror::HeapReference<mirror::Object>); + if (start_bit + num_reference_fields > 32) { + reference_offsets = mirror::Class::kClassWalkSuper; + } else { + reference_offsets |= (0xffffffffu << start_bit) & + (0xffffffffu >> (32 - (start_bit + num_reference_fields))); + } + } } } - // Update fields in klass - if (is_static) { - klass->SetReferenceStaticOffsets(reference_offsets); - } else { - klass->SetReferenceInstanceOffsets(reference_offsets); - } + klass->SetReferenceInstanceOffsets(reference_offsets); } mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t string_idx, @@ -5655,29 +5320,29 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_i << "Expected pending exception for failed resolution of: " << descriptor; // Convert a ClassNotFoundException to a NoClassDefFoundError. StackHandleScope<1> hs(self); - Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException(nullptr))); + Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException())); if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { DCHECK(resolved == nullptr); // No Handle needed to preserve resolved. self->ClearException(); ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); - self->GetException(nullptr)->SetCause(cause.Get()); + self->GetException()->SetCause(cause.Get()); } } } DCHECK((resolved == nullptr) || resolved->IsResolved() || resolved->IsErroneous()) - << PrettyDescriptor(resolved) << " " << resolved->GetStatus(); + << PrettyDescriptor(resolved) << " " << resolved->GetStatus(); return resolved; } -mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx, - Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, - Handle<mirror::ArtMethod> referrer, - InvokeType type) { +ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + ArtMethod* referrer, InvokeType type) { DCHECK(dex_cache.Get() != nullptr); // Check for hit in the dex cache. - mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); + ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); if (resolved != nullptr && !resolved->IsRuntimeMethod()) { + DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); return resolved; } // Fail, get the declaring class. @@ -5692,18 +5357,20 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t switch (type) { case kDirect: // Fall-through. case kStatic: - resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx); + resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_); + DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); break; case kInterface: - resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx); + resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_); DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); break; case kSuper: // Fall-through. case kVirtual: - resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx); + resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_); break; default: LOG(FATAL) << "Unreachable - invocation type: " << type; + UNREACHABLE(); } if (resolved == nullptr) { // Search by name, which works across dex files. @@ -5712,27 +5379,28 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t switch (type) { case kDirect: // Fall-through. case kStatic: - resolved = klass->FindDirectMethod(name, signature); + resolved = klass->FindDirectMethod(name, signature, image_pointer_size_); + DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); break; case kInterface: - resolved = klass->FindInterfaceMethod(name, signature); + resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); break; case kSuper: // Fall-through. case kVirtual: - resolved = klass->FindVirtualMethod(name, signature); + resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); break; } } // If we found a method, check for incompatible class changes. if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) { // Be a good citizen and update the dex cache to speed subsequent calls. - dex_cache->SetResolvedMethod(method_idx, resolved); + dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_); return resolved; } else { // If we had a method, it's an incompatible-class-change error. if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer.Get()); + ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); } else { // We failed to find the method which means either an access error, an incompatible class // change, or no such method. First try to find the method among direct and virtual methods. @@ -5741,93 +5409,93 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t switch (type) { case kDirect: case kStatic: - resolved = klass->FindVirtualMethod(name, signature); + resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); // Note: kDirect and kStatic are also mutually exclusive, but in that case we would // have had a resolved method before, which triggers the "true" branch above. break; case kInterface: case kVirtual: case kSuper: - resolved = klass->FindDirectMethod(name, signature); + resolved = klass->FindDirectMethod(name, signature, image_pointer_size_); break; } // If we found something, check that it can be accessed by the referrer. - if (resolved != nullptr && referrer.Get() != nullptr) { + bool exception_generated = false; + if (resolved != nullptr && referrer != nullptr) { mirror::Class* methods_class = resolved->GetDeclaringClass(); mirror::Class* referring_class = referrer->GetDeclaringClass(); if (!referring_class->CanAccess(methods_class)) { - ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, - resolved, type); - return nullptr; - } else if (!referring_class->CanAccessMember(methods_class, - resolved->GetAccessFlags())) { + ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, resolved, + type); + exception_generated = true; + } else if (!referring_class->CanAccessMember(methods_class, resolved->GetAccessFlags())) { ThrowIllegalAccessErrorMethod(referring_class, resolved); - return nullptr; + exception_generated = true; } } - - // Otherwise, throw an IncompatibleClassChangeError if we found something, and check interface - // methods and throw if we find the method there. If we find nothing, throw a - // NoSuchMethodError. - switch (type) { - case kDirect: - case kStatic: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer.Get()); - } else { - resolved = klass->FindInterfaceMethod(name, signature); + if (!exception_generated) { + // Otherwise, throw an IncompatibleClassChangeError if we found something, and check + // interface methods and throw if we find the method there. If we find nothing, throw a + // NoSuchMethodError. + switch (type) { + case kDirect: + case kStatic: if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer.Get()); + ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer); } else { - ThrowNoSuchMethodError(type, klass, name, signature); + resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer); + } else { + ThrowNoSuchMethodError(type, klass, name, signature); + } } - } - break; - case kInterface: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); - } else { - resolved = klass->FindVirtualMethod(name, signature); + break; + case kInterface: if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer.Get()); + ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer); } else { - ThrowNoSuchMethodError(type, klass, name, signature); + resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer); + } else { + ThrowNoSuchMethodError(type, klass, name, signature); + } } - } - break; - case kSuper: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); - } else { - ThrowNoSuchMethodError(type, klass, name, signature); - } - break; - case kVirtual: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); - } else { - resolved = klass->FindInterfaceMethod(name, signature); + break; + case kSuper: if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer.Get()); + ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer); } else { ThrowNoSuchMethodError(type, klass, name, signature); } - } - break; + break; + case kVirtual: + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer); + } else { + resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer); + } else { + ThrowNoSuchMethodError(type, klass, name, signature); + } + } + break; + } } } - DCHECK(Thread::Current()->IsExceptionPending()); + Thread::Current()->AssertPendingException(); return nullptr; } } -mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t field_idx, - Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, - bool is_static) { +ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t field_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, bool is_static) { DCHECK(dex_cache.Get() != nullptr); - mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); + ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_); if (resolved != nullptr) { return resolved; } @@ -5860,16 +5528,15 @@ mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t fi return nullptr; } } - dex_cache->SetResolvedField(field_idx, resolved); + dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_); return resolved; } -mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, - uint32_t field_idx, - Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader) { +ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) { DCHECK(dex_cache.Get() != nullptr); - mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); + ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_); if (resolved != nullptr) { return resolved; } @@ -5888,14 +5555,14 @@ mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_)); resolved = mirror::Class::FindField(self, klass, name, type); if (resolved != nullptr) { - dex_cache->SetResolvedField(field_idx, resolved); + dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_); } else { ThrowNoSuchFieldError("", klass.Get(), type, name); } return resolved; } -const char* ClassLinker::MethodShorty(uint32_t method_idx, mirror::ArtMethod* referrer, +const char* ClassLinker::MethodShorty(uint32_t method_idx, ArtMethod* referrer, uint32_t* length) { mirror::Class* declaring_class = referrer->GetDeclaringClass(); mirror::DexCache* dex_cache = declaring_class->GetDexCache(); @@ -5923,11 +5590,59 @@ void ClassLinker::DumpAllClasses(int flags) { } } +static OatFile::OatMethod CreateOatMethod(const void* code) { + CHECK(code != nullptr); + const uint8_t* base = reinterpret_cast<const uint8_t*>(code); // Base of data points at code. + base -= sizeof(void*); // Move backward so that code_offset != 0. + const uint32_t code_offset = sizeof(void*); + return OatFile::OatMethod(base, code_offset); +} + +bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const { + return (entry_point == GetQuickResolutionStub()) || + (quick_resolution_trampoline_ == entry_point); +} + +bool ClassLinker::IsQuickToInterpreterBridge(const void* entry_point) const { + return (entry_point == GetQuickToInterpreterBridge()) || + (quick_to_interpreter_bridge_trampoline_ == entry_point); +} + +bool ClassLinker::IsQuickGenericJniStub(const void* entry_point) const { + return (entry_point == GetQuickGenericJniStub()) || + (quick_generic_jni_trampoline_ == entry_point); +} + +const void* ClassLinker::GetRuntimeQuickGenericJniStub() const { + return GetQuickGenericJniStub(); +} + +void ClassLinker::SetEntryPointsToCompiledCode(ArtMethod* method, + const void* method_code) const { + OatFile::OatMethod oat_method = CreateOatMethod(method_code); + oat_method.LinkMethod(method); + method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); +} + +void ClassLinker::SetEntryPointsToInterpreter(ArtMethod* method) const { + if (!method->IsNative()) { + method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge); + method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + } else { + const void* quick_method_code = GetQuickGenericJniStub(); + OatFile::OatMethod oat_method = CreateOatMethod(quick_method_code); + oat_method.LinkMethod(method); + method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); + } +} + void ClassLinker::DumpForSigQuit(std::ostream& os) { + Thread* self = Thread::Current(); if (dex_cache_image_class_lookup_required_) { + ScopedObjectAccess soa(self); MoveImageClassesToClassTable(); } - ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); os << "Zygote loaded classes=" << pre_zygote_class_table_.Size() << " post zygote classes=" << class_table_.Size() << "\n"; } @@ -5961,6 +5676,54 @@ void ClassLinker::SetClassRoot(ClassRoot class_root, mirror::Class* klass) { class_roots->Set<false>(class_root, klass); } +const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { + static const char* class_roots_descriptors[] = { + "Ljava/lang/Class;", + "Ljava/lang/Object;", + "[Ljava/lang/Class;", + "[Ljava/lang/Object;", + "Ljava/lang/String;", + "Ljava/lang/DexCache;", + "Ljava/lang/ref/Reference;", + "Ljava/lang/reflect/Constructor;", + "Ljava/lang/reflect/Field;", + "Ljava/lang/reflect/Method;", + "Ljava/lang/reflect/Proxy;", + "[Ljava/lang/String;", + "[Ljava/lang/reflect/Constructor;", + "[Ljava/lang/reflect/Field;", + "[Ljava/lang/reflect/Method;", + "Ljava/lang/ClassLoader;", + "Ljava/lang/Throwable;", + "Ljava/lang/ClassNotFoundException;", + "Ljava/lang/StackTraceElement;", + "Z", + "B", + "C", + "D", + "F", + "I", + "J", + "S", + "V", + "[Z", + "[B", + "[C", + "[D", + "[F", + "[I", + "[J", + "[S", + "[Ljava/lang/StackTraceElement;", + }; + static_assert(arraysize(class_roots_descriptors) == size_t(kClassRootsMax), + "Mismatch between class descriptors and class-root enum"); + + const char* descriptor = class_roots_descriptors[class_root]; + CHECK(descriptor != nullptr); + return descriptor; +} + std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& root) const { std::string temp; @@ -5968,7 +5731,7 @@ std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirr } bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a, - const GcRoot<mirror::Class>& b) { + const GcRoot<mirror::Class>& b) const { if (a.Read()->GetClassLoader() != b.Read()->GetClassLoader()) { return false; } @@ -5982,7 +5745,7 @@ std::size_t ClassLinker::ClassDescriptorHashEquals::operator()( } bool ClassLinker::ClassDescriptorHashEquals::operator()( - const GcRoot<mirror::Class>& a, const std::pair<const char*, mirror::ClassLoader*>& b) { + const GcRoot<mirror::Class>& a, const std::pair<const char*, mirror::ClassLoader*>& b) const { if (a.Read()->GetClassLoader() != b.second) { return false; } @@ -5990,7 +5753,7 @@ bool ClassLinker::ClassDescriptorHashEquals::operator()( } bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a, - const char* descriptor) { + const char* descriptor) const { return a.Read()->DescriptorEquals(descriptor); } @@ -5998,7 +5761,11 @@ std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const char* descr return ComputeModifiedUtf8Hash(descriptor); } -bool ClassLinker::MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m) { +bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) { + if (Runtime::Current()->UseJit()) { + // JIT can have direct code pointers from any method to any other method. + return true; + } // Non-image methods don't use direct code pointer. if (!m->GetDeclaringClass()->IsBootStrapClassLoaded()) { return false; @@ -6006,9 +5773,8 @@ bool ClassLinker::MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m) { if (m->IsPrivate()) { // The method can only be called inside its own oat file. Therefore it won't be called using // its direct code if the oat file has been compiled in PIC mode. - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); const DexFile& dex_file = m->GetDeclaringClass()->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = class_linker->FindOpenedOatDexFileForDexFile(dex_file); + const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr) { // No oat file: the method has not been compiled. return false; @@ -6028,4 +5794,106 @@ bool ClassLinker::MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m) { } } +jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) { + // SOAAlreadyRunnable is protected, and we need something to add a global reference. + // We could move the jobject to the callers, but all call-sites do this... + ScopedObjectAccessUnchecked soa(self); + + // Register the dex files. + for (const DexFile* dex_file : dex_files) { + RegisterDexFile(*dex_file); + } + + // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex. + StackHandleScope<10> hs(self); + + ArtField* dex_elements_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements); + + mirror::Class* dex_elements_class = dex_elements_field->GetType<true>(); + DCHECK(dex_elements_class != nullptr); + DCHECK(dex_elements_class->IsArrayClass()); + Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements(hs.NewHandle( + mirror::ObjectArray<mirror::Object>::Alloc(self, dex_elements_class, dex_files.size()))); + Handle<mirror::Class> h_dex_element_class = + hs.NewHandle(dex_elements_class->GetComponentType()); + + ArtField* element_file_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + DCHECK_EQ(h_dex_element_class.Get(), element_file_field->GetDeclaringClass()); + + ArtField* cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie); + DCHECK_EQ(cookie_field->GetDeclaringClass(), element_file_field->GetType<false>()); + + // Fill the elements array. + int32_t index = 0; + for (const DexFile* dex_file : dex_files) { + StackHandleScope<3> hs2(self); + + Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(self, 1)); + DCHECK(h_long_array.Get() != nullptr); + h_long_array->Set(0, reinterpret_cast<intptr_t>(dex_file)); + + Handle<mirror::Object> h_dex_file = hs2.NewHandle( + cookie_field->GetDeclaringClass()->AllocObject(self)); + DCHECK(h_dex_file.Get() != nullptr); + cookie_field->SetObject<false>(h_dex_file.Get(), h_long_array.Get()); + + Handle<mirror::Object> h_element = hs2.NewHandle(h_dex_element_class->AllocObject(self)); + DCHECK(h_element.Get() != nullptr); + element_file_field->SetObject<false>(h_element.Get(), h_dex_file.Get()); + + h_dex_elements->Set(index, h_element.Get()); + index++; + } + DCHECK_EQ(index, h_dex_elements->GetLength()); + + // Create DexPathList. + Handle<mirror::Object> h_dex_path_list = hs.NewHandle( + dex_elements_field->GetDeclaringClass()->AllocObject(self)); + DCHECK(h_dex_path_list.Get() != nullptr); + // Set elements. + dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get()); + + // Create PathClassLoader. + Handle<mirror::Class> h_path_class_class = hs.NewHandle( + soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)); + Handle<mirror::Object> h_path_class_loader = hs.NewHandle( + h_path_class_class->AllocObject(self)); + DCHECK(h_path_class_loader.Get() != nullptr); + // Set DexPathList. + ArtField* path_list_field = + soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList); + DCHECK(path_list_field != nullptr); + path_list_field->SetObject<false>(h_path_class_loader.Get(), h_dex_path_list.Get()); + + // Make a pretend boot-classpath. + // TODO: Should we scan the image? + ArtField* const parent_field = + mirror::Class::FindField(self, hs.NewHandle(h_path_class_loader->GetClass()), "parent", + "Ljava/lang/ClassLoader;"); + DCHECK(parent_field!= nullptr); + mirror::Object* boot_cl = + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self); + parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl); + + // Make it a global ref and return. + ScopedLocalRef<jobject> local_ref( + soa.Env(), soa.Env()->AddLocalReference<jobject>(h_path_class_loader.Get())); + return soa.Env()->NewGlobalRef(local_ref.get()); +} + +ArtMethod* ClassLinker::CreateRuntimeMethod() { + ArtMethod* method = AllocArtMethodArray(Thread::Current(), 1); + CHECK(method != nullptr); + method->SetDexMethodIndex(DexFile::kDexNoIndex); + CHECK(method->IsRuntimeMethod()); + return method; +} + +void ClassLinker::DropFindArrayClassCache() { + std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr)); + find_array_class_cache_next_victim_ = 0; +} + } // namespace art |