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