mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 1 | // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | package invokecustom2; |
| 6 | |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 7 | import java.io.IOException; |
Rico Wind | c0016d1 | 2017-10-26 13:39:32 +0200 | [diff] [blame] | 8 | import java.io.InputStream; |
| 9 | import java.io.OutputStream; |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 10 | import java.lang.invoke.CallSite; |
| 11 | import java.lang.invoke.MethodHandle; |
| 12 | import java.lang.invoke.MethodHandles; |
| 13 | import java.lang.invoke.MethodType; |
Rico Wind | c0016d1 | 2017-10-26 13:39:32 +0200 | [diff] [blame] | 14 | import java.nio.file.Files; |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 15 | import java.nio.file.Path; |
| 16 | import java.nio.file.Paths; |
Morten Krogh-Jespersen | 1bac3de | 2023-09-01 10:26:42 +0200 | [diff] [blame] | 17 | import java.nio.file.StandardOpenOption; |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 18 | import org.objectweb.asm.ClassReader; |
| 19 | import org.objectweb.asm.ClassVisitor; |
| 20 | import org.objectweb.asm.ClassWriter; |
| 21 | import org.objectweb.asm.Handle; |
| 22 | import org.objectweb.asm.MethodVisitor; |
| 23 | import org.objectweb.asm.Opcodes; |
| 24 | import org.objectweb.asm.Type; |
| 25 | |
| 26 | public class TestGenerator { |
| 27 | |
| 28 | private final Path classNamePath; |
Morten Krogh-Jespersen | 1bac3de | 2023-09-01 10:26:42 +0200 | [diff] [blame] | 29 | private final Path outputClassNamePath; |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 30 | |
| 31 | public static void main(String[] args) throws IOException { |
Morten Krogh-Jespersen | 1bac3de | 2023-09-01 10:26:42 +0200 | [diff] [blame] | 32 | assert args.length == 2; |
| 33 | String fileName = invokecustom.InvokeCustom.class.getSimpleName() + ".class"; |
| 34 | Path inputFile = Paths.get(args[0], TestGenerator.class.getPackage().getName(), fileName); |
| 35 | Path outputFile = Paths.get(args[1], fileName); |
| 36 | TestGenerator testGenerator = new TestGenerator(inputFile, outputFile); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 37 | testGenerator.generateTests(); |
| 38 | } |
| 39 | |
Morten Krogh-Jespersen | 1bac3de | 2023-09-01 10:26:42 +0200 | [diff] [blame] | 40 | public TestGenerator(Path classNamePath, Path outputClassNamePath) { |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 41 | this.classNamePath = classNamePath; |
Morten Krogh-Jespersen | 1bac3de | 2023-09-01 10:26:42 +0200 | [diff] [blame] | 42 | this.outputClassNamePath = outputClassNamePath; |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 43 | } |
| 44 | |
| 45 | private void generateTests() throws IOException { |
Morten Krogh-Jespersen | 1bac3de | 2023-09-01 10:26:42 +0200 | [diff] [blame] | 46 | Files.createDirectories(outputClassNamePath.getParent()); |
Rico Wind | c0016d1 | 2017-10-26 13:39:32 +0200 | [diff] [blame] | 47 | try (InputStream input = Files.newInputStream(classNamePath)) { |
| 48 | ClassReader cr = new ClassReader(input); |
| 49 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); |
| 50 | cr.accept( |
clementbera | 6c91c65 | 2019-04-15 17:28:42 +0200 | [diff] [blame] | 51 | new ClassVisitor(Opcodes.ASM7, cw) { |
Rico Wind | c0016d1 | 2017-10-26 13:39:32 +0200 | [diff] [blame] | 52 | @Override |
| 53 | public void visitEnd() { |
| 54 | generateMethodTest1(cw); |
| 55 | generateMethodTest2(cw); |
| 56 | generateMethodTest3(cw); |
| 57 | generateMethodTest4(cw); |
| 58 | generateMethodTest5(cw); |
| 59 | generateMethodTest6(cw); |
| 60 | generateMethodTest7(cw); |
| 61 | generateMethodTest8(cw); |
| 62 | generateMethodTest9(cw); |
| 63 | generateMethodMain(cw); |
| 64 | super.visitEnd(); |
| 65 | } |
| 66 | }, 0); |
Morten Krogh-Jespersen | 1bac3de | 2023-09-01 10:26:42 +0200 | [diff] [blame] | 67 | try (OutputStream output = |
| 68 | Files.newOutputStream(outputClassNamePath, StandardOpenOption.CREATE)) { |
Rico Wind | c0016d1 | 2017-10-26 13:39:32 +0200 | [diff] [blame] | 69 | output.write(cw.toByteArray()); |
| 70 | } |
| 71 | } |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 72 | } |
| 73 | |
| 74 | /* generate main method that only call all test methods. */ |
| 75 | private void generateMethodMain(ClassVisitor cv) { |
| 76 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, |
| 77 | "main", "([Ljava/lang/String;)V", null, null); |
| 78 | String internalName = Type.getInternalName(InvokeCustom.class); |
| 79 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test1", "()V", false); |
| 80 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test2", "()V", false); |
| 81 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test3", "()V", false); |
| 82 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test4", "()V", false); |
| 83 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test5", "()V", false); |
| 84 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test6", "()V", false); |
| 85 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test7", "()V", false); |
| 86 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test8", "()V", false); |
| 87 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test9", "()V", false); |
| 88 | mv.visitInsn(Opcodes.RETURN); |
| 89 | mv.visitMaxs(-1, -1); |
| 90 | } |
| 91 | |
| 92 | /** |
| 93 | * Generate test with an invokedynamic, a static bootstrap method without extra args and no arg |
| 94 | * to the target method. |
| 95 | */ |
| 96 | private void generateMethodTest1(ClassVisitor cv) { |
| 97 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test1", "()V", |
| 98 | null, null); |
| 99 | MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, |
| 100 | MethodType.class); |
| 101 | Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class), |
| 102 | "bsmLookupStatic", mt.toMethodDescriptorString(), false); |
| 103 | mv.visitInvokeDynamicInsn("targetMethodTest1", "()V", bootstrap); |
| 104 | mv.visitInsn(Opcodes.RETURN); |
| 105 | mv.visitMaxs(-1, -1); |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * Generate test with an invokedynamic, a static bootstrap method without extra args and |
| 110 | * args to the target method. |
| 111 | */ |
| 112 | private void generateMethodTest2(ClassVisitor cv) { |
| 113 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2", "()V", |
| 114 | null, null); |
| 115 | MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, |
| 116 | MethodType.class); |
| 117 | Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class), |
| 118 | "bsmLookupStatic", mt.toMethodDescriptorString(), false); |
| 119 | mv.visitLdcInsn(new Boolean(true)); |
| 120 | mv.visitLdcInsn(new Byte((byte) 127)); |
| 121 | mv.visitLdcInsn(new Character('c')); |
| 122 | mv.visitLdcInsn(new Short((short) 1024)); |
| 123 | mv.visitLdcInsn(new Integer(123456)); |
| 124 | mv.visitLdcInsn(new Float(1.2f)); |
| 125 | mv.visitLdcInsn(new Long(123456789)); |
| 126 | mv.visitLdcInsn(new Double(3.5123456789)); |
| 127 | mv.visitLdcInsn("String"); |
| 128 | mv.visitInvokeDynamicInsn("targetMethodTest2", "(ZBCSIFJDLjava/lang/String;)V", bootstrap); |
| 129 | mv.visitInsn(Opcodes.RETURN); |
| 130 | mv.visitMaxs(-1, -1); |
| 131 | } |
| 132 | |
| 133 | /** |
| 134 | * Generate test with an invokedynamic, a static bootstrap method with extra args and no arg |
| 135 | * to the target method. |
| 136 | */ |
| 137 | private void generateMethodTest3(ClassVisitor cv) { |
| 138 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test3", "()V", |
| 139 | null, null); |
| 140 | MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, |
| 141 | MethodType.class, int.class, |
| 142 | long.class, float.class, double.class); |
| 143 | Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class), |
| 144 | "bsmLookupStaticWithExtraArgs", mt.toMethodDescriptorString(), false); |
| 145 | mv.visitInvokeDynamicInsn("targetMethodTest3", "()V", bootstrap, new Integer(1), |
| 146 | new Long(123456789), new Float(123.456), new Double(123456.789123)); |
| 147 | mv.visitInsn(Opcodes.RETURN); |
| 148 | mv.visitMaxs(-1, -1); |
| 149 | } |
| 150 | |
| 151 | /** |
| 152 | * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a |
| 153 | * MethodHandle of kind invokespecial. |
| 154 | */ |
| 155 | private void generateMethodTest4(ClassVisitor cv) { |
| 156 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test4", "()V", |
| 157 | null, null); |
| 158 | MethodType mt = |
| 159 | MethodType.methodType( |
| 160 | CallSite.class, |
| 161 | MethodHandles.Lookup.class, |
| 162 | String.class, |
| 163 | MethodType.class, |
| 164 | MethodHandle.class); |
| 165 | Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class), |
| 166 | "bsmCreateCallSite", mt.toMethodDescriptorString(), false); |
| 167 | mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class)); |
| 168 | mv.visitInsn(Opcodes.DUP); |
| 169 | mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), |
| 170 | "<init>", "()V", false); |
| 171 | mv.visitInvokeDynamicInsn("targetMethodTest4", "(Linvokecustom2/InvokeCustom;)V", bootstrap, |
| 172 | new Handle(Opcodes.H_INVOKESPECIAL, Type.getInternalName(Super.class), |
| 173 | "targetMethodTest4", "()V", false)); |
| 174 | mv.visitInsn(Opcodes.RETURN); |
| 175 | mv.visitMaxs(-1, -1); |
| 176 | } |
| 177 | |
| 178 | /** |
| 179 | * Generate a test with an invokedynamic where the target generates |
| 180 | * a result that the call site prints out. |
| 181 | */ |
| 182 | private void generateMethodTest5(ClassVisitor cv) { |
| 183 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test5", "()V", |
| 184 | null, null); |
| 185 | MethodType mt = MethodType.methodType( |
| 186 | CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class); |
| 187 | Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class), |
| 188 | "bsmLookupStatic", mt.toMethodDescriptorString(), false); |
| 189 | mv.visitIntInsn(Opcodes.SIPUSH, 1000); |
| 190 | mv.visitIntInsn(Opcodes.SIPUSH, -923); |
| 191 | mv.visitIntInsn(Opcodes.SIPUSH, 77); |
| 192 | mv.visitInvokeDynamicInsn("targetMethodTest5", "(III)I", bootstrap); |
| 193 | mv.visitVarInsn(Opcodes.ISTORE, 0); |
| 194 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); |
| 195 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); |
| 196 | mv.visitInsn(Opcodes.DUP); |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 197 | mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 198 | mv.visitLdcInsn("targetMethodTest5 returned: "); |
| 199 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 200 | "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 201 | mv.visitVarInsn(Opcodes.ILOAD, 0); |
| 202 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 203 | "(I)Ljava/lang/StringBuilder;", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 204 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 205 | "()Ljava/lang/String;", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 206 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 207 | "(Ljava/lang/String;)V", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 208 | mv.visitInsn(Opcodes.RETURN); |
| 209 | mv.visitMaxs(-1, -1); |
| 210 | } |
| 211 | |
| 212 | /** |
| 213 | * Generate a test with an invokedynamic where the call site invocation tests the summation of |
| 214 | * two long values and returns a long. |
| 215 | */ |
| 216 | private void generateMethodTest6(ClassVisitor cv) { |
| 217 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test6", "()V", |
| 218 | null, null); |
| 219 | MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, |
| 220 | MethodType.class); |
| 221 | Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class), |
| 222 | "bsmLookupStatic", mt.toMethodDescriptorString(), false); |
| 223 | mv.visitLdcInsn(0x77777777777l); |
| 224 | mv.visitLdcInsn(-0x11111111111l); |
| 225 | mv.visitLdcInsn(0x66666666666l); |
| 226 | mv.visitInvokeDynamicInsn("targetMethodTest6", "(JJJ)J", bootstrap); |
| 227 | mv.visitVarInsn(Opcodes.LSTORE, 0); |
| 228 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); |
| 229 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); |
| 230 | mv.visitInsn(Opcodes.DUP); |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 231 | mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 232 | mv.visitLdcInsn("targetMethodTest6 returned: "); |
| 233 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 234 | "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 235 | mv.visitVarInsn(Opcodes.LLOAD, 0); |
| 236 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 237 | "(J)Ljava/lang/StringBuilder;", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 238 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 239 | "()Ljava/lang/String;", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 240 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 241 | "(Ljava/lang/String;)V", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 242 | mv.visitInsn(Opcodes.RETURN); |
| 243 | mv.visitMaxs(-1, -1); |
| 244 | } |
| 245 | |
| 246 | /** |
| 247 | * Generate a test with an invokedynamic where the call site invocation tests the product of |
| 248 | * two float values and returns a double. |
| 249 | */ |
| 250 | private void generateMethodTest7(ClassVisitor cv) { |
| 251 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test7", "()V", |
| 252 | null, null); |
| 253 | MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, |
| 254 | MethodType.class); |
| 255 | Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class), |
| 256 | "bsmLookupStatic", mt.toMethodDescriptorString(), false); |
| 257 | double x = 0.5009765625; |
| 258 | double y = -x; |
| 259 | mv.visitLdcInsn((float) x); |
| 260 | mv.visitLdcInsn((float) y); |
| 261 | mv.visitLdcInsn(x * y); |
| 262 | mv.visitInvokeDynamicInsn("targetMethodTest7", "(FFD)D", bootstrap); |
| 263 | mv.visitVarInsn(Opcodes.DSTORE, 0); |
| 264 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); |
| 265 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); |
| 266 | mv.visitInsn(Opcodes.DUP); |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 267 | mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 268 | mv.visitLdcInsn("targetMethodTest6 returned: "); |
| 269 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 270 | "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 271 | mv.visitVarInsn(Opcodes.DLOAD, 0); |
| 272 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 273 | "(D)Ljava/lang/StringBuilder;", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 274 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 275 | "()Ljava/lang/String;", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 276 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", |
Sebastien Hertz | 24a47e1 | 2017-09-11 11:42:57 +0200 | [diff] [blame] | 277 | "(Ljava/lang/String;)V", false); |
mikaelpeltier | a05d3d5 | 2017-08-21 15:03:22 +0200 | [diff] [blame] | 278 | mv.visitInsn(Opcodes.RETURN); |
| 279 | mv.visitMaxs(-1, -1); |
| 280 | } |
| 281 | |
| 282 | /** |
| 283 | * Generate a test with multiple invokedynamic bytecodes operating on the same parameters. |
| 284 | * These invocations should each produce invoke-custom bytecodes with unique call site ids. |
| 285 | */ |
| 286 | private void generateMethodTest8(ClassVisitor cv) { |
| 287 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test8", "()V", |
| 288 | null, null); |
| 289 | MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, |
| 290 | MethodType.class); |
| 291 | // These should be two distinct call sites and both invoke the |
| 292 | // bootstrap method. An erroneous implementation might treat them |
| 293 | // as the same call site because the handle arguments are the same. |
| 294 | Handle bootstrap1 = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class), |
| 295 | "bsmLookupStatic", mt.toMethodDescriptorString(), false); |
| 296 | mv.visitLdcInsn("First invokedynamic invocation"); |
| 297 | mv.visitInvokeDynamicInsn("targetMethodTest8", "(Ljava/lang/String;)V", bootstrap1); |
| 298 | |
| 299 | Handle bootstrap2 = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class), |
| 300 | "bsmLookupStatic", mt.toMethodDescriptorString(), false); |
| 301 | mv.visitLdcInsn("Second invokedynamic invocation"); |
| 302 | mv.visitInvokeDynamicInsn("targetMethodTest8", "(Ljava/lang/String;)V", bootstrap2); |
| 303 | |
| 304 | // Using same handle again creates a new call site so invokes the bootstrap method. |
| 305 | mv.visitLdcInsn("Dupe first invokedynamic invocation"); |
| 306 | mv.visitInvokeDynamicInsn("targetMethodTest8", "(Ljava/lang/String;)V", bootstrap1); |
| 307 | mv.visitInsn(Opcodes.RETURN); |
| 308 | mv.visitMaxs(-1, -1); |
| 309 | } |
| 310 | |
| 311 | /** |
| 312 | * Generate a test with different kinds of constant method handles. |
| 313 | */ |
| 314 | private void generateMethodTest9(ClassVisitor cv) { |
| 315 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test9", "()V", |
| 316 | null, null); |
| 317 | MethodType mt = |
| 318 | MethodType.methodType(CallSite.class, |
| 319 | MethodHandles.Lookup.class, String.class, MethodType.class, |
| 320 | MethodHandle.class, MethodHandle.class, |
| 321 | MethodHandle.class, MethodHandle.class, |
| 322 | MethodHandle.class, MethodHandle.class, |
| 323 | MethodHandle.class, MethodHandle.class); |
| 324 | String internalName = Type.getInternalName(InvokeCustom.class); |
| 325 | Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, internalName, "bsmLookupTest9", |
| 326 | mt.toMethodDescriptorString(), false); |
| 327 | Handle staticSetter = |
| 328 | new Handle(Opcodes.H_GETSTATIC, internalName, "staticFieldTest9", "I", false); |
| 329 | Handle staticGetter = |
| 330 | new Handle(Opcodes.H_PUTSTATIC, internalName, "staticFieldTest9", "I", false); |
| 331 | Handle setter = |
| 332 | new Handle(Opcodes.H_GETFIELD, internalName, "fieldTest9", "F", false); |
| 333 | Handle getter = |
| 334 | new Handle(Opcodes.H_PUTFIELD, internalName, "fieldTest9", "F", false); |
| 335 | Handle instanceInvoke = |
| 336 | new Handle(Opcodes.H_INVOKEVIRTUAL, internalName, "helperMethodTest9", "()V", false); |
| 337 | Handle constructor = |
| 338 | new Handle(Opcodes.H_NEWINVOKESPECIAL, internalName, "<init>", "(I)V", false); |
| 339 | Handle interfaceInvoke = |
| 340 | new Handle(Opcodes.H_INVOKEINTERFACE, |
| 341 | Type.getInternalName(Runnable.class), |
| 342 | "run", "()V", true); |
| 343 | // test4 covers invokespecial for a super method. This covers invokespecial of a private method. |
| 344 | Handle privateInvoke = |
| 345 | new Handle(Opcodes.H_INVOKESPECIAL, internalName, "privateMethodTest9", "()V", false); |
| 346 | |
| 347 | mv.visitInvokeDynamicInsn("targetMethodTest9", "()V", bootstrap, |
| 348 | staticSetter, staticGetter, |
| 349 | setter, getter, |
| 350 | instanceInvoke, constructor, |
| 351 | interfaceInvoke, privateInvoke); |
| 352 | mv.visitInsn(Opcodes.RETURN); |
| 353 | mv.visitMaxs(-1, -1); |
| 354 | } |
| 355 | } |