blob: ef5f8de5822e30e3d6489795a15e9c7ce9809c4a [file] [log] [blame]
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +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.
4package invokecustom;
5
Rico Windc0016d12017-10-26 13:39:32 +02006
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +02007import java.io.IOException;
Rico Windc0016d12017-10-26 13:39:32 +02008import java.io.InputStream;
9import java.io.OutputStream;
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020010import java.lang.invoke.CallSite;
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020011import java.lang.invoke.MethodHandles;
12import java.lang.invoke.MethodType;
Rico Windc0016d12017-10-26 13:39:32 +020013import java.nio.file.Files;
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020014import java.nio.file.Path;
15import java.nio.file.Paths;
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020016import java.nio.file.StandardOpenOption;
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020017import org.objectweb.asm.ClassReader;
18import org.objectweb.asm.ClassVisitor;
19import org.objectweb.asm.ClassWriter;
20import org.objectweb.asm.Handle;
21import org.objectweb.asm.MethodVisitor;
22import org.objectweb.asm.Opcodes;
23import org.objectweb.asm.Type;
24
25public class TestGenerator {
26
27 private final Path classNamePath;
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020028 private final Path outputClassNamePath;
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020029
30 public static void main(String[] args) throws IOException {
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020031 assert args.length == 2;
32 String fileName = InvokeCustom.class.getSimpleName() + ".class";
33 Path inputFile = Paths.get(args[0], TestGenerator.class.getPackage().getName(), fileName);
34 Path outputFile = Paths.get(args[1], fileName);
35 TestGenerator testGenerator = new TestGenerator(inputFile, outputFile);
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020036 testGenerator.generateTests();
37 }
38
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020039 public TestGenerator(Path classNamePath, Path outputClassNamePath) {
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020040 this.classNamePath = classNamePath;
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020041 this.outputClassNamePath = outputClassNamePath;
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020042 }
43
44 private void generateTests() throws IOException {
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020045 Files.createDirectories(outputClassNamePath.getParent());
Rico Windc0016d12017-10-26 13:39:32 +020046 try (InputStream inputStream = Files.newInputStream(classNamePath)) {
47 ClassReader cr = new ClassReader(inputStream);
48 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
49 cr.accept(
clementbera6c91c652019-04-15 17:28:42 +020050 new ClassVisitor(Opcodes.ASM7, cw) {
Rico Windc0016d12017-10-26 13:39:32 +020051 @Override
52 public void visitEnd() {
53 generateMethodTest1(cw);
54 generateMethodTest2(cw);
Mads Agerab4dc702018-09-24 09:43:52 +020055 generateMethodTest3(cw);
56 generateMethodTest4(cw);
Rico Windc0016d12017-10-26 13:39:32 +020057 generateMethodMain(cw);
58 super.visitEnd();
59 }
60 }, 0);
Morten Krogh-Jespersen1bac3de2023-09-01 10:26:42 +020061 try (OutputStream output =
62 Files.newOutputStream(outputClassNamePath, StandardOpenOption.CREATE)) {
Rico Windc0016d12017-10-26 13:39:32 +020063 output.write(cw.toByteArray());
64 }
65 }
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020066 }
67
68 /* Generate main method that only call all test methods. */
69 private void generateMethodMain(ClassVisitor cv) {
70 MethodVisitor mv = cv.visitMethod(
71 Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
72 mv.visitMethodInsn(
73 Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test1", "()V", false);
Mikaël Peltiercfd6dac2017-10-10 13:45:55 +020074 mv.visitMethodInsn(
75 Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test2", "()V", false);
Mads Agerab4dc702018-09-24 09:43:52 +020076 mv.visitMethodInsn(
77 Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test3", "()V", false);
78 mv.visitMethodInsn(
79 Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test4", "()V", false);
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020080 mv.visitInsn(Opcodes.RETURN);
81 mv.visitMaxs(-1, -1);
82 }
83
84 /**
Mads Agerab4dc702018-09-24 09:43:52 +020085 * Generate test with an invokedynamic, a static bootstrap method without extra args and
86 * args to the target method.
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020087 */
88 private void generateMethodTest1(ClassVisitor cv) {
89 MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test1", "()V",
90 null, null);
91 MethodType mt = MethodType.methodType(
92 CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
Mads Agerab4dc702018-09-24 09:43:52 +020093 Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +020094 "bsmLookupStatic", mt.toMethodDescriptorString(), false);
95 mv.visitLdcInsn(new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
96 "targetMethodTest1", "()V", false));
97 mv.visitLdcInsn(new Handle(Opcodes.H_GETSTATIC, Type.getInternalName(InvokeCustom.class),
98 "staticField1", "Ljava/lang/String;", false));
99 mv.visitInvokeDynamicInsn("targetMethodTest2",
100 "(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V",
101 bootstrap);
102 mv.visitInsn(Opcodes.RETURN);
103 mv.visitMaxs(-1, -1);
104 }
Mikaël Peltiercfd6dac2017-10-10 13:45:55 +0200105
106 /**
Mads Agerab4dc702018-09-24 09:43:52 +0200107 * Generate test with an invokedynamic, a static bootstrap method without extra args and
108 * args to the target method.
Mikaël Peltiercfd6dac2017-10-10 13:45:55 +0200109 */
110 private void generateMethodTest2(ClassVisitor cv) {
111 MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2", "()V",
112 null, null);
113 MethodType mt = MethodType.methodType(
114 CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
Mads Agerab4dc702018-09-24 09:43:52 +0200115 Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
Mikaël Peltiercfd6dac2017-10-10 13:45:55 +0200116 "bsmLookupStatic", mt.toMethodDescriptorString(), false);
117 mv.visitLdcInsn(Type.getMethodType("(ZBSCIFJDLjava/lang/String;)Ljava/lang/Object;"));
118 mv.visitInvokeDynamicInsn("targetMethodTest3", "(Ljava/lang/invoke/MethodType;)V",
119 bootstrap);
120 mv.visitInsn(Opcodes.RETURN);
121 mv.visitMaxs(-1, -1);
122 }
Mads Agerab4dc702018-09-24 09:43:52 +0200123
124 /**
125 * Generate test with a const method handle pointing to the middle of a class hierarchy.
126 * Call a static method with the method handle which will do a MethodHandle.invoke call on
127 * a sub class instance.
128 *
129 * Tests that the const method handle is rewritten when renaming. Also tests that the
130 * middle class does not disappear (for instance via class merging).
131 */
132 private void generateMethodTest3(ClassVisitor cv) {
133 MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test3", "()V",
134 null, null);
135 MethodType mt = MethodType.methodType(ReturnType.class, ArgumentType.class);
136 Handle invokeWithArg = new Handle(
137 Opcodes.H_INVOKEVIRTUAL,
138 Type.getInternalName(Middle.class),
139 "targetMethodTest4",
140 mt.toMethodDescriptorString(),
141 false);
142 mv.visitLdcInsn(invokeWithArg);
143 mv.visitMethodInsn(
144 Opcodes.INVOKESTATIC,
145 Type.getInternalName(InvokeCustom.class),
146 "doInvokeSubWithArg",
147 "(Ljava/lang/invoke/MethodHandle;)V",
148 false);
149 mv.visitInsn(Opcodes.RETURN);
150 mv.visitMaxs(-1, -1);
151 }
152
153 /**
154 * Generate test with a const method handle pointing to a class which inherits the method from
155 * the super class. Call a static method with the method handle which will do a
156 * MethodHandle.invokeExact.
157 */
158 private void generateMethodTest4(ClassVisitor cv) {
159 MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test4", "()V",
160 null, null);
161 MethodType mt = MethodType.methodType(ReturnType.class, ArgumentType.class);
162 Handle invokeExactWithArg = new Handle(
163 Opcodes.H_INVOKEVIRTUAL,
164 Type.getInternalName(Impl.class),
165 "targetMethodTest5",
166 mt.toMethodDescriptorString(),
167 false);
168 mv.visitLdcInsn(invokeExactWithArg);
169 mv.visitMethodInsn(
170 Opcodes.INVOKESTATIC,
171 Type.getInternalName(InvokeCustom.class),
172 "doInvokeExactImplWithArg",
173 "(Ljava/lang/invoke/MethodHandle;)V",
174 false);
175 mv.visitInsn(Opcodes.RETURN);
176 mv.visitMaxs(-1, -1);
177 }
Mikaël Peltier7b7b53a2017-10-09 13:33:21 +0200178}