| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "class_linker.h" |
| |
| #include <deque> |
| #include <iostream> |
| #include <memory> |
| #include <queue> |
| #include <string> |
| #include <unistd.h> |
| #include <utility> |
| #include <vector> |
| |
| #include "art_field-inl.h" |
| #include "base/casts.h" |
| #include "base/logging.h" |
| #include "base/scoped_flock.h" |
| #include "base/stl_util.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" |
| #include "gc/heap.h" |
| #include "gc/space/image_space.h" |
| #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 "linear_alloc.h" |
| #include "oat.h" |
| #include "oat_file.h" |
| #include "oat_file_assistant.h" |
| #include "object_lock.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" |
| #include "mirror/reference-inl.h" |
| #include "mirror/stack_trace_element.h" |
| #include "mirror/string-inl.h" |
| #include "os.h" |
| #include "runtime.h" |
| #include "entrypoints/entrypoint_utils.h" |
| #include "ScopedLocalRef.h" |
| #include "scoped_thread_state_change.h" |
| #include "handle_scope-inl.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_); |
| static void ThrowNoClassDefFoundError(const char* fmt, ...) { |
| va_list args; |
| va_start(args, fmt); |
| Thread* self = Thread::Current(); |
| self->ThrowNewExceptionV("Ljava/lang/NoClassDefFoundError;", fmt, args); |
| va_end(args); |
| } |
| |
| static bool HasInitWithString(Thread* self, ClassLinker* class_linker, const char* descriptor) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| mirror::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; |
| } |
| |
| mirror::ArtMethod* exception_init_method = |
| exception_class->FindDeclaredDirectMethod("<init>", "(Ljava/lang/String;)V"); |
| 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. |
| 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(); |
| 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 { |
| 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 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(); |
| |
| ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred()); |
| CHECK(cause.get() != nullptr); |
| |
| env->ExceptionClear(); |
| bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error); |
| env->Throw(cause.get()); |
| |
| // We only wrap non-Error exceptions; an Error can just be used as-is. |
| if (!is_error) { |
| 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. |
| : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel), |
| dex_cache_image_class_lookup_required_(false), |
| failed_dex_cache_class_lookups_(0), |
| class_roots_(nullptr), |
| array_iftable_(nullptr), |
| find_array_class_cache_next_victim_(0), |
| init_done_(false), |
| log_new_dex_caches_roots_(false), |
| log_new_class_table_roots_(false), |
| intern_table_(intern_table), |
| quick_resolution_trampoline_(nullptr), |
| quick_imt_conflict_trampoline_(nullptr), |
| quick_generic_jni_trampoline_(nullptr), |
| quick_to_interpreter_bridge_trampoline_(nullptr), |
| image_pointer_size_(sizeof(void*)) { |
| CHECK(intern_table_ != nullptr); |
| for (size_t i = 0; i < kFindArrayCacheSize; ++i) { |
| find_array_class_cache_[i] = GcRoot<mirror::Class>(nullptr); |
| } |
| } |
| |
| 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."; |
| |
| CHECK(!init_done_); |
| |
| // java_lang_Class comes first, it's needed for AllocClass |
| Thread* const self = Thread::Current(); |
| gc::Heap* const 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. |
| Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>( |
| heap->AllocNonMovableObject<true>(self, nullptr, |
| mirror::Class::ClassClassSize(), |
| 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->SetPrimitiveType(Primitive::kPrimNot); |
| heap->DecrementDisableMovingGC(self); |
| // AllocClass(mirror::Class*) can now be used |
| |
| // Class[] is used for reflection support. |
| Handle<mirror::Class> class_array_class(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray<mirror::Class>::ClassSize()))); |
| 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()))); |
| CHECK(java_lang_Object.Get() != nullptr); |
| // backfill Object as the super class of Class. |
| java_lang_Class->SetSuperClass(java_lang_Object.Get()); |
| 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()))); |
| 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()))); |
| // 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()))); |
| 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()))); |
| mirror::String::SetClass(java_lang_String.Get()); |
| mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusResolved, self); |
| java_lang_String->SetStringClass(); |
| |
| // Setup java.lang.ref.Reference. |
| Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle( |
| AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize()))); |
| mirror::Reference::SetClass(java_lang_ref_Reference.Get()); |
| java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize()); |
| 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>>( |
| mirror::ObjectArray<mirror::Class>::Alloc(self, object_array_class.Get(), |
| kClassRootsMax)); |
| CHECK(!class_roots_.IsNull()); |
| SetClassRoot(kJavaLangClass, java_lang_Class.Get()); |
| SetClassRoot(kJavaLangObject, java_lang_Object.Get()); |
| SetClassRoot(kClassArrayClass, class_array_class.Get()); |
| SetClassRoot(kObjectArrayClass, object_array_class.Get()); |
| SetClassRoot(kCharArrayClass, char_array_class.Get()); |
| SetClassRoot(kJavaLangString, java_lang_String.Get()); |
| SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference.Get()); |
| |
| // Setup the primitive type classes. |
| SetClassRoot(kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean)); |
| SetClassRoot(kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte)); |
| SetClassRoot(kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort)); |
| SetClassRoot(kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt)); |
| SetClassRoot(kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong)); |
| SetClassRoot(kPrimitiveFloat, CreatePrimitiveClass(self, Primitive::kPrimFloat)); |
| SetClassRoot(kPrimitiveDouble, CreatePrimitiveClass(self, Primitive::kPrimDouble)); |
| SetClassRoot(kPrimitiveVoid, CreatePrimitiveClass(self, Primitive::kPrimVoid)); |
| |
| // Create array interface entries to populate once we can load system classes. |
| array_iftable_ = GcRoot<mirror::IfTable>(AllocIfTable(self, 2)); |
| |
| // 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()))); |
| 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()))); |
| 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()))); |
| SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get()); |
| java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize()); |
| mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusResolved, self); |
| |
| // Constructor, Method, and AbstractMethod are necessary so |
| // that FindClass can link members. |
| |
| 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); |
| size_t pointer_size = GetInstructionSetPointerSize(Runtime::Current()->GetInstructionSet()); |
| java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(pointer_size)); |
| SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get()); |
| mirror::Class::SetStatus(java_lang_reflect_ArtMethod, mirror::Class::kStatusResolved, self); |
| mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get()); |
| |
| // 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()))); |
| 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()); |
| |
| // 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 (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 |
| |
| // run char class through InitializePrimitiveClass to finish init |
| 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_ = GetQuickGenericJniStub(); |
| if (!runtime->IsAotCompiler()) { |
| // We need to set up the generic trampolines since we don't have an image. |
| 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 |
| 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()); |
| mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusNotReady, self); |
| mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;"); |
| 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. |
| SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z")); |
| mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); |
| |
| SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B")); |
| mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); |
| |
| CHECK_EQ(char_array_class.Get(), FindSystemClass(self, "[C")); |
| |
| SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S")); |
| mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); |
| |
| CHECK_EQ(int_array_class.Get(), FindSystemClass(self, "[I")); |
| |
| CHECK_EQ(long_array_class.Get(), FindSystemClass(self, "[J")); |
| |
| SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F")); |
| mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); |
| |
| SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D")); |
| mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); |
| |
| CHECK_EQ(class_array_class.Get(), FindSystemClass(self, "[Ljava/lang/Class;")); |
| |
| CHECK_EQ(object_array_class.Get(), FindSystemClass(self, "[Ljava/lang/Object;")); |
| |
| // Setup the single, global copy of "iftable". |
| 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. |
| 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_. |
| CHECK_EQ(java_lang_Class.Get(), FindSystemClass(self, "Ljava/lang/Class;")); |
| |
| mirror::Class::SetStatus(java_lang_reflect_ArtMethod, mirror::Class::kStatusNotReady, self); |
| CHECK_EQ(java_lang_reflect_ArtMethod.Get(), |
| FindSystemClass(self, "Ljava/lang/reflect/ArtMethod;")); |
| CHECK_EQ(object_array_string.Get(), |
| FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass))); |
| CHECK_EQ(object_array_art_method.Get(), |
| FindSystemClass(self, GetClassRootDescriptor(kJavaLangReflectArtMethodArrayClass))); |
| |
| // End of special init trickery, subsequent classes may be loaded via FindSystemClass. |
| |
| // Create java.lang.reflect.Proxy root. |
| 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 |
| 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()); |
| 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_. |
| 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. |
| SetClassRoot(kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;")); |
| mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); |
| SetClassRoot(kJavaLangClassNotFoundException, |
| FindSystemClass(self, "Ljava/lang/ClassNotFoundException;")); |
| SetClassRoot(kJavaLangStackTraceElement, FindSystemClass(self, "Ljava/lang/StackTraceElement;")); |
| SetClassRoot(kJavaLangStackTraceElementArrayClass, |
| FindSystemClass(self, "[Ljava/lang/StackTraceElement;")); |
| mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); |
| |
| // Ensure void type is resolved in the core's dex cache so java.lang.Void is correctly |
| // initialized. |
| { |
| const DexFile& dex_file = java_lang_Object->GetDexFile(); |
| const DexFile::StringId* void_string_id = dex_file.FindStringId("V"); |
| CHECK(void_string_id != nullptr); |
| uint32_t void_string_index = dex_file.GetIndexForStringId(*void_string_id); |
| const DexFile::TypeId* void_type_id = dex_file.FindTypeId(void_string_index); |
| CHECK(void_type_id != nullptr); |
| uint16_t void_type_idx = dex_file.GetIndexForTypeId(*void_type_id); |
| // Now we resolve void type so the dex cache contains it. We use java.lang.Object class |
| // as referrer so the used dex cache is core's one. |
| mirror::Class* resolved_type = ResolveType(dex_file, void_type_idx, java_lang_Object.Get()); |
| CHECK_EQ(resolved_type, GetClassRoot(kPrimitiveVoid)); |
| self->AssertNoPendingException(); |
| } |
| |
| FinishInit(self); |
| |
| VLOG(startup) << "ClassLinker::InitFromCompiler exiting"; |
| } |
| |
| void ClassLinker::FinishInit(Thread* self) { |
| VLOG(startup) << "ClassLinker::FinishInit entering"; |
| |
| // Let the heap know some key offsets into java.lang.ref instances |
| // Note: we hard code the field indexes here rather than using FindInstanceField |
| // as the types of the field can't be resolved prior to the runtime being |
| // fully initialized |
| mirror::Class* java_lang_ref_Reference = GetClassRoot(kJavaLangRefReference); |
| mirror::Class* java_lang_ref_FinalizerReference = |
| FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); |
| |
| ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0); |
| CHECK_STREQ(pendingNext->GetName(), "pendingNext"); |
| CHECK_STREQ(pendingNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); |
| |
| ArtField* queue = java_lang_ref_Reference->GetInstanceField(1); |
| CHECK_STREQ(queue->GetName(), "queue"); |
| CHECK_STREQ(queue->GetTypeDescriptor(), "Ljava/lang/ref/ReferenceQueue;"); |
| |
| ArtField* queueNext = java_lang_ref_Reference->GetInstanceField(2); |
| CHECK_STREQ(queueNext->GetName(), "queueNext"); |
| CHECK_STREQ(queueNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); |
| |
| ArtField* referent = java_lang_ref_Reference->GetInstanceField(3); |
| CHECK_STREQ(referent->GetName(), "referent"); |
| CHECK_STREQ(referent->GetTypeDescriptor(), "Ljava/lang/Object;"); |
| |
| ArtField* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2); |
| CHECK_STREQ(zombie->GetName(), "zombie"); |
| CHECK_STREQ(zombie->GetTypeDescriptor(), "Ljava/lang/Object;"); |
| |
| // ensure all class_roots_ are initialized |
| for (size_t i = 0; i < kClassRootsMax; i++) { |
| ClassRoot class_root = static_cast<ClassRoot>(i); |
| mirror::Class* klass = GetClassRoot(class_root); |
| CHECK(klass != nullptr); |
| DCHECK(klass->IsArrayClass() || klass->IsPrimitive() || klass->GetDexCache() != nullptr); |
| // note SetClassRoot does additional validation. |
| // if possible add new checks there to catch errors early |
| } |
| |
| CHECK(!array_iftable_.IsNull()); |
| |
| // disable the slow paths in FindClass and CreatePrimitiveClass now |
| // that Object, Class, and Object[] are setup |
| init_done_ = true; |
| |
| VLOG(startup) << "ClassLinker::FinishInit exiting"; |
| } |
| |
| void ClassLinker::RunRootClinits() { |
| Thread* self = Thread::Current(); |
| for (size_t i = 0; i < ClassLinker::kClassRootsMax; ++i) { |
| mirror::Class* c = GetClassRoot(ClassRoot(i)); |
| if (!c->IsArrayClass() && !c->IsPrimitive()) { |
| StackHandleScope<1> hs(self); |
| Handle<mirror::Class> h_class(hs.NewHandle(GetClassRoot(ClassRoot(i)))); |
| EnsureInitialized(self, h_class, true, true); |
| self->AssertNoPendingException(); |
| } |
| } |
| } |
| |
| const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) { |
| WriterMutexLock mu(Thread::Current(), dex_lock_); |
| if (kIsDebugBuild) { |
| for (size_t i = 0; i < oat_files_.size(); ++i) { |
| CHECK_NE(oat_file, oat_files_[i]) << oat_file->GetLocation(); |
| } |
| } |
| VLOG(class_linker) << "Registering " << oat_file->GetLocation(); |
| oat_files_.push_back(oat_file); |
| return oat_file; |
| } |
| |
| OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) { |
| VLOG(startup) << "ClassLinker::GetImageOatFile entering"; |
| OatFile* oat_file = space->ReleaseOatFile(); |
| CHECK_EQ(RegisterOatFile(oat_file), oat_file); |
| VLOG(startup) << "ClassLinker::GetImageOatFile exiting"; |
| return *oat_file; |
| } |
| |
| 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) {} |
| |
| DexFileAndClassPair(const DexFileAndClassPair&) = default; |
| |
| 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; |
| } |
| |
| const char* GetCachedDescriptor() const { |
| return cached_descriptor_; |
| } |
| |
| 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; |
| } |
| return dex_file_ < rhs.dex_file_; |
| } |
| |
| bool DexFileHasMoreClasses() const { |
| return current_class_index_ + 1 < dex_file_->NumClassDefs(); |
| } |
| |
| DexFileAndClassPair GetNext() const { |
| return DexFileAndClassPair(dex_file_, current_class_index_ + 1, from_loaded_oat_); |
| } |
| |
| size_t GetCurrentClassIndex() const { |
| return current_class_index_; |
| } |
| |
| bool FromLoadedOat() const { |
| return from_loaded_oat_; |
| } |
| |
| const DexFile* GetDexFile() const { |
| return dex_file_; |
| } |
| |
| void DeleteDexFile() { |
| delete dex_file_; |
| dex_file_ = nullptr; |
| } |
| |
| 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). |
| }; |
| |
| 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); |
| } |
| } |
| } |
| } |
| |
| static void AddNext(DexFileAndClassPair* original, |
| std::priority_queue<DexFileAndClassPair>* heap) { |
| if (original->DexFileHasMoreClasses()) { |
| heap->push(original->GetNext()); |
| } else { |
| // Need to delete the dex file. |
| original->DeleteDexFile(); |
| } |
| } |
| |
| static void FreeDexFilesInHeap(std::priority_queue<DexFileAndClassPair>* heap) { |
| while (!heap->empty()) { |
| delete heap->top().GetDexFile(); |
| heap->pop(); |
| } |
| } |
| |
| const OatFile* ClassLinker::GetBootOatFile() { |
| // To grab the boot oat, look at the dex files in the boot classpath. Any of those is fine, as |
| // they were all compiled into the same oat file. So grab the first one, which is guaranteed to |
| // exist if the boot class-path isn't empty. |
| if (boot_class_path_.empty()) { |
| return nullptr; |
| } |
| const DexFile* boot_dex_file = boot_class_path_[0]; |
| // Is it from an oat file? |
| if (boot_dex_file->GetOatDexFile() != nullptr) { |
| return boot_dex_file->GetOatDexFile()->GetOatFile(); |
| } |
| return nullptr; |
| } |
| |
| 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 nullptr; |
| } |
| |
| // 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; |
| } |
| |
| // 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_); |
| |
| 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); |
| } |
| } |
| |
| if (queue.empty()) { |
| // No other oat files, return early. |
| 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; |
| } |
| } |
| AddNext(&compare_pop, &queue); |
| } |
| |
| return false; |
| } |
| |
| 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; |
| } |
| |
| // 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; |
| } |
| } |
| } |
| |
| // 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; |
| } |
| |
| // 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 (accept_oat_file) { |
| source_oat_file = oat_file.release(); |
| RegisterOatFile(source_oat_file); |
| } |
| } |
| } |
| |
| 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()); |
| } |
| } |
| |
| // 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 (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."); |
| } |
| } |
| return dex_files; |
| } |
| |
| 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; |
| } |
| |
| void ClassLinker::InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) { |
| 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()) { |
| const size_t pointer_size = class_linker->image_pointer_size_; |
| method->SetEntryPointFromInterpreterPtrSize(artInterpreterToInterpreterBridge, pointer_size); |
| if (!method->IsRuntimeMethod() && method != Runtime::Current()->GetResolutionMethod()) { |
| method->SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), |
| pointer_size); |
| } |
| } |
| } |
| } |
| |
| void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| DCHECK(obj != nullptr); |
| CHECK(obj->GetClass() != nullptr) << "Null class " << 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); |
| } |
| } |
| } |
| } |
| |
| void ClassLinker::InitFromImage() { |
| VLOG(startup) << "ClassLinker::InitFromImage entering"; |
| CHECK(!init_done_); |
| |
| Runtime* const runtime = Runtime::Current(); |
| Thread* const self = Thread::Current(); |
| gc::Heap* const heap = runtime->GetHeap(); |
| gc::space::ImageSpace* const space = heap->GetImageSpace(); |
| dex_cache_image_class_lookup_required_ = true; |
| CHECK(space != nullptr); |
| 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); |
| quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline(); |
| 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(); |
| mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); |
| mirror::ObjectArray<mirror::DexCache>* dex_caches = |
| dex_caches_object->AsObjectArray<mirror::DexCache>(); |
| |
| StackHandleScope<1> hs(self); |
| Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle( |
| space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)-> |
| AsObjectArray<mirror::Class>())); |
| class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get()); |
| |
| // Special case of setting up the String class early so that we can test arbitrary objects |
| // as being Strings or not |
| mirror::String::SetClass(GetClassRoot(kJavaLangString)); |
| |
| 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> 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; |
| 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(); |
| } |
| |
| CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum()); |
| |
| AppendToBootClassPath(*dex_file.get(), dex_cache); |
| opened_dex_files_.push_back(std::move(dex_file)); |
| } |
| |
| // 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->IsAotCompiler()) { |
| // 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(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; |
| } |
| |
| // Set entry point to interpreter if in InterpretOnly mode. |
| if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) { |
| heap->VisitObjects(InitFromImageInterpretOnlyCallback, this); |
| } |
| 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); |
| } |
| |
| // reinit class_roots_ |
| mirror::Class::SetClassClass(class_roots->Get(kJavaLangClass)); |
| class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get()); |
| |
| // reinit array_iftable_ from any array class instance, they should be == |
| 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::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); |
| mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); |
| mirror::CharArray::SetArrayClass(GetClassRoot(kCharArrayClass)); |
| mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); |
| mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); |
| mirror::IntArray::SetArrayClass(GetClassRoot(kIntArrayClass)); |
| mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass)); |
| mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); |
| mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); |
| mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); |
| |
| FinishInit(self); |
| |
| VLOG(startup) << "ClassLinker::InitFromImage exiting"; |
| } |
| |
| 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) { |
| for (GcRoot<mirror::Class>& root : class_table_) { |
| buffered_visitor.VisitRoot(root); |
| root.Read()->VisitFieldRoots(buffered_visitor); |
| } |
| // PreZygote classes can't move so we won't need to update fields' declaring classes. |
| for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) { |
| buffered_visitor.VisitRoot(root); |
| root.Read()->VisitFieldRoots(buffered_visitor); |
| } |
| } else if ((flags & kVisitRootFlagNewRoots) != 0) { |
| for (auto& root : new_class_roots_) { |
| mirror::Class* old_ref = root.Read<kWithoutReadBarrier>(); |
| old_ref->VisitFieldRoots(buffered_visitor); |
| 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 |
| // corresponding object. This is slow, but luckily for us, this may only happen with a |
| // concurrent moving GC. |
| auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref)); |
| DCHECK(it != class_table_.end()); |
| *it = GcRoot<mirror::Class>(new_ref); |
| } |
| } |
| } |
| buffered_visitor.Flush(); // Flush before clearing new_class_roots_. |
| if ((flags & kVisitRootFlagClearRootLog) != 0) { |
| new_class_roots_.clear(); |
| } |
| if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) { |
| log_new_class_table_roots_ = true; |
| } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) { |
| log_new_class_table_roots_ = false; |
| } |
| // We deliberately ignore the class roots in the image since we |
| // handle image roots by using the MS/CMS rescanning of dirty cards. |
| } |
| |
| // 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(RootVisitor* visitor, VisitRootFlags flags) { |
| class_roots_.VisitRoot(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(visitor, RootInfo(kRootVMInternal)); |
| } |
| } else if ((flags & kVisitRootFlagNewRoots) != 0) { |
| for (size_t index : new_dex_cache_roots_) { |
| dex_caches_[index].VisitRoot(visitor, RootInfo(kRootVMInternal)); |
| } |
| } |
| if ((flags & kVisitRootFlagClearRootLog) != 0) { |
| new_dex_cache_roots_.clear(); |
| } |
| if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) { |
| log_new_dex_caches_roots_ = true; |
| } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) { |
| log_new_dex_caches_roots_ = false; |
| } |
| } |
| VisitClassRoots(visitor, flags); |
| array_iftable_.VisitRoot(visitor, RootInfo(kRootVMInternal)); |
| for (size_t i = 0; i < kFindArrayCacheSize; ++i) { |
| find_array_class_cache_[i].VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); |
| } |
| } |
| |
| void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) { |
| if (dex_cache_image_class_lookup_required_) { |
| MoveImageClassesToClassTable(); |
| } |
| // TODO: why isn't this a ReaderMutexLock? |
| WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); |
| for (GcRoot<mirror::Class>& root : class_table_) { |
| if (!visitor(root.Read(), arg)) { |
| return; |
| } |
| } |
| for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) { |
| if (!visitor(root.Read(), arg)) { |
| return; |
| } |
| } |
| } |
| |
| static bool GetClassesVisitorSet(mirror::Class* c, void* arg) { |
| std::set<mirror::Class*>* classes = reinterpret_cast<std::set<mirror::Class*>*>(arg); |
| classes->insert(c); |
| return true; |
| } |
| |
| struct GetClassesVisitorArrayArg { |
| Handle<mirror::ObjectArray<mirror::Class>>* classes; |
| int32_t index; |
| bool success; |
| }; |
| |
| static bool GetClassesVisitorArray(mirror::Class* c, void* varg) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| GetClassesVisitorArrayArg* arg = reinterpret_cast<GetClassesVisitorArrayArg*>(varg); |
| if (arg->index < (*arg->classes)->GetLength()) { |
| (*arg->classes)->Set(arg->index, c); |
| arg->index++; |
| return true; |
| } else { |
| arg->success = false; |
| return false; |
| } |
| } |
| |
| void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) { |
| // TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem |
| // is avoiding duplicates. |
| if (!kMovingClasses) { |
| std::set<mirror::Class*> classes; |
| VisitClasses(GetClassesVisitorSet, &classes); |
| for (mirror::Class* klass : classes) { |
| if (!visitor(klass, arg)) { |
| return; |
| } |
| } |
| } else { |
| Thread* self = Thread::Current(); |
| StackHandleScope<1> hs(self); |
| MutableHandle<mirror::ObjectArray<mirror::Class>> classes = |
| hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr); |
| GetClassesVisitorArrayArg local_arg; |
| local_arg.classes = &classes; |
| local_arg.success = false; |
| // We size the array assuming classes won't be added to the class table during the visit. |
| // If this assumption fails we iterate again. |
| while (!local_arg.success) { |
| size_t class_table_size; |
| { |
| 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(); |
| mirror::Class* array_of_class = FindArrayClass(self, &class_type); |
| classes.Assign( |
| mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size)); |
| CHECK(classes.Get() != nullptr); // OOME. |
| local_arg.index = 0; |
| local_arg.success = true; |
| VisitClasses(GetClassesVisitorArray, &local_arg); |
| } |
| for (int32_t i = 0; i < classes->GetLength(); ++i) { |
| // If the class table shrank during creation of the clases array we expect null elements. If |
| // the class table grew then the loop repeats. If classes are created after the loop has |
| // finished then we don't visit. |
| mirror::Class* klass = classes->Get(i); |
| if (klass != nullptr && !visitor(klass, arg)) { |
| return; |
| } |
| } |
| } |
| } |
| |
| ClassLinker::~ClassLinker() { |
| mirror::ArtMethod::ResetClass(); |
| mirror::Class::ResetClass(); |
| mirror::Constructor::ResetClass(); |
| mirror::Field::ResetClass(); |
| mirror::Method::ResetClass(); |
| mirror::Reference::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(); |
| STLDeleteElements(&oat_files_); |
| } |
| |
| mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) { |
| gc::Heap* const 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())))); |
| if (dex_cache.Get() == nullptr) { |
| return nullptr; |
| } |
| Handle<mirror::String> |
| location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str()))); |
| if (location.Get() == nullptr) { |
| return nullptr; |
| } |
| Handle<mirror::ObjectArray<mirror::String>> |
| strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds()))); |
| if (strings.Get() == nullptr) { |
| return nullptr; |
| } |
| Handle<mirror::ObjectArray<mirror::Class>> |
| types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds()))); |
| if (types.Get() == nullptr) { |
| return nullptr; |
| } |
| Handle<mirror::ObjectArray<mirror::ArtMethod>> |
| methods(hs.NewHandle(AllocArtMethodArray(self, dex_file.NumMethodIds()))); |
| if (methods.Get() == nullptr) { |
| return nullptr; |
| } |
| Handle<mirror::Array> fields; |
| if (image_pointer_size_ == 8) { |
| fields = hs.NewHandle<mirror::Array>(mirror::LongArray::Alloc(self, dex_file.NumFieldIds())); |
| } else { |
| fields = hs.NewHandle<mirror::Array>(mirror::IntArray::Alloc(self, dex_file.NumFieldIds())); |
| } |
| if (fields.Get() == nullptr) { |
| return nullptr; |
| } |
| dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(), |
| fields.Get()); |
| return dex_cache.Get(); |
| } |
| |
| mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Class, |
| uint32_t class_size) { |
| DCHECK_GE(class_size, sizeof(mirror::Class)); |
| gc::Heap* heap = Runtime::Current()->GetHeap(); |
| mirror::Class::InitializeClassVisitor visitor(class_size); |
| mirror::Object* k = kMovingClasses ? |
| 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. |
| return nullptr; |
| } |
| return k->AsClass(); |
| } |
| |
| mirror::Class* ClassLinker::AllocClass(Thread* self, uint32_t class_size) { |
| return AllocClass(self, GetClassRoot(kJavaLangClass), class_size); |
| } |
| |
| 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( |
| self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length); |
| } |
| |
| mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor, |
| mirror::Class* klass) { |
| DCHECK(klass != nullptr); |
| |
| // For temporary classes we must wait for them to be retired. |
| if (init_done_ && klass->IsTemp()) { |
| CHECK(!klass->IsResolved()); |
| if (klass->IsErroneous()) { |
| ThrowEarlierClassFailure(klass); |
| return nullptr; |
| } |
| StackHandleScope<1> hs(self); |
| Handle<mirror::Class> h_class(hs.NewHandle(klass)); |
| ObjectLock<mirror::Class> lock(self, h_class); |
| // Loop and wait for the resolving thread to retire this class. |
| while (!h_class->IsRetired() && !h_class->IsErroneous()) { |
| lock.WaitIgnoringInterrupts(); |
| } |
| if (h_class->IsErroneous()) { |
| ThrowEarlierClassFailure(h_class.Get()); |
| return nullptr; |
| } |
| CHECK(h_class->IsRetired()); |
| // Get the updated class from class table. |
| klass = LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor), |
| h_class.Get()->GetClassLoader()); |
| } |
| |
| // Wait for the class if it has not already been linked. |
| if (!klass->IsResolved() && !klass->IsErroneous()) { |
| StackHandleScope<1> hs(self); |
| HandleWrapper<mirror::Class> h_class(hs.NewHandleWrapper(&klass)); |
| ObjectLock<mirror::Class> lock(self, h_class); |
| // Check for circular dependencies between classes. |
| if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) { |
| ThrowClassCircularityError(h_class.Get()); |
| mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self); |
| return nullptr; |
| } |
| // Wait for the pending initialization to complete. |
| while (!h_class->IsResolved() && !h_class->IsErroneous()) { |
| lock.WaitIgnoringInterrupts(); |
| } |
| } |
| |
| if (klass->IsErroneous()) { |
| ThrowEarlierClassFailure(klass); |
| return nullptr; |
| } |
| // Return the loaded class. No exceptions should be pending. |
| CHECK(klass->IsResolved()) << PrettyClass(klass); |
| self->AssertNoPendingException(); |
| return klass; |
| } |
| |
| typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry; |
| |
| // Search a collection of DexFiles for a descriptor |
| ClassPathEntry FindInClassPath(const char* descriptor, |
| size_t hash, const std::vector<const DexFile*>& class_path) { |
| for (const DexFile* dex_file : class_path) { |
| const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash); |
| if (dex_class_def != nullptr) { |
| return ClassPathEntry(dex_file, dex_class_def); |
| } |
| } |
| return ClassPathEntry(nullptr, nullptr); |
| } |
| |
| 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)) { |
| *result = nullptr; |
| return false; |
| } |
| |
| // 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; |
| } |
| 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(); |
| } |
| |
| // 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, |
| Handle<mirror::ClassLoader> class_loader) { |
| DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; |
| DCHECK(self != nullptr); |
| self->AssertNoPendingException(); |
| if (descriptor[1] == '\0') { |
| // only the descriptors of primitive types should be 1 character long, also avoid class lookup |
| // for primitive classes that aren't backed by dex files. |
| return FindPrimitiveClass(descriptor[0]); |
| } |
| const size_t hash = ComputeModifiedUtf8Hash(descriptor); |
| // Find the class in the loaded classes table. |
| mirror::Class* klass = LookupClass(self, descriptor, hash, class_loader.Get()); |
| if (klass != nullptr) { |
| return EnsureResolved(self, descriptor, klass); |
| } |
| // Class is not yet loaded. |
| if (descriptor[0] == '[') { |
| return CreateArrayClass(self, descriptor, hash, class_loader); |
| } else if (class_loader.Get() == nullptr) { |
| // The boot class loader, search the boot class path. |
| ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); |
| if (pair.second != nullptr) { |
| return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first, |
| *pair.second); |
| } else { |
| // The boot class loader is searched ahead of the application class loader, failures are |
| // 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(pre_allocated); |
| return nullptr; |
| } |
| } else { |
| ScopedObjectAccessUnchecked soa(self); |
| 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)); |
| ScopedLocalRef<jobject> result(soa.Env(), nullptr); |
| { |
| ScopedThreadStateChange tsc(self, kNative); |
| ScopedLocalRef<jobject> class_name_object(soa.Env(), |
| soa.Env()->NewStringUTF(class_name_string.c_str())); |
| if (class_name_object.get() == nullptr) { |
| DCHECK(self->IsExceptionPending()); // OOME. |
| return nullptr; |
| } |
| CHECK(class_loader_object.get() != nullptr); |
| result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(), |
| WellKnownClasses::java_lang_ClassLoader_loadClass, |
| class_name_object.get())); |
| } |
| if (self->IsExceptionPending()) { |
| // If the ClassLoader threw, pass that exception up. |
| return nullptr; |
| } else if (result.get() == nullptr) { |
| // broken loader - throw NPE to be compatible with Dalvik |
| 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()); |
| } |
| } |
| UNREACHABLE(); |
| } |
| |
| mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash, |
| Handle<mirror::ClassLoader> class_loader, |
| const DexFile& dex_file, |
| const DexFile::ClassDef& dex_class_def) { |
| StackHandleScope<3> hs(self); |
| auto klass = hs.NewHandle<mirror::Class>(nullptr); |
| |
| // Load the class from the dex file. |
| if (UNLIKELY(!init_done_)) { |
| // finish up init of hand crafted class_roots_ |
| if (strcmp(descriptor, "Ljava/lang/Object;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangObject)); |
| } else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangClass)); |
| } else if (strcmp(descriptor, "Ljava/lang/String;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangString)); |
| } else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangRefReference)); |
| } else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangDexCache)); |
| } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) { |
| klass.Assign(GetClassRoot(kJavaLangReflectArtMethod)); |
| } |
| } |
| |
| if (klass.Get() == nullptr) { |
| // Allocate a class with the status of not ready. |
| // Interface object should get the right size here. Regular class will |
| // figure out the right size later and be replaced with one of the right |
| // size when the class becomes resolved. |
| klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def))); |
| } |
| if (UNLIKELY(klass.Get() == nullptr)) { |
| CHECK(self->IsExceptionPending()); // Expect an OOME. |
| return nullptr; |
| } |
| klass->SetDexCache(FindDexCache(dex_file)); |
| |
| 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(); |
| } |
| } |
| |
| ObjectLock<mirror::Class> lock(self, klass); |
| klass->SetClinitThreadId(self->GetTid()); |
| |
| // Add the newly loaded class to the loaded classes table. |
| mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash); |
| if (existing != nullptr) { |
| // We failed to insert because we raced with another thread. Calling EnsureResolved may cause |
| // this thread to block. |
| 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()) { |
| mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); |
| } |
| return nullptr; |
| } |
| CHECK(klass->IsLoaded()); |
| // Link the class (if necessary) |
| CHECK(!klass->IsResolved()); |
| // 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)) { |
| // Linking failed. |
| if (!klass->IsErroneous()) { |
| mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); |
| } |
| return nullptr; |
| } |
| self->AssertNoPendingException(); |
| CHECK(new_class != nullptr) << descriptor; |
| CHECK(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(new_class_h.Get()); |
| } |
| |
| /* |
| * We send CLASS_PREPARE events to the debugger from here. The |
| * definition of "preparation" is creating the static fields for a |
| * class and initializing them to the standard default values, but not |
| * executing any code (that comes later, during "initialization"). |
| * |
| * We did the static preparation in LinkClass. |
| * |
| * The class has been prepared and resolved but possibly not yet verified |
| * at this point. |
| */ |
| Dbg::PostClassPrepare(new_class_h.Get()); |
| |
| return new_class_h.Get(); |
| } |
| |
| uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, |
| const DexFile::ClassDef& 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) { |
| for (ClassDataItemIterator it(dex_file, class_data); it.HasNextStaticField(); it.Next()) { |
| const DexFile::FieldId& field_id = dex_file.GetFieldId(it.GetMemberIndex()); |
| const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); |
| char c = descriptor[0]; |
| 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_8, num_16, num_32, num_64, num_ref); |
| } |
| |
| 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 = dex_file.GetOatDexFile(); |
| if (oat_dex_file == nullptr) { |
| *found = false; |
| return OatFile::OatClass::Invalid(); |
| } |
| *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 uint8_t* class_data = dex_file.GetClassData(class_def); |
| CHECK(class_data != nullptr); |
| ClassDataItemIterator it(dex_file, class_data); |
| // Skip fields |
| while (it.HasNextStaticField()) { |
| it.Next(); |
| } |
| while (it.HasNextInstanceField()) { |
| it.Next(); |
| } |
| // Process methods |
| size_t class_def_method_index = 0; |
| while (it.HasNextDirectMethod()) { |
| if (it.GetMemberIndex() == method_idx) { |
| return class_def_method_index; |
| } |
| class_def_method_index++; |
| it.Next(); |
| } |
| while (it.HasNextVirtualMethod()) { |
| if (it.GetMemberIndex() == method_idx) { |
| return class_def_method_index; |
| } |
| class_def_method_index++; |
| it.Next(); |
| } |
| DCHECK(!it.HasNext()); |
| LOG(FATAL) << "Failed to find method index " << method_idx << " in " << dex_file.GetLocation(); |
| UNREACHABLE(); |
| } |
| |
| const OatFile::OatMethod ClassLinker::FindOatMethodFor(mirror::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(); |
| size_t oat_method_index; |
| if (method->IsStatic() || method->IsDirect()) { |
| // Simple case where the oat method index was stashed at load time. |
| oat_method_index = method->GetMethodIndex(); |
| } else { |
| // We're invoking a virtual method directly (thanks to sharpening), compute the oat_method_index |
| // by search for its position in the declared virtual methods. |
| oat_method_index = declaring_class->NumDirectMethods(); |
| size_t end = declaring_class->NumVirtualMethods(); |
| 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_virtual = true; |
| break; |
| } |
| oat_method_index++; |
| } |
| 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 = FindOatClass(*declaring_class->GetDexCache()->GetDexFile(), |
| declaring_class->GetDexClassDefIndex(), |
| found); |
| if (!(*found)) { |
| return OatFile::OatMethod::Invalid(); |
| } |
| return oat_class.GetOatMethod(oat_method_index); |
| } |
| |
| // Special case to get oat code without overwriting a trampoline. |
| const void* ClassLinker::GetQuickOatCodeFor(mirror::ArtMethod* method) { |
| CHECK(!method->IsAbstract()) << PrettyMethod(method); |
| if (method->IsProxyMethod()) { |
| return GetQuickProxyInvokeHandler(); |
| } |
| bool found; |
| OatFile::OatMethod oat_method = FindOatMethodFor(method, &found); |
| if (found) { |
| auto* code = oat_method.GetQuickCode(); |
| if (code != nullptr) { |
| return code; |
| } |
| } |
| jit::Jit* const jit = Runtime::Current()->GetJit(); |
| if (jit != nullptr) { |
| auto* code = jit->GetCodeCache()->GetCodeFor(method); |
| if (code != nullptr) { |
| return code; |
| } |
| } |
| if (method->IsNative()) { |
| // No code and native? Use generic trampoline. |
| return GetQuickGenericJniStub(); |
| } |
| return GetQuickToInterpreterBridge(); |
| } |
| |
| const void* ClassLinker::GetOatMethodQuickCodeFor(mirror::ArtMethod* method) { |
| if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) { |
| return 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) { |
| 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(); |
| } |
| |
| // Returns true if the method must run with interpreter, false otherwise. |
| static bool NeedsInterpreter(mirror::ArtMethod* method, const void* quick_code) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| 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; |
| } |
| // If interpreter mode is enabled, every method (except native and proxy) must |
| // be run with interpreter. |
| return Runtime::Current()->GetInstrumentation()->InterpretOnly() && |
| !method->IsNative() && !method->IsProxyMethod(); |
| } |
| |
| void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { |
| DCHECK(klass->IsInitialized()) << PrettyDescriptor(klass); |
| if (klass->NumDirectMethods() == 0) { |
| return; // No direct methods => no static methods. |
| } |
| Runtime* runtime = Runtime::Current(); |
| if (!runtime->IsStarted()) { |
| if (runtime->IsAotCompiler() || runtime->GetHeap()->HasImageSpace()) { |
| return; // OAT file unavailable. |
| } |
| } |
| |
| const DexFile& dex_file = klass->GetDexFile(); |
| const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); |
| CHECK(dex_class_def != nullptr); |
| 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); |
| // Skip fields |
| while (it.HasNextStaticField()) { |
| it.Next(); |
| } |
| while (it.HasNextInstanceField()) { |
| it.Next(); |
| } |
| 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); |
| if (!method->IsStatic()) { |
| // Only update static methods. |
| continue; |
| } |
| const void* quick_code = nullptr; |
| if (has_oat_class) { |
| OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index); |
| quick_code = oat_method.GetQuickCode(); |
| } |
| 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 && method->IsNative()) { |
| quick_code = GetQuickGenericJniStub(); |
| } else { |
| quick_code = GetQuickToInterpreterBridge(); |
| } |
| } |
| runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code); |
| } |
| // Ignore virtual methods on the iterator. |
| } |
| |
| void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, |
| const OatFile::OatClass* oat_class, |
| uint32_t class_def_method_index) { |
| Runtime* 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 (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(class_def_method_index); |
| oat_method.LinkMethod(method.Get()); |
| } |
| |
| // Install entry point from interpreter. |
| bool enter_interpreter = NeedsInterpreter(method.Get(), |
| method->GetEntryPointFromQuickCompiledCode()); |
| if (enter_interpreter && !method->IsNative()) { |
| method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge); |
| } else { |
| method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); |
| } |
| |
| if (method->IsAbstract()) { |
| method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); |
| return; |
| } |
| |
| 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(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()); |
| } else { |
| method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); |
| } |
| } |
| |
| if (method->IsNative()) { |
| // Unregistering restores the dlsym lookup stub. |
| method->UnregisterNative(); |
| |
| if (enter_interpreter) { |
| // 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)); |
| } |
| } |
| } |
| |
| 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()); |
| const char* descriptor = dex_file.GetClassDescriptor(dex_class_def); |
| CHECK(descriptor != nullptr); |
| |
| klass->SetClass(GetClassRoot(kJavaLangClass)); |
| 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); |
| 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); |
| } |
| |
| 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); |
| } |
| } |
| |
| 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; |
| } |
| |
| void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file, |
| const uint8_t* class_data, |
| Handle<mirror::Class> klass, |
| const OatFile::OatClass* oat_class) { |
| // 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->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); |
| // Note: We cannot have thread suspension until the field arrays are setup or else |
| // Class::VisitFieldRoots may miss some fields. |
| self->AllowThreadSuspension(); |
| // 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()) { |
| self->AllowThreadSuspension(); |
| 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; |
| } |
| klass->SetDirectMethod(i, method.Get()); |
| 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++; |
| } |
| for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) { |
| self->AllowThreadSuspension(); |
| 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; |
| } |
| klass->SetVirtualMethod(i, method.Get()); |
| DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i); |
| LinkCode(method, oat_class, class_def_method_index); |
| class_def_method_index++; |
| } |
| DCHECK(!it.HasNext()); |
| } |
| |
| 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) { |
| 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()); |
| |
| ScopedAssertNoThreadSuspension ants(self, "LoadMethod"); |
| dst->SetDexMethodIndex(dex_method_idx); |
| dst->SetDeclaringClass(klass.Get()); |
| dst->SetCodeItemOffset(it.GetMethodCodeItemOffset()); |
| |
| dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods()); |
| dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes()); |
| |
| uint32_t access_flags = it.GetMethodAccessFlags(); |
| |
| if (UNLIKELY(strcmp("finalize", method_name) == 0)) { |
| // Set finalizable flag on declaring class. |
| if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) { |
| // Void return type. |
| if (klass->GetClassLoader() != nullptr) { // All non-boot finalizer methods are flagged. |
| klass->SetFinalizable(); |
| } else { |
| std::string temp; |
| const char* klass_descriptor = klass->GetDescriptor(&temp); |
| // The Enum class declares a "final" finalize() method to prevent subclasses from |
| // introducing a finalizer. We don't want to set the finalizable flag for Enum or its |
| // subclasses, so we exclude it here. |
| // We also want to avoid setting the flag on Object, where we know that finalize() is |
| // empty. |
| if (strcmp(klass_descriptor, "Ljava/lang/Object;") != 0 && |
| strcmp(klass_descriptor, "Ljava/lang/Enum;") != 0) { |
| klass->SetFinalizable(); |
|
|