ART: Fix MethodHandle invoke-super
Adds test for MethodHandles.unreflectSpecial() and fixes the
associated method refinement in the method handle dispatch.
Bug: 77729852
Test: art/test/run-test --host --64 956
(cherry picked from commit d7959c2abe72629581f7465500b95a3d4e57e7ca)
Change-Id: Idf022997a3d34b75428cac0bc539f6ea4bb1bc94
Merged-In: I6c364a5a6edaa9e9d04ae96683d5bb3e6adbcb6e
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 82370c4..64ab789 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -674,13 +674,15 @@
return WellKnownClasses::StringInitToStringFactory(target_method);
}
} else if (handle_kind == mirror::MethodHandle::Kind::kInvokeSuper) {
- ObjPtr<mirror::Class> declaring_class = target_method->GetDeclaringClass();
-
// Note that we're not dynamically dispatching on the type of the receiver
// here. We use the static type of the "receiver" object that we've
// recorded in the method handle's type, which will be the same as the
// special caller that was specified at the point of lookup.
ObjPtr<mirror::Class> referrer_class = handle_type->GetPTypes()->Get(0);
+ ObjPtr<mirror::Class> declaring_class = target_method->GetDeclaringClass();
+ if (referrer_class == declaring_class) {
+ return target_method;
+ }
if (!declaring_class->IsInterface()) {
ObjPtr<mirror::Class> super_class = referrer_class->GetSuperClass();
uint16_t vtable_index = target_method->GetMethodIndex();
@@ -690,8 +692,6 @@
// will always be declared by super_class (or one of its super classes).
DCHECK_LT(vtable_index, super_class->GetVTableLength());
return super_class->GetVTableEntry(vtable_index, kRuntimePointerSize);
- } else {
- return referrer_class->FindVirtualMethodForInterfaceSuper(target_method, kRuntimePointerSize);
}
}
return target_method;
diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt
index 9b09327..6954c22 100644
--- a/test/956-methodhandles/expected.txt
+++ b/test/956-methodhandles/expected.txt
@@ -3,7 +3,17 @@
foo_A
foo_B
privateRyan_D
-Received exception: Expected (java.lang.String, java.lang.String)java.lang.String but was (java.lang.String, java.lang.Object)void
+Received WrongMethodTypeException exception
+G.sayHi()
+G.sayHi()
+G.sayHi()
+F.sayHi()
+F.sayHi()
+H.chatter()
+H.chatter()
+H.chatter()
+Chatty.chatter()
+Chatty.chatter()
String constructors done.
testReferenceReturnValueConversions done.
testPrimitiveReturnValueConversions done.
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index 1ddef03..dee818a 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -30,6 +30,8 @@
import java.util.Arrays;
import java.util.List;
+import other.Chatty;
+
public class Main {
public static class A {
@@ -66,6 +68,30 @@
public static final Lookup lookup = MethodHandles.lookup();
}
+ private interface F {
+ public default void sayHi() {
+ System.out.println("F.sayHi()");
+ }
+ }
+
+ public static class G implements F {
+ public void sayHi() {
+ System.out.println("G.sayHi()");
+ }
+ public MethodHandles.Lookup getLookup() {
+ return MethodHandles.lookup();
+ }
+ }
+
+ public static class H implements Chatty {
+ public void chatter() {
+ System.out.println("H.chatter()");
+ }
+ public MethodHandles.Lookup getLookup() {
+ return MethodHandles.lookup();
+ }
+ }
+
public static void main(String[] args) throws Throwable {
testfindSpecial_invokeSuperBehaviour();
testfindSpecial_invokeDirectBehaviour();
@@ -173,7 +199,7 @@
handle.invokeExact("a", new Object());
System.out.println("invokeExact(\"a\", new Object()) unexpectedly succeeded.");
} catch (WrongMethodTypeException ex) {
- System.out.println("Received exception: " + ex.getMessage());
+ System.out.println("Received WrongMethodTypeException exception");
}
}
@@ -528,6 +554,34 @@
privateStaticField.set(null, "updatedStaticValue");
mh.invokeExact("updatedStaticValue2");
assertEquals("updatedStaticValue2", (String) privateStaticField.get(null));
+
+ // unreflectSpecial testing - F is an interface that G implements
+
+ G g = new G();
+ g.sayHi(); // prints "G.sayHi()"
+
+ MethodHandles.Lookup lookupInG = g.getLookup();
+ Method methodInG = G.class.getDeclaredMethod("sayHi");
+ lookupInG.unreflectSpecial(methodInG, G.class).invoke(g); // prints "G.sayHi()"
+
+ Method methodInF = F.class.getDeclaredMethod("sayHi");
+ lookupInG.unreflect(methodInF).invoke(g); // prints "G.sayHi()"
+ lookupInG.in(G.class).unreflectSpecial(methodInF, G.class).invoke(g); // prints "F.sayHi()"
+ lookupInG.unreflectSpecial(methodInF, G.class).bindTo(g).invokeWithArguments();
+
+ // unreflectSpecial testing - other.Chatty is an interface that H implements
+
+ H h = new H();
+ h.chatter();
+
+ MethodHandles.Lookup lookupInH = h.getLookup();
+ Method methodInH = H.class.getDeclaredMethod("chatter");
+ lookupInH.unreflectSpecial(methodInH, H.class).invoke(h);
+
+ Method methodInChatty = Chatty.class.getDeclaredMethod("chatter");
+ lookupInH.unreflect(methodInChatty).invoke(h);
+ lookupInH.in(H.class).unreflectSpecial(methodInChatty, H.class).invoke(h);
+ lookupInH.unreflectSpecial(methodInChatty, H.class).bindTo(h).invokeWithArguments();
}
// This method only exists to fool Jack's handling of types. See b/32536744.
diff --git a/test/956-methodhandles/src/other/Chatty.java b/test/956-methodhandles/src/other/Chatty.java
new file mode 100644
index 0000000..98aef14
--- /dev/null
+++ b/test/956-methodhandles/src/other/Chatty.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+public interface Chatty {
+ public default void chatter() {
+ System.out.println("Chatty.chatter()");
+ }
+}