diff options
author | 2022-01-19 10:34:57 +0000 | |
---|---|---|
committer | 2022-02-01 14:23:12 +0000 | |
commit | bdf7dc06b54b8571070acb2dd13d70dd4e696a48 (patch) | |
tree | 9aaad569a759cf4c670367d950615ce03d8dcde1 | |
parent | b81df962c0e1547cb9235e68dbbb0478f604b4bb (diff) |
Introduce FindSuperMethodToCall to find the target of a super call.
And use it across compiler/runtime/nterp. This also fixes an issue in
nterp when handling an obsolete method.
Also move FindMethodFast and FindFieldFast next to their only use.
Test: test.py
Bug: 214328881
Change-Id: If64849575ae342324e30026e29d285eca6e61263
-rw-r--r-- | compiler/optimizing/instruction_builder.cc | 31 | ||||
-rw-r--r-- | runtime/common_throws.cc | 27 | ||||
-rw-r--r-- | runtime/common_throws.h | 6 | ||||
-rw-r--r-- | runtime/entrypoints/entrypoint_utils-inl.h | 216 | ||||
-rw-r--r-- | runtime/entrypoints/entrypoint_utils.h | 21 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_field_entrypoints.cc | 42 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 103 | ||||
-rw-r--r-- | runtime/interpreter/mterp/nterp.cc | 39 |
8 files changed, 207 insertions, 278 deletions
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index f8bf126eeb..aabc433448 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -28,6 +28,7 @@ #include "dex/dex_instruction-inl.h" #include "driver/dex_compilation_unit.h" #include "driver/compiler_options.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "imtable-inl.h" #include "intrinsics.h" #include "intrinsics_utils.h" @@ -932,36 +933,18 @@ static ArtMethod* ResolveMethod(uint16_t method_idx, // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of // which require runtime handling. if (*invoke_type == kSuper) { - ObjPtr<mirror::Class> compiling_class = dex_compilation_unit.GetCompilingClass().Get(); - if (compiling_class == nullptr) { + if (referrer == nullptr) { // We could not determine the method's class we need to wait until runtime. DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; } - ObjPtr<mirror::Class> referenced_class = class_linker->LookupResolvedType( - dex_compilation_unit.GetDexFile()->GetMethodId(method_idx).class_idx_, - dex_compilation_unit.GetDexCache().Get(), - class_loader.Get()); - DCHECK(referenced_class != nullptr); // We have already resolved a method from this class. - if (!referenced_class->IsAssignableFrom(compiling_class)) { - // We cannot statically determine the target method. The runtime will throw a - // NoSuchMethodError on this one. + ArtMethod* actual_method = FindSuperMethodToCall</*access_check=*/true>( + method_idx, resolved_method, referrer, soa.Self()); + if (actual_method == nullptr) { + // Clean up any exception left by method resolution. + soa.Self()->ClearException(); return nullptr; } - ArtMethod* actual_method; - if (referenced_class->IsInterface()) { - actual_method = referenced_class->FindVirtualMethodForInterfaceSuper( - resolved_method, class_linker->GetImagePointerSize()); - } else { - uint16_t vtable_index = resolved_method->GetMethodIndex(); - if (vtable_index >= static_cast<uint32_t>( - compiling_class->GetSuperClass()->GetVTableLength())) { - // No super method. The runtime will throw a NoSuchMethodError. - return nullptr; - } - actual_method = compiling_class->GetSuperClass()->GetVTableEntry( - vtable_index, class_linker->GetImagePointerSize()); - } if (!actual_method->IsInvokable()) { // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub // could resolve the callee to the wrong method. diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 853ded0338..c6e7cfb5ae 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -257,8 +257,10 @@ void ThrowIllegalStateException(const char* msg) { // IncompatibleClassChangeError -void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type, - ArtMethod* method, ArtMethod* referrer) { +void ThrowIncompatibleClassChangeError(InvokeType expected_type, + InvokeType found_type, + ArtMethod* method, + ArtMethod* referrer) { std::ostringstream msg; msg << "The method '" << ArtMethod::PrettyMethod(method) << "' was expected to be of type " << expected_type << " but instead was found to be of type " << found_type; @@ -267,24 +269,6 @@ void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType foun msg.str().c_str()); } -void ThrowIncompatibleClassChangeErrorClassForInterfaceSuper(ArtMethod* method, - ObjPtr<mirror::Class> target_class, - ObjPtr<mirror::Object> this_object, - ArtMethod* referrer) { - // Referrer is calling interface_method on this_object, however, the interface_method isn't - // implemented by this_object. - CHECK(this_object != nullptr); - std::ostringstream msg; - msg << "Class '" << mirror::Class::PrettyDescriptor(this_object->GetClass()) - << "' does not implement interface '" << mirror::Class::PrettyDescriptor(target_class) - << "' in call to '" - << ArtMethod::PrettyMethod(method) << "'"; - DumpB77342775DebugData(target_class, this_object->GetClass()); - ThrowException("Ljava/lang/IncompatibleClassChangeError;", - referrer != nullptr ? referrer->GetDeclaringClass() : nullptr, - msg.str().c_str()); -} - void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(ArtMethod* interface_method, ObjPtr<mirror::Object> this_object, ArtMethod* referrer) { @@ -302,7 +286,8 @@ void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(ArtMethod* inter msg.str().c_str()); } -void ThrowIncompatibleClassChangeErrorField(ArtField* resolved_field, bool is_static, +void ThrowIncompatibleClassChangeErrorField(ArtField* resolved_field, + bool is_static, ArtMethod* referrer) { std::ostringstream msg; msg << "Expected '" << ArtField::PrettyField(resolved_field) << "' to be a " diff --git a/runtime/common_throws.h b/runtime/common_throws.h index 832eac6f28..3a723f7f5a 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -134,12 +134,6 @@ void ThrowIncompatibleClassChangeError(InvokeType expected_type, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; -void ThrowIncompatibleClassChangeErrorClassForInterfaceSuper(ArtMethod* method, - ObjPtr<mirror::Class> target_class, - ObjPtr<mirror::Object> this_object, - ArtMethod* referrer) - REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; - void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(ArtMethod* interface_method, ObjPtr<mirror::Object> this_object, ArtMethod* referrer) diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index b36c5c3c96..4ee1013816 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -486,6 +486,66 @@ EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite); #undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL #undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL +template<bool access_check> +ALWAYS_INLINE ArtMethod* FindSuperMethodToCall(uint32_t method_idx, + ArtMethod* resolved_method, + ArtMethod* referrer, + Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + // TODO This lookup is quite slow. + // NB This is actually quite tricky to do any other way. We cannot use GetDeclaringClass since + // that will actually not be what we want in some cases where there are miranda methods or + // defaults. What we actually need is a GetContainingClass that says which classes virtuals + // this method is coming from. + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + dex::TypeIndex type_idx = referrer->GetDexFile()->GetMethodId(method_idx).class_idx_; + ObjPtr<mirror::Class> referenced_class = linker->ResolveType(type_idx, referrer); + if (UNLIKELY(referenced_class == nullptr)) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + if (access_check) { + if (!referenced_class->IsAssignableFrom(referrer->GetDeclaringClass())) { + ThrowNoSuchMethodError(kSuper, + resolved_method->GetDeclaringClass(), + resolved_method->GetName(), + resolved_method->GetSignature()); + return nullptr; + } + } + + if (referenced_class->IsInterface()) { + // TODO We can do better than this for a (compiled) fastpath. + ArtMethod* found_method = referenced_class->FindVirtualMethodForInterfaceSuper( + resolved_method, linker->GetImagePointerSize()); + DCHECK(found_method != nullptr); + return found_method; + } + + DCHECK(resolved_method->IsCopied() || + !resolved_method->GetDeclaringClass()->IsInterface()); + + uint16_t vtable_index = resolved_method->GetMethodIndex(); + ObjPtr<mirror::Class> super_class = referrer->GetDeclaringClass()->GetSuperClass(); + if (access_check) { + DCHECK(super_class == nullptr || super_class->HasVTable()); + // Check existence of super class. + if (super_class == nullptr || + vtable_index >= static_cast<uint32_t>(super_class->GetVTableLength())) { + // Behavior to agree with that of the verifier. + ThrowNoSuchMethodError(kSuper, + resolved_method->GetDeclaringClass(), + resolved_method->GetName(), + resolved_method->GetSignature()); + return nullptr; // Failure. + } + } + DCHECK(super_class != nullptr); + DCHECK(super_class->HasVTable()); + return super_class->GetVTableEntry(vtable_index, linker->GetImagePointerSize()); +} + // Follow virtual/interface indirections if applicable. // Will throw null-pointer exception the if the object is null. template<InvokeType type, bool access_check> @@ -531,67 +591,7 @@ ALWAYS_INLINE ArtMethod* FindMethodToCall(uint32_t method_idx, return klass->GetVTableEntry(vtable_index, class_linker->GetImagePointerSize()); } case kSuper: { - // TODO This lookup is quite slow. - // NB This is actually quite tricky to do any other way. We cannot use GetDeclaringClass since - // that will actually not be what we want in some cases where there are miranda methods or - // defaults. What we actually need is a GetContainingClass that says which classes virtuals - // this method is coming from. - StackHandleScope<2> hs2(self); - HandleWrapperObjPtr<mirror::Object> h_this(hs2.NewHandleWrapper(this_object)); - Handle<mirror::Class> h_referring_class(hs2.NewHandle(referrer->GetDeclaringClass())); - const dex::TypeIndex method_type_idx = - referrer->GetDexFile()->GetMethodId(method_idx).class_idx_; - ObjPtr<mirror::Class> method_reference_class = - class_linker->ResolveType(method_type_idx, referrer); - if (UNLIKELY(method_reference_class == nullptr)) { - // Bad type idx. - CHECK(self->IsExceptionPending()); - return nullptr; - } else if (!method_reference_class->IsInterface()) { - // It is not an interface. If the referring class is in the class hierarchy of the - // referenced class in the bytecode, we use its super class. Otherwise, we throw - // a NoSuchMethodError. - ObjPtr<mirror::Class> super_class = nullptr; - if (method_reference_class->IsAssignableFrom(h_referring_class.Get())) { - super_class = h_referring_class->GetSuperClass(); - } - uint16_t vtable_index = resolved_method->GetMethodIndex(); - if (access_check) { - // Check existence of super class. - if (super_class == nullptr || - !super_class->HasVTable() || - vtable_index >= static_cast<uint32_t>(super_class->GetVTableLength())) { - // Behavior to agree with that of the verifier. - ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), - resolved_method->GetName(), resolved_method->GetSignature()); - return nullptr; // Failure. - } - } - DCHECK(super_class != nullptr); - DCHECK(super_class->HasVTable()); - return super_class->GetVTableEntry(vtable_index, class_linker->GetImagePointerSize()); - } else { - // It is an interface. - if (access_check) { - if (!method_reference_class->IsAssignableFrom(h_this->GetClass())) { - ThrowIncompatibleClassChangeErrorClassForInterfaceSuper(resolved_method, - method_reference_class, - h_this.Get(), - referrer); - return nullptr; // Failure. - } - } - // TODO We can do better than this for a (compiled) fastpath. - ArtMethod* result = method_reference_class->FindVirtualMethodForInterfaceSuper( - resolved_method, class_linker->GetImagePointerSize()); - // Throw an NSME if nullptr; - if (result == nullptr) { - ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), - resolved_method->GetName(), resolved_method->GetSignature()); - } - return result; - } - UNREACHABLE(); + return FindSuperMethodToCall<access_check>(method_idx, resolved_method, referrer, self); } case kInterface: { size_t imt_index = resolved_method->GetImtIndex(); @@ -670,100 +670,6 @@ EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface); #undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL #undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL -// Fast path field resolution that can't initialize classes or throw exceptions. -inline ArtField* FindFieldFast(uint32_t field_idx, ArtMethod* referrer, FindFieldType type, - size_t expected_size) { - ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtField* resolved_field = referrer->GetDexCache()->GetResolvedField(field_idx); - if (UNLIKELY(resolved_field == nullptr)) { - return nullptr; - } - // Check for incompatible class change. - const bool is_primitive = (type & FindFieldFlags::PrimitiveBit) != 0; - const bool is_set = (type & FindFieldFlags::WriteBit) != 0; - const bool is_static = (type & FindFieldFlags::StaticBit) != 0; - if (UNLIKELY(resolved_field->IsStatic() != is_static)) { - // Incompatible class change. - return nullptr; - } - ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass(); - if (is_static) { - // Check class is initialized else fail so that we can contend to initialize the class with - // other threads that may be racing to do this. - if (UNLIKELY(!fields_class->IsVisiblyInitialized())) { - return nullptr; - } - } - ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); - if (UNLIKELY(!referring_class->CanAccess(fields_class) || - !referring_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) || - (is_set && !resolved_field->CanBeChangedBy(referrer)))) { - // Illegal access. - return nullptr; - } - if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive || - resolved_field->FieldSize() != expected_size)) { - return nullptr; - } - return resolved_field; -} - -// Fast path method resolution that can't throw exceptions. -template <InvokeType type, bool access_check> -inline ArtMethod* FindMethodFast(uint32_t method_idx, - ObjPtr<mirror::Object> this_object, - ArtMethod* referrer) { - ScopedAssertNoThreadSuspension ants(__FUNCTION__); - if (UNLIKELY(this_object == nullptr && type != kStatic)) { - return nullptr; - } - ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); - ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache(); - constexpr ClassLinker::ResolveMode resolve_mode = access_check - ? ClassLinker::ResolveMode::kCheckICCEAndIAE - : ClassLinker::ResolveMode::kNoChecks; - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - ArtMethod* resolved_method = linker->GetResolvedMethod<type, resolve_mode>(method_idx, referrer); - if (UNLIKELY(resolved_method == nullptr)) { - return nullptr; - } - if (type == kInterface) { // Most common form of slow path dispatch. - return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method, - kRuntimePointerSize); - } else if (type == kStatic || type == kDirect) { - return resolved_method; - } else if (type == kSuper) { - // TODO This lookup is rather slow. - dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; - ObjPtr<mirror::Class> method_reference_class = linker->LookupResolvedType( - method_type_idx, dex_cache, referrer->GetClassLoader()); - if (method_reference_class == nullptr) { - // Need to do full type resolution... - return nullptr; - } else if (!method_reference_class->IsInterface()) { - // It is not an interface. If the referring class is in the class hierarchy of the - // referenced class in the bytecode, we use its super class. Otherwise, we cannot - // resolve the method. - if (!method_reference_class->IsAssignableFrom(referring_class)) { - return nullptr; - } - ObjPtr<mirror::Class> super_class = referring_class->GetSuperClass(); - if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) { - // The super class does not have the method. - return nullptr; - } - return super_class->GetVTableEntry(resolved_method->GetMethodIndex(), kRuntimePointerSize); - } else { - return method_reference_class->FindVirtualMethodForInterfaceSuper( - resolved_method, kRuntimePointerSize); - } - } else { - DCHECK(type == kVirtual); - return this_object->GetClass()->GetVTableEntry( - resolved_method->GetMethodIndex(), kRuntimePointerSize); - } -} - inline ObjPtr<mirror::Class> ResolveVerifyAndClinit(dex::TypeIndex type_idx, ArtMethod* referrer, Thread* self, diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 4731a867d2..5faf387093 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -128,6 +128,13 @@ enum FindFieldType { StaticPrimitiveWrite = StaticBit | PrimitiveBit | WriteBit, }; +template<bool access_check> +inline ArtMethod* FindSuperMethodToCall(uint32_t method_idx, + ArtMethod* resolved_method, + ArtMethod* referrer, + Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_); + template<FindFieldType type, bool access_check> inline ArtField* FindFieldFromCode(uint32_t field_idx, ArtMethod* referrer, @@ -144,20 +151,6 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -// Fast path field resolution that can't initialize classes or throw exceptions. -inline ArtField* FindFieldFast(uint32_t field_idx, - ArtMethod* referrer, - FindFieldType type, - size_t expected_size) - REQUIRES_SHARED(Locks::mutator_lock_); - -// Fast path method resolution that can't throw exceptions. -template <InvokeType type, bool access_check> -inline ArtMethod* FindMethodFast(uint32_t method_idx, - ObjPtr<mirror::Object> this_object, - ArtMethod* referrer) - REQUIRES_SHARED(Locks::mutator_lock_); - inline ObjPtr<mirror::Class> ResolveVerifyAndClinit(dex::TypeIndex type_idx, ArtMethod* referrer, Thread* self, diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc index bfe015ffc2..618f7636da 100644 --- a/runtime/entrypoints/quick/quick_field_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc @@ -28,6 +28,48 @@ namespace art { +// Fast path field resolution that can't initialize classes or throw exceptions. +inline ArtField* FindFieldFast(uint32_t field_idx, + ArtMethod* referrer, + FindFieldType type, + size_t expected_size) + REQUIRES(!Roles::uninterruptible_) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedAssertNoThreadSuspension ants(__FUNCTION__); + ArtField* resolved_field = referrer->GetDexCache()->GetResolvedField(field_idx); + if (UNLIKELY(resolved_field == nullptr)) { + return nullptr; + } + // Check for incompatible class change. + const bool is_primitive = (type & FindFieldFlags::PrimitiveBit) != 0; + const bool is_set = (type & FindFieldFlags::WriteBit) != 0; + const bool is_static = (type & FindFieldFlags::StaticBit) != 0; + if (UNLIKELY(resolved_field->IsStatic() != is_static)) { + // Incompatible class change. + return nullptr; + } + ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass(); + if (is_static) { + // Check class is initialized else fail so that we can contend to initialize the class with + // other threads that may be racing to do this. + if (UNLIKELY(!fields_class->IsVisiblyInitialized())) { + return nullptr; + } + } + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); + if (UNLIKELY(!referring_class->CanAccess(fields_class) || + !referring_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) || + (is_set && !resolved_field->CanBeChangedBy(referrer)))) { + // Illegal access. + return nullptr; + } + if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive || + resolved_field->FieldSize() != expected_size)) { + return nullptr; + } + return resolved_field; +} + // Helper function to do a null check after trying to resolve the field. Not for statics since obj // does not exist there. There is a suspend check, object is a double pointer to update the value // in the caller in case it moves. diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 926d534496..b5bd6efc47 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2198,13 +2198,75 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, return GenericJniMethodEnd(self, cookie, result, result_f, called); } +// Fast path method resolution that can't throw exceptions. +template <InvokeType type> +inline ArtMethod* FindMethodFast(uint32_t method_idx, + ObjPtr<mirror::Object> this_object, + ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_) { + ScopedAssertNoThreadSuspension ants(__FUNCTION__); + if (UNLIKELY(this_object == nullptr && type != kStatic)) { + return nullptr; + } + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); + ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache(); + constexpr ClassLinker::ResolveMode resolve_mode = ClassLinker::ResolveMode::kCheckICCEAndIAE; + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + ArtMethod* resolved_method = linker->GetResolvedMethod<type, resolve_mode>(method_idx, referrer); + if (UNLIKELY(resolved_method == nullptr)) { + return nullptr; + } + if (type == kInterface) { // Most common form of slow path dispatch. + return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method, + kRuntimePointerSize); + } + if (type == kStatic || type == kDirect) { + return resolved_method; + } + + if (type == kSuper) { + // TODO This lookup is rather slow. + dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; + ObjPtr<mirror::Class> method_reference_class = linker->LookupResolvedType( + method_type_idx, dex_cache, referrer->GetClassLoader()); + if (method_reference_class == nullptr) { + // Need to do full type resolution... + return nullptr; + } + + // If the referring class is in the class hierarchy of the + // referenced class in the bytecode, we use its super class. Otherwise, we cannot + // resolve the method. + if (!method_reference_class->IsAssignableFrom(referring_class)) { + return nullptr; + } + + if (method_reference_class->IsInterface()) { + return method_reference_class->FindVirtualMethodForInterfaceSuper( + resolved_method, kRuntimePointerSize); + } + + ObjPtr<mirror::Class> super_class = referring_class->GetSuperClass(); + if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) { + // The super class does not have the method. + return nullptr; + } + return super_class->GetVTableEntry(resolved_method->GetMethodIndex(), kRuntimePointerSize); + } + + DCHECK(type == kVirtual); + return this_object->GetClass()->GetVTableEntry( + resolved_method->GetMethodIndex(), kRuntimePointerSize); +} + // We use TwoWordReturn to optimize scalar returns. We use the hi value for code, and the lo value // for the method pointer. // // It is valid to use this, as at the usage points here (returns from C functions) we are assuming // to hold the mutator lock (see REQUIRES_SHARED(Locks::mutator_lock_) annotations). -template <InvokeType type, bool access_check> +template <InvokeType type> static TwoWordReturn artInvokeCommon(uint32_t method_idx, ObjPtr<mirror::Object> this_object, Thread* self, @@ -2212,7 +2274,7 @@ static TwoWordReturn artInvokeCommon(uint32_t method_idx, ScopedQuickEntrypointChecks sqec(self); DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs)); ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp); - ArtMethod* method = FindMethodFast<type, access_check>(method_idx, this_object, caller_method); + ArtMethod* method = FindMethodFast<type>(method_idx, this_object, caller_method); if (UNLIKELY(method == nullptr)) { const DexFile* dex_file = caller_method->GetDexFile(); uint32_t shorty_len; @@ -2222,10 +2284,8 @@ static TwoWordReturn artInvokeCommon(uint32_t method_idx, ScopedObjectAccessUnchecked soa(self->GetJniEnv()); RememberForGcArgumentVisitor visitor(sp, type == kStatic, shorty, shorty_len, &soa); visitor.VisitArguments(); - method = FindMethodFromCode<type, access_check>(method_idx, - &this_object, - caller_method, - self); + method = FindMethodFromCode<type, /*access_check=*/true>( + method_idx, &this_object, caller_method, self); visitor.FixupReferences(); } @@ -2247,34 +2307,29 @@ static TwoWordReturn artInvokeCommon(uint32_t method_idx, } // Explicit artInvokeCommon template function declarations to please analysis tool. -#define EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(type, access_check) \ - template REQUIRES_SHARED(Locks::mutator_lock_) \ - TwoWordReturn artInvokeCommon<type, access_check>( \ +#define EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(type) \ + template REQUIRES_SHARED(Locks::mutator_lock_) \ + TwoWordReturn artInvokeCommon<type>( \ uint32_t method_idx, ObjPtr<mirror::Object> his_object, Thread* self, ArtMethod** sp) -EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kVirtual, false); -EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kVirtual, true); -EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kInterface, false); -EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kInterface, true); -EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kDirect, false); -EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kDirect, true); -EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kStatic, false); -EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kStatic, true); -EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kSuper, false); -EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kSuper, true); +EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kVirtual); +EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kInterface); +EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kDirect); +EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kStatic); +EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kSuper); #undef EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL // See comments in runtime_support_asm.S extern "C" TwoWordReturn artInvokeInterfaceTrampolineWithAccessCheck( uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { - return artInvokeCommon<kInterface, true>(method_idx, this_object, self, sp); + return artInvokeCommon<kInterface>(method_idx, this_object, self, sp); } extern "C" TwoWordReturn artInvokeDirectTrampolineWithAccessCheck( uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { - return artInvokeCommon<kDirect, true>(method_idx, this_object, self, sp); + return artInvokeCommon<kDirect>(method_idx, this_object, self, sp); } extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck( @@ -2284,19 +2339,19 @@ extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck( ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { // For static, this_object is not required and may be random garbage. Don't pass it down so that // it doesn't cause ObjPtr alignment failure check. - return artInvokeCommon<kStatic, true>(method_idx, nullptr, self, sp); + return artInvokeCommon<kStatic>(method_idx, nullptr, self, sp); } extern "C" TwoWordReturn artInvokeSuperTrampolineWithAccessCheck( uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { - return artInvokeCommon<kSuper, true>(method_idx, this_object, self, sp); + return artInvokeCommon<kSuper>(method_idx, this_object, self, sp); } extern "C" TwoWordReturn artInvokeVirtualTrampolineWithAccessCheck( uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { - return artInvokeCommon<kVirtual, true>(method_idx, this_object, self, sp); + return artInvokeCommon<kVirtual>(method_idx, this_object, self, sp); } // Determine target of interface dispatch. The interface method and this object are known non-null. diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc index 7719b5f363..34c971c109 100644 --- a/runtime/interpreter/mterp/nterp.cc +++ b/runtime/interpreter/mterp/nterp.cc @@ -330,43 +330,14 @@ extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, uint16_t* dex_ return 0; } - // ResolveMethod returns the method based on the method_id. For super invokes - // we must use the executing class's context to find the right method. if (invoke_type == kSuper) { - ObjPtr<mirror::Class> executing_class = caller->GetDeclaringClass(); - ObjPtr<mirror::Class> referenced_class = class_linker->LookupResolvedType( - executing_class->GetDexFile().GetMethodId(method_index).class_idx_, - executing_class->GetDexCache(), - executing_class->GetClassLoader()); - DCHECK(referenced_class != nullptr); // We have already resolved a method from this class. - if (!referenced_class->IsAssignableFrom(executing_class)) { - // We cannot determine the target method. - ThrowNoSuchMethodError(invoke_type, - resolved_method->GetDeclaringClass(), - resolved_method->GetName(), - resolved_method->GetSignature()); + resolved_method = caller->SkipAccessChecks() + ? FindSuperMethodToCall</*access_check=*/false>(method_index, resolved_method, caller, self) + : FindSuperMethodToCall</*access_check=*/true>(method_index, resolved_method, caller, self); + if (resolved_method == nullptr) { + DCHECK(self->IsExceptionPending()); return 0; } - if (referenced_class->IsInterface()) { - resolved_method = referenced_class->FindVirtualMethodForInterfaceSuper( - resolved_method, class_linker->GetImagePointerSize()); - } else { - uint16_t vtable_index = resolved_method->GetMethodIndex(); - ObjPtr<mirror::Class> super_class = executing_class->GetSuperClass(); - if (super_class == nullptr || - !super_class->HasVTable() || - vtable_index >= static_cast<uint32_t>(super_class->GetVTableLength())) { - // Behavior to agree with that of the verifier. - ThrowNoSuchMethodError(invoke_type, - resolved_method->GetDeclaringClass(), - resolved_method->GetName(), - resolved_method->GetSignature()); - return 0; - } else { - resolved_method = executing_class->GetSuperClass()->GetVTableEntry( - vtable_index, class_linker->GetImagePointerSize()); - } - } } if (invoke_type == kInterface) { |