blob: 2a4eba4b6672f1080a1fad0a2f2b5f73c29533d2 [file] [log] [blame]
mikaelpeltiera05d3d52017-08-21 15:03:22 +02001// 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
5package invokecustom2;
6
mikaelpeltiera05d3d52017-08-21 15:03:22 +02007import java.io.IOException;
Rico Windc0016d12017-10-26 13:39:32 +02008import java.io.InputStream;
9import java.io.OutputStream;
mikaelpeltiera05d3d52017-08-21 15:03:22 +020010import java.lang.invoke.CallSite;
11import java.lang.invoke.MethodHandle;
12import java.lang.invoke.MethodHandles;
13import java.lang.invoke.MethodType;
Rico Windc0016d12017-10-26 13:39:32 +020014import java.nio.file.Files;
mikaelpeltiera05d3d52017-08-21 15:03:22 +020015import java.nio.file.Path;
16import java.nio.file.Paths;
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020017import java.nio.file.StandardOpenOption;
mikaelpeltiera05d3d52017-08-21 15:03:22 +020018import org.objectweb.asm.ClassReader;
19import org.objectweb.asm.ClassVisitor;
20import org.objectweb.asm.ClassWriter;
21import org.objectweb.asm.Handle;
22import org.objectweb.asm.MethodVisitor;
23import org.objectweb.asm.Opcodes;
24import org.objectweb.asm.Type;
25
26public class TestGenerator {
27
28 private final Path classNamePath;
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020029 private final Path outputClassNamePath;
mikaelpeltiera05d3d52017-08-21 15:03:22 +020030
31 public static void main(String[] args) throws IOException {
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020032 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);
mikaelpeltiera05d3d52017-08-21 15:03:22 +020037 testGenerator.generateTests();
38 }
39
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020040 public TestGenerator(Path classNamePath, Path outputClassNamePath) {
mikaelpeltiera05d3d52017-08-21 15:03:22 +020041 this.classNamePath = classNamePath;
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020042 this.outputClassNamePath = outputClassNamePath;
mikaelpeltiera05d3d52017-08-21 15:03:22 +020043 }
44
45 private void generateTests() throws IOException {
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020046 Files.createDirectories(outputClassNamePath.getParent());
Rico Windc0016d12017-10-26 13:39:32 +020047 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(
clementbera6c91c652019-04-15 17:28:42 +020051 new ClassVisitor(Opcodes.ASM7, cw) {
Rico Windc0016d12017-10-26 13:39:32 +020052 @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-Jespersen1bac3de2023-09-01 10:26:42 +020067 try (OutputStream output =
68 Files.newOutputStream(outputClassNamePath, StandardOpenOption.CREATE)) {
Rico Windc0016d12017-10-26 13:39:32 +020069 output.write(cw.toByteArray());
70 }
71 }
mikaelpeltiera05d3d52017-08-21 15:03:22 +020072 }
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 Hertz24a47e12017-09-11 11:42:57 +0200197 mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200198 mv.visitLdcInsn("targetMethodTest5 returned: ");
199 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200200 "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200201 mv.visitVarInsn(Opcodes.ILOAD, 0);
202 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200203 "(I)Ljava/lang/StringBuilder;", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200204 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200205 "()Ljava/lang/String;", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200206 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200207 "(Ljava/lang/String;)V", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200208 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 Hertz24a47e12017-09-11 11:42:57 +0200231 mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200232 mv.visitLdcInsn("targetMethodTest6 returned: ");
233 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200234 "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200235 mv.visitVarInsn(Opcodes.LLOAD, 0);
236 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200237 "(J)Ljava/lang/StringBuilder;", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200238 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200239 "()Ljava/lang/String;", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200240 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200241 "(Ljava/lang/String;)V", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200242 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 Hertz24a47e12017-09-11 11:42:57 +0200267 mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200268 mv.visitLdcInsn("targetMethodTest6 returned: ");
269 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200270 "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200271 mv.visitVarInsn(Opcodes.DLOAD, 0);
272 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200273 "(D)Ljava/lang/StringBuilder;", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200274 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200275 "()Ljava/lang/String;", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200276 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
Sebastien Hertz24a47e12017-09-11 11:42:57 +0200277 "(Ljava/lang/String;)V", false);
mikaelpeltiera05d3d52017-08-21 15:03:22 +0200278 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}