Align method handle type with the runtime
- Remove temporary hack in invoke-custom test due to runtime bug
since an updated runtime fix it.
Change-Id: I5424bacf7ca0e292974df7e81b35dea51d8a9b51
diff --git a/src/test/examplesAndroidO/invokecustom2/TestGenerator.java b/src/test/examplesAndroidO/invokecustom2/TestGenerator.java
new file mode 100644
index 0000000..e9cbf7e
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom2/TestGenerator.java
@@ -0,0 +1,343 @@
+// Copyright (c) 2017, 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.
+
+package invokecustom2;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class TestGenerator {
+
+ private final Path classNamePath;
+
+ public static void main(String[] args) throws IOException {
+ assert args.length == 1;
+ TestGenerator testGenerator = new TestGenerator(Paths.get(args[0],
+ TestGenerator.class.getPackage().getName(), InvokeCustom.class.getSimpleName() + ".class"));
+ testGenerator.generateTests();
+ }
+
+ public TestGenerator(Path classNamePath) {
+ this.classNamePath = classNamePath;
+ }
+
+ private void generateTests() throws IOException {
+ ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile()));
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ cr.accept(
+ new ClassVisitor(Opcodes.ASM5, cw) {
+ @Override
+ public void visitEnd() {
+ generateMethodTest1(cw);
+ generateMethodTest2(cw);
+ generateMethodTest3(cw);
+ generateMethodTest4(cw);
+ generateMethodTest5(cw);
+ generateMethodTest6(cw);
+ generateMethodTest7(cw);
+ generateMethodTest8(cw);
+ generateMethodTest9(cw);
+ generateMethodMain(cw);
+ super.visitEnd();
+ }
+ }, 0);
+ new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray());
+ }
+
+ /* generate main method that only call all test methods. */
+ private void generateMethodMain(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC,
+ "main", "([Ljava/lang/String;)V", null, null);
+ String internalName = Type.getInternalName(InvokeCustom.class);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test1", "()V", false);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test2", "()V", false);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test3", "()V", false);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test4", "()V", false);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test5", "()V", false);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test6", "()V", false);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test7", "()V", false);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test8", "()V", false);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test9", "()V", false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method without extra args and no arg
+ * 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),
+ "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+ mv.visitInvokeDynamicInsn("targetMethodTest1", "()V", bootstrap);
+ 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.
+ */
+ 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),
+ "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+ mv.visitLdcInsn(new Boolean(true));
+ mv.visitLdcInsn(new Byte((byte) 127));
+ mv.visitLdcInsn(new Character('c'));
+ mv.visitLdcInsn(new Short((short) 1024));
+ mv.visitLdcInsn(new Integer(123456));
+ mv.visitLdcInsn(new Float(1.2f));
+ mv.visitLdcInsn(new Long(123456789));
+ mv.visitLdcInsn(new Double(3.5123456789));
+ mv.visitLdcInsn("String");
+ mv.visitInvokeDynamicInsn("targetMethodTest2", "(ZBCSIFJDLjava/lang/String;)V", bootstrap);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate test with an invokedynamic, a static bootstrap method with extra args and no arg
+ * to the target method.
+ */
+ private void generateMethodTest3(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test3", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class, int.class,
+ long.class, float.class, double.class);
+ Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmLookupStaticWithExtraArgs", mt.toMethodDescriptorString(), false);
+ mv.visitInvokeDynamicInsn("targetMethodTest3", "()V", bootstrap, new Integer(1),
+ new Long(123456789), new Float(123.456), new Double(123456.789123));
+ 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 invokespecial.
+ */
+ private void generateMethodTest4(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test4", "()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.visitInvokeDynamicInsn("targetMethodTest4", "(Linvokecustom2/InvokeCustom;)V", bootstrap,
+ new Handle(Opcodes.H_INVOKESPECIAL, Type.getInternalName(Super.class),
+ "targetMethodTest4", "()V", false));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate a test with an invokedynamic where the target generates
+ * a result that the call site prints out.
+ */
+ private void generateMethodTest5(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test5", "()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),
+ "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+ mv.visitIntInsn(Opcodes.SIPUSH, 1000);
+ mv.visitIntInsn(Opcodes.SIPUSH, -923);
+ mv.visitIntInsn(Opcodes.SIPUSH, 77);
+ mv.visitInvokeDynamicInsn("targetMethodTest5", "(III)I", bootstrap);
+ mv.visitVarInsn(Opcodes.ISTORE, 0);
+ mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V");
+ mv.visitLdcInsn("targetMethodTest5 returned: ");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
+ mv.visitVarInsn(Opcodes.ILOAD, 0);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+ "(I)Ljava/lang/StringBuilder;");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
+ "()Ljava/lang/String;");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
+ "(Ljava/lang/String;)V");
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate a test with an invokedynamic where the call site invocation tests the summation of
+ * two long values and returns a long.
+ */
+ private void generateMethodTest6(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test6", "()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),
+ "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+ mv.visitLdcInsn(0x77777777777l);
+ mv.visitLdcInsn(-0x11111111111l);
+ mv.visitLdcInsn(0x66666666666l);
+ mv.visitInvokeDynamicInsn("targetMethodTest6", "(JJJ)J", bootstrap);
+ mv.visitVarInsn(Opcodes.LSTORE, 0);
+ mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V");
+ mv.visitLdcInsn("targetMethodTest6 returned: ");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
+ mv.visitVarInsn(Opcodes.LLOAD, 0);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+ "(J)Ljava/lang/StringBuilder;");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
+ "()Ljava/lang/String;");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
+ "(Ljava/lang/String;)V");
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate a test with an invokedynamic where the call site invocation tests the product of
+ * two float values and returns a double.
+ */
+ private void generateMethodTest7(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test7", "()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),
+ "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+ double x = 0.5009765625;
+ double y = -x;
+ mv.visitLdcInsn((float) x);
+ mv.visitLdcInsn((float) y);
+ mv.visitLdcInsn(x * y);
+ mv.visitInvokeDynamicInsn("targetMethodTest7", "(FFD)D", bootstrap);
+ mv.visitVarInsn(Opcodes.DSTORE, 0);
+ mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V");
+ mv.visitLdcInsn("targetMethodTest6 returned: ");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
+ mv.visitVarInsn(Opcodes.DLOAD, 0);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+ "(D)Ljava/lang/StringBuilder;");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
+ "()Ljava/lang/String;");
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
+ "(Ljava/lang/String;)V");
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate a test with multiple invokedynamic bytecodes operating on the same parameters.
+ * These invocations should each produce invoke-custom bytecodes with unique call site ids.
+ */
+ private void generateMethodTest8(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test8", "()V",
+ null, null);
+ MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+ MethodType.class);
+ // These should be two distinct call sites and both invoke the
+ // bootstrap method. An erroneous implementation might treat them
+ // as the same call site because the handle arguments are the same.
+ Handle bootstrap1 = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+ mv.visitLdcInsn("First invokedynamic invocation");
+ mv.visitInvokeDynamicInsn("targetMethodTest8", "(Ljava/lang/String;)V", bootstrap1);
+
+ Handle bootstrap2 = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+ "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+ mv.visitLdcInsn("Second invokedynamic invocation");
+ mv.visitInvokeDynamicInsn("targetMethodTest8", "(Ljava/lang/String;)V", bootstrap2);
+
+ // Using same handle again creates a new call site so invokes the bootstrap method.
+ mv.visitLdcInsn("Dupe first invokedynamic invocation");
+ mv.visitInvokeDynamicInsn("targetMethodTest8", "(Ljava/lang/String;)V", bootstrap1);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ /**
+ * Generate a test with different kinds of constant method handles.
+ */
+ private void generateMethodTest9(ClassVisitor cv) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test9", "()V",
+ null, null);
+ MethodType mt =
+ MethodType.methodType(CallSite.class,
+ MethodHandles.Lookup.class, String.class, MethodType.class,
+ MethodHandle.class, MethodHandle.class,
+ MethodHandle.class, MethodHandle.class,
+ MethodHandle.class, MethodHandle.class,
+ MethodHandle.class, MethodHandle.class);
+ String internalName = Type.getInternalName(InvokeCustom.class);
+ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, internalName, "bsmLookupTest9",
+ mt.toMethodDescriptorString(), false);
+ Handle staticSetter =
+ new Handle(Opcodes.H_GETSTATIC, internalName, "staticFieldTest9", "I", false);
+ Handle staticGetter =
+ new Handle(Opcodes.H_PUTSTATIC, internalName, "staticFieldTest9", "I", false);
+ Handle setter =
+ new Handle(Opcodes.H_GETFIELD, internalName, "fieldTest9", "F", false);
+ Handle getter =
+ new Handle(Opcodes.H_PUTFIELD, internalName, "fieldTest9", "F", false);
+ Handle instanceInvoke =
+ new Handle(Opcodes.H_INVOKEVIRTUAL, internalName, "helperMethodTest9", "()V", false);
+ Handle constructor =
+ new Handle(Opcodes.H_NEWINVOKESPECIAL, internalName, "<init>", "(I)V", false);
+ Handle interfaceInvoke =
+ new Handle(Opcodes.H_INVOKEINTERFACE,
+ Type.getInternalName(Runnable.class),
+ "run", "()V", true);
+ // test4 covers invokespecial for a super method. This covers invokespecial of a private method.
+ Handle privateInvoke =
+ new Handle(Opcodes.H_INVOKESPECIAL, internalName, "privateMethodTest9", "()V", false);
+
+ mv.visitInvokeDynamicInsn("targetMethodTest9", "()V", bootstrap,
+ staticSetter, staticGetter,
+ setter, getter,
+ instanceInvoke, constructor,
+ interfaceInvoke, privateInvoke);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+}
\ No newline at end of file