diff options
Diffstat (limited to 'runtime/class_linker-inl.h')
-rw-r--r-- | runtime/class_linker-inl.h | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 4e8f8edf64..26c422b9eb 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -401,6 +401,104 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, return resolved_method; } +template <ClassLinker::ResolveMode kResolveMode> +inline ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + ArtMethod* referrer, + InvokeType type) { + DCHECK(dex_cache->GetClassLoader() == class_loader.Get()); + DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump(); + DCHECK(dex_cache != nullptr); + DCHECK(referrer == nullptr || !referrer->IsProxyMethod()); + // Check for hit in the dex cache. + ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); + Thread::PoisonObjectPointersIfDebug(); + 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(); + return resolved; + } + const DexFile& dex_file = *dex_cache->GetDexFile(); + const dex::MethodId& method_id = dex_file.GetMethodId(method_idx); + 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(method_id.class_idx_, dex_cache.Get(), class_loader.Get()); + if (UNLIKELY(klass == nullptr)) { + // We normaly should not end up here. However the verifier currently doesn't guarantee + // the invariant of having the klass in the class table. b/73760543 + klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { + // This can only happen if the current thread is not allowed to load + // classes. + DCHECK(!Thread::Current()->CanLoadClasses()); + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + } + } else { + // The method was not in the DexCache, resolve the declaring class. + klass = ResolveType(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; + } + + if (!valid_dex_cache_method) { + resolved = FindResolvedMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx); + } + + // 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) && + LIKELY(kResolveMode == ResolveMode::kNoChecks || + !resolved->CheckIncompatibleClassChange(type))) { + return resolved; + } else { + // 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) { + resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx); + } + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); + } else { + // 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); + ThrowNoSuchMethodError(type, klass, name, signature); + } + Thread::Current()->AssertPendingException(); + return nullptr; + } +} + inline ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, ArtMethod* referrer, bool is_static) { @@ -431,6 +529,35 @@ inline ArtField* ClassLinker::ResolveField(uint32_t field_idx, return resolved_field; } +inline ArtField* ClassLinker::ResolveField(uint32_t field_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + bool is_static) { + DCHECK(dex_cache != nullptr); + DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Get()); + DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump(); + ArtField* resolved = dex_cache->GetResolvedField(field_idx); + Thread::PoisonObjectPointersIfDebug(); + if (resolved != nullptr) { + return resolved; + } + const DexFile& dex_file = *dex_cache->GetDexFile(); + const dex::FieldId& field_id = dex_file.GetFieldId(field_idx); + ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + + resolved = FindResolvedField(klass, dex_cache.Get(), class_loader.Get(), field_idx, is_static); + if (resolved == nullptr) { + const char* name = dex_file.GetFieldName(field_id); + const char* type = dex_file.GetFieldTypeDescriptor(field_id); + ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name); + } + return resolved; +} + template <class Visitor> inline void ClassLinker::VisitClassTables(const Visitor& visitor) { Thread* const self = Thread::Current(); |