Treat non-lambda metafactory argument method handles specially.
They can reach MethodHandle.invokeExact calls which match exact
method signatures. Therefore, we cannot perform member rebinding
on the methods in method handles because that could (and likely
would) change the receiver type making the matching fail.
Therefore, we keep the original receiver type for all such
method handles. That also means that we need to keep those
receiver types in the output and cannot perform merging or
other operations that would make those types disappear.
R=christofferqa@google.com, sgjesse@google.com, tamaskenez@google.com
Change-Id: Ic31373f574493b6941acca176f39decf0ef12182
diff --git a/src/test/examplesAndroidO/invokecustom/InvokeCustom.java b/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
index 449d426..64405bb 100644
--- a/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
+++ b/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
@@ -9,6 +9,20 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+class ClassWithLongName {
+ @Override
+ public String toString() {
+ return getClass().getName();
+ }
+}
+
+class AnotherClassWithALongName {
+ @Override
+ public String toString() {
+ return getClass().getName();
+ }
+}
+
interface J {
default void targetMethodTest8() {
@@ -38,6 +52,11 @@
default void targetMethodTest10() {
System.out.println("targetMethodTest10 from I");
}
+
+ default AnotherClassWithALongName targetMethodTest11(ClassWithLongName a) {
+ System.out.println("targetMethodTest11 from I");
+ return new AnotherClassWithALongName();
+ }
}
abstract class Super {
diff --git a/src/test/examplesAndroidO/invokecustom/TestGenerator.java b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
index e42b9c9..05a6f28 100644
--- a/src/test/examplesAndroidO/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
@@ -57,6 +57,7 @@
generateMethodTest11(cw);
generateMethodTest12(cw);
generateMethodTest13(cw);
+ generateMethodTest14(cw);
generateMethodMain(cw);
super.visitEnd();
}
@@ -97,6 +98,8 @@
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test12", "()V", false);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test13", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test14", "()V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
}
@@ -424,4 +427,43 @@
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
}
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+ * MethodHandle of kind invoke instance taking an argument and returning a result. This should
+ * work through renaming.
+ */
+ private void generateMethodTest14(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test14", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, MethodHandle.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(ClassWithLongName.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL,
+ Type.getInternalName(ClassWithLongName.class),
+ "<init>",
+ "()V",
+ false);
+ mv.visitInvokeDynamicInsn(
+ "targetMethodTest11",
+ "(Linvokecustom/InvokeCustom;Linvokecustom/ClassWithLongName;)"
+ + "Linvokecustom/AnotherClassWithALongName;",
+ bootstrap,
+ new Handle(
+ Opcodes.H_INVOKEVIRTUAL,
+ Type.getInternalName(InvokeCustom.class),
+ "targetMethodTest11",
+ "(Linvokecustom/ClassWithLongName;)Linvokecustom/AnotherClassWithALongName;",
+ false));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
}
diff --git a/src/test/examplesAndroidO/invokecustom/keep-rules.txt b/src/test/examplesAndroidO/invokecustom/keep-rules.txt
index bcefc4b..54a5682 100644
--- a/src/test/examplesAndroidO/invokecustom/keep-rules.txt
+++ b/src/test/examplesAndroidO/invokecustom/keep-rules.txt
@@ -8,7 +8,7 @@
public static void main(...);
}
--keepclasseswithmembers class * {
+-keepclassmembers class * {
*** targetMethodTest*(...);
}
diff --git a/src/test/examplesAndroidO/invokecustom2/keep-rules.txt b/src/test/examplesAndroidO/invokecustom2/keep-rules.txt
new file mode 100644
index 0000000..629ec41
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom2/keep-rules.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point and the methods that are targets for invoke custom invocations.
+# Get rid of everything that is not reachable from there.
+-keep public class invokecustom2.InvokeCustom {
+ public static void main(...);
+}
+
+-keepclassmembers class * {
+ *** targetMethodTest*(...);
+}
+
+-allowaccessmodification
diff --git a/src/test/examplesAndroidP/invokecustom/InvokeCustom.java b/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
index e8447a3..c3cb86f 100644
--- a/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
+++ b/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
@@ -9,6 +9,41 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+class ArgumentType {
+}
+
+class ReturnType {
+}
+
+interface I {
+ default ReturnType targetMethodTest4(ArgumentType arg) {
+ System.out.println("I.targetMethodTest4");
+ return new ReturnType();
+ }
+}
+
+class Middle implements I {
+}
+
+class Sub extends Middle {
+}
+
+interface I2 {
+ default ReturnType targetMethodTest5(ArgumentType arg) {
+ System.out.println("I2.targetMethodTest5");
+ return new ReturnType();
+ }
+}
+
+// TODO(116283747): Add the same test where the interface method is not overriden but inherited
+// from the interface. Currently, that works on the reference implementation but fails on Art.
+class Impl implements I2 {
+ @Override
+ public ReturnType targetMethodTest5(ArgumentType arg) {
+ System.out.println("Impl.targetMethodTest5");
+ return new ReturnType();
+ }
+}
public class InvokeCustom {
@@ -35,4 +70,12 @@
final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
return new ConstantCallSite(targetMH.asType(type));
}
+
+ public static void doInvokeSubWithArg(MethodHandle handle) throws Throwable {
+ handle.invoke(new Sub(), new ArgumentType());
+ }
+
+ public static void doInvokeExactImplWithArg(MethodHandle handle) throws Throwable {
+ ReturnType result = (ReturnType) handle.invokeExact(new Impl(), new ArgumentType());
+ }
}
diff --git a/src/test/examplesAndroidP/invokecustom/TestGenerator.java b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
index 7ee4bdc..495f080 100644
--- a/src/test/examplesAndroidP/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
@@ -46,6 +46,8 @@
public void visitEnd() {
generateMethodTest1(cw);
generateMethodTest2(cw);
+ generateMethodTest3(cw);
+ generateMethodTest4(cw);
generateMethodMain(cw);
super.visitEnd();
}
@@ -64,20 +66,24 @@
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test1", "()V", false);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test2", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test3", "()V", false);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test4", "()V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
}
/**
- * Generate test with an invokedynamic, a static bootstrap method without extra args and
- * args to the target method.
+ * Generate test with an invokedynamic, a static bootstrap method without extra args and
+ * args to the target method.
*/
private void generateMethodTest1(ClassVisitor cv) {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test1", "()V",
null, null);
MethodType mt = MethodType.methodType(
CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
- Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
"bsmLookupStatic", mt.toMethodDescriptorString(), false);
mv.visitLdcInsn(new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
"targetMethodTest1", "()V", false));
@@ -91,15 +97,15 @@
}
/**
- * Generate test with an invokedynamic, a static bootstrap method without extra args and
- * args to the target method.
+ * Generate test with an invokedynamic, a static bootstrap method without extra args and
+ * args to the target method.
*/
private void generateMethodTest2(ClassVisitor cv) {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2", "()V",
null, null);
MethodType mt = MethodType.methodType(
CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
- Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
"bsmLookupStatic", mt.toMethodDescriptorString(), false);
mv.visitLdcInsn(Type.getMethodType("(ZBSCIFJDLjava/lang/String;)Ljava/lang/Object;"));
mv.visitInvokeDynamicInsn("targetMethodTest3", "(Ljava/lang/invoke/MethodType;)V",
@@ -107,4 +113,59 @@
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
}
+
+ /**
+ * Generate test with a const method handle pointing to the middle of a class hierarchy.
+ * Call a static method with the method handle which will do a MethodHandle.invoke call on
+ * a sub class instance.
+ *
+ * Tests that the const method handle is rewritten when renaming. Also tests that the
+ * middle class does not disappear (for instance via class merging).
+ */
+ private void generateMethodTest3(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test3", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(ReturnType.class, ArgumentType.class);
+ Handle invokeWithArg = new Handle(
+ Opcodes.H_INVOKEVIRTUAL,
+ Type.getInternalName(Middle.class),
+ "targetMethodTest4",
+ mt.toMethodDescriptorString(),
+ false);
+ mv.visitLdcInsn(invokeWithArg);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ Type.getInternalName(InvokeCustom.class),
+ "doInvokeSubWithArg",
+ "(Ljava/lang/invoke/MethodHandle;)V",
+ false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with a const method handle pointing to a class which inherits the method from
+ * the super class. Call a static method with the method handle which will do a
+ * MethodHandle.invokeExact.
+ */
+ private void generateMethodTest4(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test4", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(ReturnType.class, ArgumentType.class);
+ Handle invokeExactWithArg = new Handle(
+ Opcodes.H_INVOKEVIRTUAL,
+ Type.getInternalName(Impl.class),
+ "targetMethodTest5",
+ mt.toMethodDescriptorString(),
+ false);
+ mv.visitLdcInsn(invokeExactWithArg);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ Type.getInternalName(InvokeCustom.class),
+ "doInvokeExactImplWithArg",
+ "(Ljava/lang/invoke/MethodHandle;)V",
+ false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
}
diff --git a/src/test/examplesAndroidP/invokecustom/keep-rules.txt b/src/test/examplesAndroidP/invokecustom/keep-rules.txt
index dbc21fc..b4998ec 100644
--- a/src/test/examplesAndroidP/invokecustom/keep-rules.txt
+++ b/src/test/examplesAndroidP/invokecustom/keep-rules.txt
@@ -8,7 +8,7 @@
public static void main(...);
}
--keepclasseswithmembers class * {
+-keepclassmembers class * {
*** targetMethodTest*(...);
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 71f4559..e47a430 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -90,6 +90,16 @@
.run();
}
+ @Test
+ public void invokeCustom2WithShrinking() throws Throwable {
+ test("invokecustom2-with-shrinking", "invokecustom2", "InvokeCustom")
+ .withMinApiLevel(AndroidApiLevel.O)
+ .withBuilderTransformation(builder ->
+ builder.addProguardConfigurationFiles(
+ Paths.get(ToolHelper.EXAMPLES_ANDROID_O_DIR, "invokecustom2/keep-rules.txt")))
+ .run();
+ }
+
@Override
@Test
public void lambdaDesugaring() throws Throwable {