summaryrefslogtreecommitdiff
path: root/runtime/class_linker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r--runtime/class_linker.cc700
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,