ART: Fix const-method-handle method resolution

The MethodType associated with the const-method-handle should refer to
the method handle's target class, not the declaring class of the
method.

Bug: 115964401
Test: art/test.py --host -r -t 979
Test: art/test.py --host -r -t 714

Change-Id: I5b8f2bb64882a708ade51403f0a1f07e0c102dc2
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index befeea4..b8ce1f9 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8474,7 +8474,7 @@
   target_method->GetShorty(&shorty_length);
   int32_t num_params = static_cast<int32_t>(shorty_length + receiver_count - 1);
 
-  StackHandleScope<7> hs(self);
+  StackHandleScope<5> hs(self);
   ObjPtr<mirror::Class> array_of_class = GetClassRoot<mirror::ObjectArray<mirror::Class>>(this);
   Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
       mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_params)));
@@ -8483,20 +8483,25 @@
     return nullptr;
   }
 
+  const DexFile* dex_file = referrer->GetDexFile();
+  const DexFile::MethodId& method_id = dex_file->GetMethodId(method_handle.field_or_method_idx_);
   int32_t index = 0;
   if (receiver_count != 0) {
-    // Insert receiver
-    method_params->Set(index++, target_method->GetDeclaringClass());
+    // Insert receiver. Use the class identified in the method handle rather than the declaring
+    // class of the resolved method which may be super class or default interface method
+    // (b/115964401).
+    ObjPtr<mirror::Class> receiver_class = LookupResolvedType(method_id.class_idx_, referrer);
+    // receiver_class should have been resolved when resolving the target method.
+    DCHECK(receiver_class != nullptr);
+    method_params->Set(index++, receiver_class);
   }
-  DexFileParameterIterator it(*target_method->GetDexFile(), target_method->GetPrototype());
-  Handle<mirror::DexCache> target_method_dex_cache(hs.NewHandle(target_method->GetDexCache()));
-  Handle<mirror::ClassLoader> target_method_class_loader(hs.NewHandle(target_method->GetClassLoader()));
+
+  const DexFile::ProtoId& proto_id = dex_file->GetProtoId(method_id.proto_idx_);
+  DexFileParameterIterator it(*dex_file, proto_id);
   while (it.HasNext()) {
     DCHECK_LT(index, num_params);
     const dex::TypeIndex type_idx = it.GetTypeIdx();
-    ObjPtr<mirror::Class> klass = ResolveType(type_idx,
-                                              target_method_dex_cache,
-                                              target_method_class_loader);
+    ObjPtr<mirror::Class> klass = ResolveType(type_idx, referrer);
     if (nullptr == klass) {
       DCHECK(self->IsExceptionPending());
       return nullptr;
@@ -8505,7 +8510,8 @@
     it.Next();
   }
 
-  Handle<mirror::Class> return_type = hs.NewHandle(target_method->ResolveReturnType());
+  Handle<mirror::Class> return_type =
+      hs.NewHandle(ResolveType(proto_id.return_type_idx_, referrer));
   if (UNLIKELY(return_type.IsNull())) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt
index bbaaedb..8531709 100644
--- a/test/979-const-method-handle/expected.txt
+++ b/test/979-const-method-handle/expected.txt
@@ -7,3 +7,11 @@
 2.718281828459045
 repeatConstMethodHandle()
 Attempting to set Math.E raised IAE
+Quack
+Moo
+Woof
+Test
+Getting field in TestTokenizer raised WMTE (woohoo!)
+Stack: tos was 7
+Stack: capacity was 10
+Stack: capacity is 2
diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java
index 427ca7a..5368a22 100644
--- a/test/979-const-method-handle/src/Main.java
+++ b/test/979-const-method-handle/src/Main.java
@@ -18,6 +18,11 @@
 import annotations.ConstantMethodType;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+
+import java.io.StreamTokenizer;
+import java.io.StringReader;
+import java.util.Stack;
 
 class Main {
     /**
@@ -45,6 +50,12 @@
         private int field;
     }
 
+    private static class TestTokenizer extends StreamTokenizer {
+        public TestTokenizer(String message) {
+            super(new StringReader(message));
+        }
+    }
+
     @ConstantMethodType(
             returnType = String.class,
             parameterTypes = {int.class, Integer.class, System.class})
@@ -136,6 +147,48 @@
         return null;
     }
 
+    @ConstantMethodHandle(
+        kind = ConstantMethodHandle.INSTANCE_GET,
+        owner = "java/io/StreamTokenizer",
+        fieldOrMethodName = "sval",
+        descriptor = "Ljava/lang/String;")
+     private static MethodHandle getSval() {
+        unreachable();
+        return null;
+    }
+
+    // This constant-method-handle references a private instance field. If
+    // referenced in bytecode it raises IAE at load time.
+    @ConstantMethodHandle(
+        kind = ConstantMethodHandle.INSTANCE_PUT,
+        owner = "java/io/StreamTokenizer",
+        fieldOrMethodName = "peekc",
+        descriptor = "I")
+     private static MethodHandle putPeekc() {
+        unreachable();
+        return null;
+    }
+
+    @ConstantMethodHandle(
+        kind = ConstantMethodHandle.INVOKE_VIRTUAL,
+        owner = "java/util/Stack",
+        fieldOrMethodName = "pop",
+        descriptor = "()Ljava/lang/Object;")
+    private static MethodHandle stackPop() {
+        unreachable();
+        return null;
+    }
+
+    @ConstantMethodHandle(
+        kind = ConstantMethodHandle.INVOKE_VIRTUAL,
+        owner = "java/util/Stack",
+        fieldOrMethodName = "trimToSize",
+        descriptor = "()V")
+    private static MethodHandle stackTrim() {
+        unreachable();
+        return null;
+    }
+
     private static void repeatConstMethodHandle() throws Throwable {
         System.out.println("repeatConstMethodHandle()");
         String[] values = {"A", "B", "C"};
@@ -166,5 +219,29 @@
         } catch (IllegalAccessError expected) {
             System.out.println("Attempting to set Math.E raised IAE");
         }
+
+        StreamTokenizer st = new StreamTokenizer(new StringReader("Quack Moo Woof"));
+        while (st.nextToken() != StreamTokenizer.TT_EOF) {
+            System.out.println((String) getSval().invokeExact(st));
+        }
+
+        TestTokenizer tt = new TestTokenizer("Test message 123");
+        tt.nextToken();
+        System.out.println((String) getSval().invoke(tt));
+        try {
+            System.out.println((String) getSval().invokeExact(tt));
+        } catch (WrongMethodTypeException wmte) {
+            System.out.println("Getting field in TestTokenizer raised WMTE (woohoo!)");
+        }
+
+        Stack stack = new Stack();
+        stack.push(Integer.valueOf(3));
+        stack.push(Integer.valueOf(5));
+        stack.push(Integer.valueOf(7));
+        Object tos = stackPop().invokeExact(stack);
+        System.out.println("Stack: tos was " + tos);
+        System.out.println("Stack: capacity was " + stack.capacity());
+        stackTrim().invokeExact(stack);
+        System.out.println("Stack: capacity is " + stack.capacity());
     }
 }