blob: 72bdfbff6c696c137bdbd6fd08207de89ce2cb20 [file] [log] [blame]
Denis Vnukove4434312017-10-12 12:45:50 -07001// 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 stringconcat;
5
Denis Vnukove4434312017-10-12 12:45:50 -07006import java.io.IOException;
Rico Windc0016d12017-10-26 13:39:32 +02007import java.io.InputStream;
8import java.io.OutputStream;
Denis Vnukove4434312017-10-12 12:45:50 -07009import java.lang.invoke.CallSite;
10import java.lang.invoke.MethodHandles;
11import java.lang.invoke.MethodType;
Rico Windc0016d12017-10-26 13:39:32 +020012import java.nio.file.Files;
Denis Vnukove4434312017-10-12 12:45:50 -070013import java.nio.file.Path;
14import java.nio.file.Paths;
15import java.util.ArrayList;
16import java.util.List;
17import org.objectweb.asm.ClassReader;
18import org.objectweb.asm.ClassVisitor;
19import org.objectweb.asm.ClassWriter;
20import org.objectweb.asm.Handle;
21import org.objectweb.asm.Label;
22import org.objectweb.asm.MethodVisitor;
23import org.objectweb.asm.Opcodes;
24
25public class TestGenerator {
26 private static final String RECIPE_PREFIX = "RECIPE:";
27
28 private static final String STRING_CONCAT_FACTORY = "java/lang/invoke/StringConcatFactory";
29
30 private static final Handle MAKE_CONCAT_WITH_CONSTANTS = new Handle(
31 Opcodes.H_INVOKESTATIC, STRING_CONCAT_FACTORY, "makeConcatWithConstants",
32 MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
33 MethodType.class, String.class, Object[].class).toMethodDescriptorString(),
34 false);
35
36 private static final Handle MAKE_CONCAT = new Handle(
37 Opcodes.H_INVOKESTATIC, STRING_CONCAT_FACTORY, "makeConcat",
38 MethodType.methodType(CallSite.class, MethodHandles.Lookup.class,
39 String.class, MethodType.class).toMethodDescriptorString(),
40 false);
41
42 public static void main(String[] args) throws IOException {
43 assert args.length == 1;
44 generateTests(Paths.get(args[0],
45 TestGenerator.class.getPackage().getName(),
46 StringConcat.class.getSimpleName() + ".class"));
47 }
48
49 private static void generateTests(Path classNamePath) throws IOException {
Rico Windc0016d12017-10-26 13:39:32 +020050 try (InputStream input = Files.newInputStream(classNamePath)) {
51 ClassReader cr = new ClassReader(input);
52 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
53 cr.accept(
clementbera6c91c652019-04-15 17:28:42 +020054 new ClassVisitor(Opcodes.ASM7, cw) {
Rico Windc0016d12017-10-26 13:39:32 +020055 @Override
56 public MethodVisitor visitMethod(int access,
57 final String methodName, String desc, String signature, String[] exceptions) {
58 MethodVisitor mv = super.visitMethod(access, methodName, desc, signature, exceptions);
clementbera6c91c652019-04-15 17:28:42 +020059 return new MethodVisitor(Opcodes.ASM7, mv) {
Rico Windc0016d12017-10-26 13:39:32 +020060 private List<Object> recentConstants = new ArrayList<>();
Denis Vnukove4434312017-10-12 12:45:50 -070061
Rico Windc0016d12017-10-26 13:39:32 +020062 @Override
63 public void visitLdcInsn(Object cst) {
64 if (!recentConstants.isEmpty() ||
65 (cst instanceof String && ((String) cst).startsWith(RECIPE_PREFIX))) {
66 // Add the constant, don't push anything on stack.
67 recentConstants.add(cst);
68 return;
Denis Vnukove4434312017-10-12 12:45:50 -070069 }
Rico Windc0016d12017-10-26 13:39:32 +020070 super.visitLdcInsn(cst);
Denis Vnukove4434312017-10-12 12:45:50 -070071 }
72
Rico Windc0016d12017-10-26 13:39:32 +020073 @Override
74 public void visitMethodInsn(
75 int opcode, String owner, String name, String desc, boolean itf) {
76 // Replace calls to 'makeConcat(...)' with appropriate `invokedynamic`.
77 if (opcode == Opcodes.INVOKESTATIC && name.equals("makeConcat")) {
78 mv.visitInvokeDynamicInsn(MAKE_CONCAT.getName(), desc, MAKE_CONCAT);
Denis Vnukove4434312017-10-12 12:45:50 -070079 recentConstants.clear();
Rico Windc0016d12017-10-26 13:39:32 +020080 return;
81 }
82
83 // Replace calls to 'makeConcat(...)' with appropriate `invokedynamic`.
84 if (opcode == Opcodes.INVOKESTATIC && name.equals("makeConcatWithConstants")) {
85 if (recentConstants.isEmpty()) {
86 throw new AssertionError("No constants detected in `" +
87 methodName + "`: call to " + name + desc);
88 }
89 recentConstants.set(0,
90 ((String) recentConstants.get(0)).substring(RECIPE_PREFIX.length()));
91
Jinseong Jeon66800e42019-03-20 11:57:24 -070092 mv.visitInvokeDynamicInsn(
93 MAKE_CONCAT_WITH_CONSTANTS.getName(),
94 removeLastParams(desc, recentConstants.size()),
95 MAKE_CONCAT_WITH_CONSTANTS,
96 recentConstants.toArray());
Rico Windc0016d12017-10-26 13:39:32 +020097 recentConstants.clear();
98 return;
99 }
100
101 // Otherwise fall back to default implementation.
102 super.visitMethodInsn(opcode, owner, name, desc, itf);
Denis Vnukove4434312017-10-12 12:45:50 -0700103 }
Denis Vnukove4434312017-10-12 12:45:50 -0700104
Rico Windc0016d12017-10-26 13:39:32 +0200105 private String removeLastParams(String descr, int paramsToRemove) {
106 MethodType methodType =
107 MethodType.fromMethodDescriptorString(
108 descr, this.getClass().getClassLoader());
109 return methodType
110 .dropParameterTypes(
111 methodType.parameterCount() - paramsToRemove,
112 methodType.parameterCount())
113 .toMethodDescriptorString();
114 }
Denis Vnukove4434312017-10-12 12:45:50 -0700115
Rico Windc0016d12017-10-26 13:39:32 +0200116 @Override
117 public void visitInsn(int opcode) {
118 switch (opcode) {
119 case Opcodes.ICONST_0:
120 if (!recentConstants.isEmpty()) {
121 recentConstants.add(0);
122 return;
123 }
124 break;
125 case Opcodes.ICONST_1:
126 if (!recentConstants.isEmpty()) {
127 recentConstants.add(1);
128 return;
129 }
130 break;
131 case Opcodes.ICONST_2:
132 if (!recentConstants.isEmpty()) {
133 recentConstants.add(2);
134 return;
135 }
136 break;
137 case Opcodes.ICONST_3:
138 if (!recentConstants.isEmpty()) {
139 recentConstants.add(3);
140 return;
141 }
142 break;
143 case Opcodes.ICONST_4:
144 if (!recentConstants.isEmpty()) {
145 recentConstants.add(4);
146 return;
147 }
148 break;
149 case Opcodes.ICONST_5:
150 if (!recentConstants.isEmpty()) {
151 recentConstants.add(5);
152 return;
153 }
154 break;
155 case Opcodes.ICONST_M1:
156 if (!recentConstants.isEmpty()) {
157 recentConstants.add(-1);
158 return;
159 }
160 break;
161 default:
162 recentConstants.clear();
163 break;
164 }
165 super.visitInsn(opcode);
166 }
Denis Vnukove4434312017-10-12 12:45:50 -0700167
Rico Windc0016d12017-10-26 13:39:32 +0200168 @Override
169 public void visitIntInsn(int opcode, int operand) {
170 recentConstants.clear();
171 super.visitIntInsn(opcode, operand);
172 }
Denis Vnukove4434312017-10-12 12:45:50 -0700173
Rico Windc0016d12017-10-26 13:39:32 +0200174 @Override
175 public void visitVarInsn(int opcode, int var) {
176 recentConstants.clear();
177 super.visitVarInsn(opcode, var);
178 }
Denis Vnukove4434312017-10-12 12:45:50 -0700179
Rico Windc0016d12017-10-26 13:39:32 +0200180 @Override
181 public void visitTypeInsn(int opcode, String type) {
182 recentConstants.clear();
183 super.visitTypeInsn(opcode, type);
184 }
Denis Vnukove4434312017-10-12 12:45:50 -0700185
Rico Windc0016d12017-10-26 13:39:32 +0200186 @Override
187 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
188 recentConstants.clear();
189 super.visitFieldInsn(opcode, owner, name, desc);
190 }
Denis Vnukove4434312017-10-12 12:45:50 -0700191
Rico Windc0016d12017-10-26 13:39:32 +0200192 @Override
193 public void visitJumpInsn(int opcode, Label label) {
194 recentConstants.clear();
195 super.visitJumpInsn(opcode, label);
196 }
Denis Vnukove4434312017-10-12 12:45:50 -0700197
Rico Windc0016d12017-10-26 13:39:32 +0200198 @Override
199 public void visitIincInsn(int var, int increment) {
200 recentConstants.clear();
201 super.visitIincInsn(var, increment);
202 }
Denis Vnukove4434312017-10-12 12:45:50 -0700203
Rico Windc0016d12017-10-26 13:39:32 +0200204 @Override
205 public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
206 recentConstants.clear();
207 super.visitTableSwitchInsn(min, max, dflt, labels);
208 }
209
210 @Override
211 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
212 recentConstants.clear();
213 super.visitLookupSwitchInsn(dflt, keys, labels);
214 }
215
216 @Override
217 public void visitMultiANewArrayInsn(String desc, int dims) {
218 recentConstants.clear();
219 super.visitMultiANewArrayInsn(desc, dims);
220 }
221 };
222 }
223 }, 0);
224 try (OutputStream output = Files.newOutputStream(classNamePath)) {
225 output.write(cw.toByteArray());
226 }
227 }
Denis Vnukove4434312017-10-12 12:45:50 -0700228 }
229}