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 constmethodhandle;
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(), ConstTest.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             generateMethodMain(cw);
62             super.visitEnd();
63           }
64         }, 0);
65     new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray());
66   }
67 
68   /* generate main method that only call all test methods. */
generateMethodMain(ClassVisitor cv)69   private void generateMethodMain(ClassVisitor cv) {
70     MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC,
71                                       "main", "([Ljava/lang/String;)V", null, null);
72     String internalName = Type.getInternalName(ConstTest.class);
73     mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test1",
74                        "()Ljava/lang/invoke/MethodHandle;", false);
75     mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName,
76                        "displayMethodHandle", "(Ljava/lang/invoke/MethodHandle;)V", false);
77     mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test2",
78                        "()Ljava/lang/invoke/MethodType;", false);
79     mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "displayMethodType",
80                        "(Ljava/lang/invoke/MethodType;)V", false);
81     mv.visitInsn(Opcodes.RETURN);
82     mv.visitMaxs(-1, -1);
83   }
84 
85   /**
86    * Generate a test that returns a constant method handle.
87    */
generateMethodTest1(ClassVisitor cv)88   private void generateMethodTest1(ClassVisitor cv) {
89     MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test1",
90                                       "()Ljava/lang/invoke/MethodHandle;", null, null);
91     MethodType mt = MethodType.methodType(Class.class);
92     Handle mh = new Handle(Opcodes.H_INVOKEVIRTUAL, Type.getInternalName(Object.class),
93                            "getClass", mt.toMethodDescriptorString(), false);
94     mv.visitLdcInsn(mh);
95     mv.visitInsn(Opcodes.ARETURN);
96     mv.visitMaxs(-1, -1);
97   }
98 
99   /**
100    * Generate a test that returns a constant method type.
101    */
generateMethodTest2(ClassVisitor cv)102   private void generateMethodTest2(ClassVisitor cv) {
103     MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2",
104                                       "()Ljava/lang/invoke/MethodType;", null, null);
105     Type mt = Type.getMethodType(Type.getType(boolean.class), Type.getType(char.class),
106                                  Type.getType(short.class), Type.getType(int.class),
107                                  Type.getType(long.class), Type.getType(float.class),
108                                  Type.getType(double.class), Type.getType(Object.class));
109     mv.visitLdcInsn(mt);
110     mv.visitInsn(Opcodes.ARETURN);
111     mv.visitMaxs(-1, -1);
112   }
113 }
114