diff options
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r-- | runtime/class_linker.cc | 700 |
1 files changed, 296 insertions, 404 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 969a67bfd1..d06ba7818d 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -16,6 +16,8 @@ #include "class_linker.h" +#include <unistd.h> + #include <algorithm> #include <deque> #include <iostream> @@ -24,7 +26,6 @@ #include <queue> #include <string> #include <tuple> -#include <unistd.h> #include <unordered_map> #include <utility> #include <vector> @@ -53,15 +54,15 @@ #include "entrypoints/entrypoint_utils.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "experimental_flags.h" -#include "gc_root-inl.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap-inl.h" #include "gc/accounting/space_bitmap-inl.h" -#include "gc/heap.h" #include "gc/heap-visit-objects-inl.h" +#include "gc/heap.h" #include "gc/scoped_gc_critical_section.h" #include "gc/space/image_space.h" #include "gc/space/space-inl.h" +#include "gc_root-inl.h" #include "handle_scope-inl.h" #include "image-inl.h" #include "imt_conflict_table.h" @@ -76,37 +77,37 @@ #include "leb128.h" #include "linear_alloc.h" #include "mirror/call_site.h" -#include "mirror/class.h" #include "mirror/class-inl.h" +#include "mirror/class.h" #include "mirror/class_ext.h" #include "mirror/class_loader.h" -#include "mirror/dex_cache.h" #include "mirror/dex_cache-inl.h" +#include "mirror/dex_cache.h" #include "mirror/emulated_stack_frame.h" #include "mirror/field.h" #include "mirror/iftable-inl.h" #include "mirror/method.h" -#include "mirror/method_type.h" #include "mirror/method_handle_impl.h" #include "mirror/method_handles_lookup.h" +#include "mirror/method_type.h" #include "mirror/object-inl.h" -#include "mirror/object_array-inl.h" #include "mirror/object-refvisitor-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 "native/dalvik_system_DexFile.h" +#include "nativehelper/ScopedLocalRef.h" #include "oat.h" -#include "oat_file.h" #include "oat_file-inl.h" +#include "oat_file.h" #include "oat_file_assistant.h" #include "oat_file_manager.h" #include "object_lock.h" #include "os.h" #include "runtime.h" #include "runtime_callbacks.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "thread_list.h" @@ -150,8 +151,8 @@ static bool HasInitWithString(Thread* self, ClassLinker* class_linker, const cha return false; } - ArtMethod* exception_init_method = exception_class->FindDeclaredDirectMethod( - "<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize()); + ArtMethod* exception_init_method = exception_class->FindConstructor( + "(Ljava/lang/String;)V", class_linker->GetImagePointerSize()); return exception_init_method != nullptr; } @@ -369,7 +370,8 @@ ClassLinker::ClassLinker(InternTable* intern_table) quick_imt_conflict_trampoline_(nullptr), quick_generic_jni_trampoline_(nullptr), quick_to_interpreter_bridge_trampoline_(nullptr), - image_pointer_size_(kRuntimePointerSize) { + image_pointer_size_(kRuntimePointerSize), + cha_(new ClassHierarchyAnalysis()) { CHECK(intern_table_ != nullptr); static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_), "Array cache size wrong."); @@ -1114,7 +1116,8 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor { virtual void Visit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { const bool is_copied = method->IsCopied(); - ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(kRuntimePointerSize); + mirror::MethodDexCacheType* resolved_methods = + method->GetDexCacheResolvedMethods(kRuntimePointerSize); if (resolved_methods != nullptr) { bool in_image_space = false; if (kIsDebugBuild || is_copied) { @@ -1136,49 +1139,6 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor { const ImageHeader& header_; }; -class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor { - public: - explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {} - - virtual void Visit(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_, Locks::classlinker_classes_lock_) { - ObjPtr<mirror::Class> klass = method->GetDeclaringClass(); - if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { - CHECK_EQ(table_->LookupByDescriptor(klass), klass) << mirror::Class::PrettyClass(klass); - } - } - - private: - ClassTable* const table_; -}; - -class VerifyDirectInterfacesInTableClassVisitor { - public: - explicit VerifyDirectInterfacesInTableClassVisitor(ObjPtr<mirror::ClassLoader> class_loader) - : class_loader_(class_loader), self_(Thread::Current()) { } - - bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { - if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader_) { - classes_.push_back(klass); - } - return true; - } - - void Check() const REQUIRES_SHARED(Locks::mutator_lock_) { - for (ObjPtr<mirror::Class> klass : classes_) { - for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) { - CHECK(klass->GetDirectInterface(self_, klass, i) != nullptr) - << klass->PrettyDescriptor() << " iface #" << i; - } - } - } - - private: - ObjPtr<mirror::ClassLoader> class_loader_; - Thread* self_; - std::vector<ObjPtr<mirror::Class>> classes_; -}; - class VerifyDeclaringClassVisitor : public ArtMethodVisitor { public: VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) @@ -1284,6 +1244,25 @@ static void CopyDexCachePairs(const std::atomic<mirror::DexCachePair<T>>* src, } } +template <typename T> +static void CopyNativeDexCachePairs(std::atomic<mirror::NativeDexCachePair<T>>* src, + size_t count, + std::atomic<mirror::NativeDexCachePair<T>>* dst, + PointerSize pointer_size) { + DCHECK_NE(count, 0u); + DCHECK(mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).object != nullptr || + mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).index != 0u); + for (size_t i = 0; i < count; ++i) { + DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).index, 0u); + DCHECK(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).object == nullptr); + mirror::NativeDexCachePair<T> source = + mirror::DexCache::GetNativePairPtrSize(src, i, pointer_size); + if (source.index != 0u || source.object != nullptr) { + mirror::DexCache::SetNativePairPtrSize(dst, i, source, pointer_size); + } + } +} + // new_class_set is the set of classes that were read from the class table section in the image. // If there was no class table section, it is null. // Note: using a class here to avoid having to make ClassLinker internals public. @@ -1363,7 +1342,10 @@ bool AppImageClassLoadersAndDexCachesHelper::Update( if (dex_file->NumTypeIds() < num_types) { num_types = dex_file->NumTypeIds(); } - const size_t num_methods = dex_file->NumMethodIds(); + size_t num_methods = mirror::DexCache::kDexCacheMethodCacheSize; + if (dex_file->NumMethodIds() < num_methods) { + num_methods = dex_file->NumMethodIds(); + } size_t num_fields = mirror::DexCache::kDexCacheFieldCacheSize; if (dex_file->NumFieldIds() < num_fields) { num_fields = dex_file->NumFieldIds(); @@ -1396,37 +1378,18 @@ bool AppImageClassLoadersAndDexCachesHelper::Update( dex_cache->SetResolvedTypes(types); } if (num_methods != 0u) { - ArtMethod** const methods = reinterpret_cast<ArtMethod**>( - raw_arrays + layout.MethodsOffset()); - ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods(); - for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) { - DCHECK(methods[j] == nullptr); - } - CopyNonNull(image_resolved_methods, - num_methods, - methods, - [] (const ArtMethod* method) { - return method == nullptr; - }); + mirror::MethodDexCacheType* const image_resolved_methods = + dex_cache->GetResolvedMethods(); + mirror::MethodDexCacheType* const methods = + reinterpret_cast<mirror::MethodDexCacheType*>(raw_arrays + layout.MethodsOffset()); + CopyNativeDexCachePairs(image_resolved_methods, num_methods, methods, image_pointer_size); dex_cache->SetResolvedMethods(methods); } if (num_fields != 0u) { mirror::FieldDexCacheType* const image_resolved_fields = dex_cache->GetResolvedFields(); mirror::FieldDexCacheType* const fields = reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset()); - for (size_t j = 0; j < num_fields; ++j) { - DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(fields, j, image_pointer_size).index, - 0u); - DCHECK(mirror::DexCache::GetNativePairPtrSize(fields, j, image_pointer_size).object == - nullptr); - mirror::DexCache::SetNativePairPtrSize( - fields, - j, - mirror::DexCache::GetNativePairPtrSize(image_resolved_fields, - j, - image_pointer_size), - image_pointer_size); - } + CopyNativeDexCachePairs(image_resolved_fields, num_fields, fields, image_pointer_size); dex_cache->SetResolvedFields(fields); } if (num_method_types != 0u) { @@ -1663,13 +1626,13 @@ class ImageSanityChecks FINAL { heap->VisitObjects(visitor); } - static void CheckPointerArray(gc::Heap* heap, - ClassLinker* class_linker, - ArtMethod** arr, - size_t size) + static void CheckArtMethodDexCacheArray(gc::Heap* heap, + ClassLinker* class_linker, + mirror::MethodDexCacheType* arr, + size_t size) REQUIRES_SHARED(Locks::mutator_lock_) { ImageSanityChecks isc(heap, class_linker); - isc.SanityCheckArtMethodPointerArray(arr, size); + isc.SanityCheckArtMethodDexCacheArray(arr, size); } private: @@ -1724,7 +1687,7 @@ class ImageSanityChecks FINAL { } } - void SanityCheckArtMethodPointerArray(ArtMethod** arr, size_t size) + void SanityCheckArtMethodDexCacheArray(mirror::MethodDexCacheType* arr, size_t size) REQUIRES_SHARED(Locks::mutator_lock_) { CHECK_EQ(arr != nullptr, size != 0u); if (arr != nullptr) { @@ -1740,7 +1703,8 @@ class ImageSanityChecks FINAL { CHECK(contains); } for (size_t j = 0; j < size; ++j) { - ArtMethod* method = mirror::DexCache::GetElementPtrSize(arr, j, pointer_size_); + auto pair = mirror::DexCache::GetNativePairPtrSize(arr, j, pointer_size_); + ArtMethod* method = pair.object; // expected_class == null means we are a dex cache. if (method != nullptr) { SanityCheckArtMethod(method, nullptr); @@ -1757,6 +1721,63 @@ class ImageSanityChecks FINAL { std::vector<const ImageSection*> runtime_method_sections_; }; +static void VerifyAppImage(const ImageHeader& header, + const Handle<mirror::ClassLoader>& class_loader, + const Handle<mirror::ObjectArray<mirror::DexCache> >& dex_caches, + ClassTable* class_table, gc::space::ImageSpace* space) + REQUIRES_SHARED(Locks::mutator_lock_) { + { + class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor { + public: + explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {} + + virtual void Visit(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_, Locks::classlinker_classes_lock_) { + ObjPtr<mirror::Class> klass = method->GetDeclaringClass(); + if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { + CHECK_EQ(table_->LookupByDescriptor(klass), klass) << mirror::Class::PrettyClass(klass); + } + } + + private: + ClassTable* const table_; + }; + VerifyClassInTableArtMethodVisitor visitor(class_table); + header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize); + } + { + // Verify that all direct interfaces of classes in the class table are also resolved. + std::vector<ObjPtr<mirror::Class>> classes; + auto verify_direct_interfaces_in_table = [&](ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader.Get()) { + classes.push_back(klass); + } + return true; + }; + class_table->Visit(verify_direct_interfaces_in_table); + Thread* self = Thread::Current(); + for (ObjPtr<mirror::Class> klass : classes) { + for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) { + CHECK(klass->GetDirectInterface(self, klass, i) != nullptr) + << klass->PrettyDescriptor() << " iface #" << i; + } + } + } + // Check that all non-primitive classes in dex caches are also in the class table. + for (int32_t i = 0; i < dex_caches->GetLength(); i++) { + ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i); + mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes(); + for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) { + ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read(); + if (klass != nullptr && !klass->IsPrimitive()) { + CHECK(class_table->Contains(klass)) + << klass->PrettyDescriptor() << " " << dex_cache->GetDexFile()->GetLocation(); + } + } + } +} + bool ClassLinker::AddImageSpace( gc::space::ImageSpace* space, Handle<mirror::ClassLoader> class_loader, @@ -1851,10 +1872,10 @@ bool ClassLinker::AddImageSpace( } } else { if (kSanityCheckObjects) { - ImageSanityChecks::CheckPointerArray(heap, - this, - dex_cache->GetResolvedMethods(), - dex_cache->NumResolvedMethods()); + ImageSanityChecks::CheckArtMethodDexCacheArray(heap, + this, + dex_cache->GetResolvedMethods(), + dex_cache->NumResolvedMethods()); } // Register dex files, keep track of existing ones that are conflicts. AppendToBootClassPath(*dex_file.get(), dex_cache); @@ -2010,28 +2031,13 @@ bool ClassLinker::AddImageSpace( WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); class_table->AddClassSet(std::move(temp_set)); } + if (kIsDebugBuild && app_image) { // This verification needs to happen after the classes have been added to the class loader. // Since it ensures classes are in the class table. - VerifyClassInTableArtMethodVisitor visitor2(class_table); - header.VisitPackedArtMethods(&visitor2, space->Begin(), kRuntimePointerSize); - // Verify that all direct interfaces of classes in the class table are also resolved. - VerifyDirectInterfacesInTableClassVisitor visitor(class_loader.Get()); - class_table->Visit(visitor); - visitor.Check(); - // Check that all non-primitive classes in dex caches are also in the class table. - for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i); - mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes(); - for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) { - ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read(); - if (klass != nullptr && !klass->IsPrimitive()) { - CHECK(class_table->Contains(klass)) << klass->PrettyDescriptor() - << " " << dex_cache->GetDexFile()->GetLocation(); - } - } - } + VerifyAppImage(header, class_loader, dex_caches, class_table, space); } + VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time); return true; } @@ -2312,8 +2318,12 @@ void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) { if (runtime->GetJit() != nullptr) { jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache(); if (code_cache != nullptr) { + // For the JIT case, RemoveMethodsIn removes the CHA dependencies. code_cache->RemoveMethodsIn(self, *data.allocator); } + } else if (cha_ != nullptr) { + // If we don't have a JIT, we need to manually remove the CHA dependencies manually. + cha_->RemoveDependenciesForLinearAlloc(data.allocator); } delete data.allocator; delete data.class_table; @@ -3483,7 +3493,8 @@ void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, ObjPtr<mirror::DexCache> dex_cache) { CHECK(dex_cache != nullptr) << dex_file.GetLocation(); boot_class_path_.push_back(&dex_file); - RegisterBootClassPathDexFile(dex_file, dex_cache); + WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_); + RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr); } void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, @@ -3666,12 +3677,6 @@ ObjPtr<mirror::DexCache> ClassLinker::RegisterDexFile(const DexFile& dex_file, return h_dex_cache.Get(); } -void ClassLinker::RegisterBootClassPathDexFile(const DexFile& dex_file, - ObjPtr<mirror::DexCache> dex_cache) { - WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_); - RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr); -} - bool ClassLinker::IsDexFileRegistered(Thread* self, const DexFile& dex_file) { ReaderMutexLock mu(self, *Locks::dex_lock_); return DecodeDexCache(self, FindDexCacheDataLocked(dex_file)) != nullptr; @@ -3722,20 +3727,6 @@ ClassLinker::DexCacheData ClassLinker::FindDexCacheDataLocked(const DexFile& dex return DexCacheData(); } -void ClassLinker::FixupDexCaches(ArtMethod* resolution_method) { - Thread* const self = Thread::Current(); - ReaderMutexLock mu(self, *Locks::dex_lock_); - for (const DexCacheData& data : dex_caches_) { - if (!self->IsJWeakCleared(data.weak_root)) { - ObjPtr<mirror::DexCache> dex_cache = ObjPtr<mirror::DexCache>::DownCast( - self->DecodeJObject(data.weak_root)); - if (dex_cache != nullptr) { - dex_cache->Fixup(resolution_method, image_pointer_size_); - } - } - } -} - mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) { ObjPtr<mirror::Class> klass = AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_)); @@ -4638,10 +4629,8 @@ void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* // Find the <init>(InvocationHandler)V method. The exact method offset varies depending // on which front-end compiler was used to build the libcore DEX files. - ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)-> - FindDeclaredDirectMethod("<init>", - "(Ljava/lang/reflect/InvocationHandler;)V", - image_pointer_size_); + ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->FindConstructor( + "(Ljava/lang/reflect/InvocationHandler;)V", image_pointer_size_); DCHECK(proxy_constructor != nullptr) << "Could not find <init> method in java.lang.reflect.Proxy"; @@ -4653,8 +4642,9 @@ void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* // code_ too) DCHECK(out != nullptr); out->CopyFrom(proxy_constructor, image_pointer_size_); - // Make this constructor public and fix the class to be our Proxy version + // Make this constructor public and fix the class to be our Proxy version. // Mark kAccCompileDontBother so that we don't take JIT samples for the method. b/62349349 + // Note that the compiler calls a ResolveMethod() overload that does not handle a Proxy referrer. out->SetAccessFlags((out->GetAccessFlags() & ~kAccProtected) | kAccPublic | kAccCompileDontBother); @@ -5491,7 +5481,9 @@ bool ClassLinker::LinkClass(Thread* self, // Update CHA info based on whether we override methods. // Have to do this before setting the class as resolved which allows // instantiation of klass. - Runtime::Current()->GetClassHierarchyAnalysis()->UpdateAfterLoadingOf(klass); + if (cha_ != nullptr) { + cha_->UpdateAfterLoadingOf(klass); + } // This will notify waiters on klass that saw the not yet resolved // class in the class_table_ during EnsureResolved. @@ -5539,7 +5531,9 @@ bool ClassLinker::LinkClass(Thread* self, // Update CHA info based on whether we override methods. // Have to do this before setting the class as resolved which allows // instantiation of klass. - Runtime::Current()->GetClassHierarchyAnalysis()->UpdateAfterLoadingOf(h_new_class); + if (cha_ != nullptr) { + cha_->UpdateAfterLoadingOf(h_new_class); + } // This will notify waiters on temp class that saw the not yet resolved class in the // class_table_ during EnsureResolved. @@ -5629,12 +5623,18 @@ bool ClassLinker::LinkSuperClass(Handle<mirror::Class> klass) { return false; } // Verify - if (super->IsFinal() || super->IsInterface()) { + if (super->IsFinal()) { + ThrowVerifyError(klass.Get(), + "Superclass %s of %s is declared final", + super->PrettyDescriptor().c_str(), + klass->PrettyDescriptor().c_str()); + return false; + } + if (super->IsInterface()) { ThrowIncompatibleClassChangeError(klass.Get(), - "Superclass %s of %s is %s", + "Superclass %s of %s is an interface", super->PrettyDescriptor().c_str(), - klass->PrettyDescriptor().c_str(), - super->IsFinal() ? "declared final" : "an interface"); + klass->PrettyDescriptor().c_str()); return false; } if (!klass->CanAccess(super)) { @@ -6887,7 +6887,8 @@ class ClassLinker::LinkInterfaceMethodsHelper { // Check that there are no stale methods are in the dex cache array. auto* resolved_methods = klass_->GetDexCache()->GetResolvedMethods(); for (size_t i = 0, count = klass_->GetDexCache()->NumResolvedMethods(); i < count; ++i) { - auto* m = mirror::DexCache::GetElementPtrSize(resolved_methods, i, pointer_size); + auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_methods, i, pointer_size); + ArtMethod* m = pair.object; CHECK(move_table_.find(m) == move_table_.end() || // The original versions of copied methods will still be present so allow those too. // Note that if the first check passes this might fail to GetDeclaringClass(). @@ -7363,10 +7364,8 @@ bool ClassLinker::LinkInterfaceMethods( // defaults. This means we don't need to do any trickery when creating the Miranda methods, since // they will already be null. This has the additional benefit that the declarer of a miranda // method will actually declare an abstract method. - for (size_t i = ifcount; i != 0; ) { + for (size_t i = ifcount; i != 0u; ) { --i; - - DCHECK_GE(i, 0u); DCHECK_LT(i, ifcount); size_t num_methods = iftable->GetInterface(i)->NumDeclaredVirtualMethods(); @@ -7947,201 +7946,96 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, ArtMethod* referrer, InvokeType type) { DCHECK(dex_cache != nullptr); + DCHECK(referrer == nullptr || !referrer->IsProxyMethod()); // Check for hit in the dex cache. - ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); + PointerSize pointer_size = image_pointer_size_; + ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, pointer_size); Thread::PoisonObjectPointersIfDebug(); - if (resolved != nullptr && !resolved->IsRuntimeMethod()) { + DCHECK(resolved == nullptr || !resolved->IsRuntimeMethod()); + bool valid_dex_cache_method = resolved != nullptr; + if (kResolveMode == ResolveMode::kNoChecks && valid_dex_cache_method) { + // We have a valid method from the DexCache and no checks to perform. DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); - if (kResolveMode == ClassLinker::kForceICCECheck) { - if (resolved->CheckIncompatibleClassChange(type)) { - ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); - return nullptr; - } - } return resolved; } - // Fail, get the declaring class. const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); - ObjPtr<mirror::Class> klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); - if (klass == nullptr) { + ObjPtr<mirror::Class> klass = nullptr; + if (valid_dex_cache_method) { + // We have a valid method from the DexCache but we need to perform ICCE and IAE checks. + DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); + klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get()); + DCHECK(klass != nullptr); + } else { + // The method was not in the DexCache, resolve the declaring class. + klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + } + + // Check if the invoke type matches the class type. + if (kResolveMode == ResolveMode::kCheckICCEAndIAE && + CheckInvokeClassMismatch</* kThrow */ true>( + dex_cache.Get(), type, [klass]() { return klass; })) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; } - // Scan using method_idx, this saves string compares but will only hit for matching dex - // caches/files. - switch (type) { - case kDirect: // Fall-through. - case kStatic: - resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_); - DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); - break; - case kInterface: - // We have to check whether the method id really belongs to an interface (dex static bytecode - // constraint A15). Otherwise you must not invoke-interface on it. - // - // This is not symmetric to A12-A14 (direct, static, virtual), as using FindInterfaceMethod - // assumes that the given type is an interface, and will check the interface table if the - // method isn't declared in the class. So it may find an interface method (usually by name - // in the handling below, but we do the constraint check early). In that case, - // CheckIncompatibleClassChange will succeed (as it is called on an interface method) - // unexpectedly. - // Example: - // interface I { - // foo() - // } - // class A implements I { - // ... - // } - // class B extends A { - // ... - // } - // invoke-interface B.foo - // -> FindInterfaceMethod finds I.foo (interface method), not A.foo (miranda method) - if (UNLIKELY(!klass->IsInterface())) { - ThrowIncompatibleClassChangeError(klass, - "Found class %s, but interface was expected", - klass->PrettyDescriptor().c_str()); - return nullptr; - } else { - resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_); - DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); - } - break; - case kSuper: - if (klass->IsInterface()) { - resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_); - } else { - resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_); - } - break; - case kVirtual: - resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_); - break; - default: - LOG(FATAL) << "Unreachable - invocation type: " << type; - UNREACHABLE(); + + if (!valid_dex_cache_method) { + // Search for the method using dex_cache and method_idx. The Class::Find*Method() + // functions can optimize the search if the dex_cache is the same as the DexCache + // of the class, with fall-back to name and signature search otherwise. + if (klass->IsInterface()) { + resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size); + } else { + resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size); + } + DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); + if (resolved != nullptr) { + // Be a good citizen and update the dex cache to speed subsequent calls. + dex_cache->SetResolvedMethod(method_idx, resolved, pointer_size); + } } - if (resolved == nullptr) { - // Search by name, which works across dex files. - const char* name = dex_file.StringDataByIdx(method_id.name_idx_); - const Signature signature = dex_file.GetMethodSignature(method_id); - switch (type) { - case kDirect: // Fall-through. - case kStatic: - resolved = klass->FindDirectMethod(name, signature, image_pointer_size_); - DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); - break; - case kInterface: - resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); - DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); - break; - case kSuper: - if (klass->IsInterface()) { - resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); - } else { - resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); - } - break; - case kVirtual: - resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); - break; + + // Note: We can check for IllegalAccessError only if we have a referrer. + if (kResolveMode == ResolveMode::kCheckICCEAndIAE && resolved != nullptr && referrer != nullptr) { + ObjPtr<mirror::Class> methods_class = resolved->GetDeclaringClass(); + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); + if (!referring_class->CheckResolvedMethodAccess(methods_class, + resolved, + dex_cache.Get(), + method_idx, + type)) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; } } + // If we found a method, check for incompatible class changes. - if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) { - // Be a good citizen and update the dex cache to speed subsequent calls. - dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_); + if (LIKELY(resolved != nullptr) && + LIKELY(kResolveMode == ResolveMode::kNoChecks || + !resolved->CheckIncompatibleClassChange(type))) { return resolved; } else { - // If we had a method, it's an incompatible-class-change error. + // If we had a method, or if we can find one with another lookup type, + // it's an incompatible-class-change error. + if (resolved == nullptr) { + if (klass->IsInterface()) { + resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size); + } else { + // If there was an interface method with the same signature, + // we would have found it also in the "copied" methods. + DCHECK(klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size) == nullptr); + } + } if (resolved != nullptr) { ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); } else { - // We failed to find the method which means either an access error, an incompatible class - // change, or no such method. First try to find the method among direct and virtual methods. + // We failed to find the method (using all lookup types), so throw a NoSuchMethodError. const char* name = dex_file.StringDataByIdx(method_id.name_idx_); const Signature signature = dex_file.GetMethodSignature(method_id); - switch (type) { - case kDirect: - case kStatic: - resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); - // Note: kDirect and kStatic are also mutually exclusive, but in that case we would - // have had a resolved method before, which triggers the "true" branch above. - break; - case kInterface: - case kVirtual: - case kSuper: - resolved = klass->FindDirectMethod(name, signature, image_pointer_size_); - break; - } - - // If we found something, check that it can be accessed by the referrer. - bool exception_generated = false; - if (resolved != nullptr && referrer != nullptr) { - ObjPtr<mirror::Class> methods_class = resolved->GetDeclaringClass(); - ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); - if (!referring_class->CanAccess(methods_class)) { - ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, - methods_class, - resolved, - type); - exception_generated = true; - } else if (!referring_class->CanAccessMember(methods_class, resolved->GetAccessFlags())) { - ThrowIllegalAccessErrorMethod(referring_class, resolved); - exception_generated = true; - } - } - if (!exception_generated) { - // Otherwise, throw an IncompatibleClassChangeError if we found something, and check - // interface methods and throw if we find the method there. If we find nothing, throw a - // NoSuchMethodError. - switch (type) { - case kDirect: - case kStatic: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer); - } else { - resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer); - } else { - ThrowNoSuchMethodError(type, klass, name, signature); - } - } - break; - case kInterface: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer); - } else { - resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer); - } else { - ThrowNoSuchMethodError(type, klass, name, signature); - } - } - break; - case kSuper: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer); - } else { - ThrowNoSuchMethodError(type, klass, name, signature); - } - break; - case kVirtual: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer); - } else { - resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer); - } else { - ThrowNoSuchMethodError(type, klass, name, signature); - } - } - break; - } - } + ThrowNoSuchMethodError(type, klass, name, signature); } Thread::Current()->AssertPendingException(); return nullptr; @@ -8154,27 +8048,23 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(const DexFile& dex_file, Handle<mirror::ClassLoader> class_loader) { ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); Thread::PoisonObjectPointersIfDebug(); - if (resolved != nullptr && !resolved->IsRuntimeMethod()) { + if (resolved != nullptr) { + DCHECK(!resolved->IsRuntimeMethod()); DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); return resolved; } // Fail, get the declaring class. const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); - ObjPtr<mirror::Class> klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + ObjPtr<mirror::Class> klass = + ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { Thread::Current()->AssertPendingException(); return nullptr; } if (klass->IsInterface()) { - LOG(FATAL) << "ResolveAmbiguousMethod: unexpected method in interface: " - << klass->PrettyClass(); - return nullptr; - } - - // Search both direct and virtual methods - resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_); - if (resolved == nullptr) { - resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_); + resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_); + } else { + resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } return resolved; @@ -8489,19 +8379,19 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( case DexFile::MethodHandleType::kInvokeStatic: { kind = mirror::MethodHandle::Kind::kInvokeStatic; receiver_count = 0; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kStatic); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kStatic); break; } case DexFile::MethodHandleType::kInvokeInstance: { kind = mirror::MethodHandle::Kind::kInvokeVirtual; receiver_count = 1; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kVirtual); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kVirtual); break; } case DexFile::MethodHandleType::kInvokeConstructor: { @@ -8509,10 +8399,10 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( // are special cased later in this method. kind = mirror::MethodHandle::Kind::kInvokeTransform; receiver_count = 0; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kDirect); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kDirect); break; } case DexFile::MethodHandleType::kInvokeDirect: { @@ -8535,16 +8425,16 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( if (target_method->IsPrivate()) { kind = mirror::MethodHandle::Kind::kInvokeDirect; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kDirect); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kDirect); } else { kind = mirror::MethodHandle::Kind::kInvokeSuper; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kSuper); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kSuper); if (UNLIKELY(target_method == nullptr)) { break; } @@ -8560,10 +8450,10 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( case DexFile::MethodHandleType::kInvokeInterface: { kind = mirror::MethodHandle::Kind::kInvokeInterface; receiver_count = 1; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kInterface); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kInterface); break; } } @@ -9100,51 +8990,6 @@ std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_bo return ret; } -std::unordered_set<std::string> ClassLinker::GetClassDescriptorsForResolvedClasses( - const std::set<DexCacheResolvedClasses>& classes) { - ScopedTrace trace(__PRETTY_FUNCTION__); - std::unordered_set<std::string> ret; - Thread* const self = Thread::Current(); - std::unordered_map<std::string, const DexFile*> location_to_dex_file; - ScopedObjectAccess soa(self); - ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ReaderMutexLock mu(self, *Locks::dex_lock_); - for (const ClassLinker::DexCacheData& data : GetDexCachesData()) { - if (!self->IsJWeakCleared(data.weak_root)) { - ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(data.weak_root); - if (dex_cache != nullptr) { - const DexFile* dex_file = dex_cache->GetDexFile(); - // There could be duplicates if two dex files with the same location are mapped. - location_to_dex_file.emplace(dex_file->GetLocation(), dex_file); - } - } - } - for (const DexCacheResolvedClasses& info : classes) { - const std::string& location = info.GetDexLocation(); - auto found = location_to_dex_file.find(location); - if (found != location_to_dex_file.end()) { - const DexFile* dex_file = found->second; - VLOG(profiler) << "Found opened dex file for " << dex_file->GetLocation() << " with " - << info.GetClasses().size() << " classes"; - DCHECK_EQ(dex_file->GetLocationChecksum(), info.GetLocationChecksum()); - for (dex::TypeIndex type_idx : info.GetClasses()) { - if (!dex_file->IsTypeIndexValid(type_idx)) { - // Something went bad. The profile is probably corrupted. Abort and return an emtpy set. - LOG(WARNING) << "Corrupted profile: invalid type index " - << type_idx.index_ << " in dex " << location; - return std::unordered_set<std::string>(); - } - const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx); - const char* descriptor = dex_file->GetTypeDescriptor(type_id); - ret.insert(descriptor); - } - } else { - VLOG(class_linker) << "Failed to find opened dex file for location " << location; - } - } - return ret; -} - class ClassLinker::FindVirtualMethodHolderVisitor : public ClassVisitor { public: FindVirtualMethodHolderVisitor(const ArtMethod* method, PointerSize pointer_size) @@ -9179,15 +9024,62 @@ mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { ifcount * mirror::IfTable::kMax)); } +ArtMethod* ClassLinker::FindMethodForProxy(ArtMethod* proxy_method) { + DCHECK(proxy_method->IsProxyMethod()); + { + uint32_t method_index = proxy_method->GetDexMethodIndex(); + PointerSize pointer_size = image_pointer_size_; + Thread* const self = Thread::Current(); + ReaderMutexLock mu(self, *Locks::dex_lock_); + // Locate the dex cache of the original interface/Object + for (const DexCacheData& data : dex_caches_) { + if (!self->IsJWeakCleared(data.weak_root) && + proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods, pointer_size)) { + ObjPtr<mirror::DexCache> dex_cache = + ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); + if (dex_cache != nullptr) { + // Lookup up the method. Instead of going through LookupResolvedMethod() + // and thus LookupResolvedType(), use the ClassTable from the DexCacheData. + ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_index, pointer_size); + if (resolved_method == nullptr) { + const DexFile::MethodId& method_id = data.dex_file->GetMethodId(method_index); + ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(method_id.class_idx_); + if (klass == nullptr) { + const char* descriptor = data.dex_file->StringByTypeIdx(method_id.class_idx_); + klass = data.class_table->Lookup(descriptor, ComputeModifiedUtf8Hash(descriptor)); + DCHECK(klass != nullptr); + dex_cache->SetResolvedType(method_id.class_idx_, klass); + } + if (klass->IsInterface()) { + resolved_method = klass->FindInterfaceMethod(dex_cache, method_index, pointer_size); + } else { + DCHECK( + klass == WellKnownClasses::ToClass(WellKnownClasses::java_lang_reflect_Proxy) || + klass == WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object)); + resolved_method = klass->FindClassMethod(dex_cache, method_index, pointer_size); + } + CHECK(resolved_method != nullptr); + dex_cache->SetResolvedMethod(method_index, resolved_method, pointer_size); + } + return resolved_method; + } + } + } + } + // Note: Do not use proxy_method->PrettyMethod() as it can call back here. + LOG(FATAL) << "Didn't find dex cache for " << proxy_method->GetDeclaringClass()->PrettyClass(); + UNREACHABLE(); +} + // Instantiate ResolveMethod. -template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::kForceICCECheck>( +template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( const DexFile& dex_file, uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer, InvokeType type); -template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::kNoICCECheckForCache>( +template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( const DexFile& dex_file, uint32_t method_idx, Handle<mirror::DexCache> dex_cache, |