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
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index f8bf126..aabc433 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 @@
   // 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 853ded0..c6e7cfb 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -257,8 +257,10 @@
 
 // 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 @@
                  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 @@
                  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 832eac6..3a723f7 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -134,12 +134,6 @@
                                        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 b36c5c3..4ee1013 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -486,6 +486,66 @@
 #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 @@
       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 @@
 #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 4731a86..5faf387 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -128,6 +128,13 @@
   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 @@
     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 bfe015f..618f763 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 926d534..b5bd6ef 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2198,13 +2198,75 @@
   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 @@
   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 @@
       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 @@
 }
 
 // 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 @@
     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 7719b5f..34c971c 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -330,43 +330,14 @@
     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) {