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