1 /*
2  * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 8027232
27  * @summary ensures that j.l.i.InvokerByteCodeGenerator and ASM visitMethodInsn
28  * generate  bytecodes with correct constant pool references
29  * @modules java.base/jdk.internal.org.objectweb.asm
30  *          jdk.jdeps/com.sun.tools.classfile
31  *          jdk.zipfs
32  * @compile -XDignore.symbol.file LambdaAsm.java LUtils.java
33  * @run main/othervm LambdaAsm
34  */
35 import com.sun.tools.classfile.Attribute;
36 import com.sun.tools.classfile.ClassFile;
37 import com.sun.tools.classfile.Code_attribute;
38 import com.sun.tools.classfile.ConstantPool;
39 import com.sun.tools.classfile.ConstantPool.CPInfo;
40 import com.sun.tools.classfile.Instruction;
41 import com.sun.tools.classfile.Method;
42 import java.io.ByteArrayInputStream;
43 import java.io.File;
44 import java.util.ArrayList;
45 import java.nio.file.DirectoryStream;
46 import java.nio.file.Path;
47 import jdk.internal.org.objectweb.asm.ClassWriter;
48 import jdk.internal.org.objectweb.asm.MethodVisitor;
49 
50 import static java.nio.file.Files.*;
51 import static jdk.internal.org.objectweb.asm.Opcodes.*;
52 
53 public class LambdaAsm {
54 
55     static final File TestFile = new File("A.java");
56 
init()57     static void init() {
58         emitCode();
59         LUtils.compile(TestFile.getName());
60         LUtils.TestResult tr = LUtils.doExec(LUtils.JAVA_CMD.getAbsolutePath(),
61                 "-Djdk.internal.lambda.dumpProxyClasses=.",
62                 "-cp", ".", "A");
63         if (tr.exitValue != 0) {
64             System.out.println("Error: " + tr.toString());
65             throw new RuntimeException("could not create proxy classes");
66         }
67     }
68 
emitCode()69     static void emitCode() {
70         ArrayList<String> scratch = new ArrayList<>();
71         scratch.add("import java.util.function.*;");
72         scratch.add("class A {");
73         scratch.add("   interface I {");
74         scratch.add("       default Supplier<Integer> a() { return () -> 1; }");
75         scratch.add("       default Supplier<Integer> b(int i) { return () -> i; }");
76         scratch.add("       default Supplier<Integer> c(int i) { return () -> m(i); }");
77         scratch.add("       int m(int i);");
78         scratch.add("       static Integer d() { return 0; }");
79         scratch.add("   }");
80         scratch.add("   static class C implements I {");
81         scratch.add("       public int m(int i) { return i;}");
82         scratch.add("   }");
83         scratch.add("   public static void main(String[] args) {");
84         scratch.add("       I i = new C();");
85         scratch.add("       i.a();");
86         scratch.add("       i.b(1);");
87         scratch.add("       i.c(1);");
88         scratch.add("       I.d();");
89         scratch.add("   }");
90         scratch.add("}");
91         LUtils.createFile(TestFile, scratch);
92     }
93 
checkMethod(String cname, String mname, ConstantPool cp, Code_attribute code)94     static void checkMethod(String cname, String mname, ConstantPool cp,
95             Code_attribute code) throws ConstantPool.InvalidIndex {
96         for (Instruction i : code.getInstructions()) {
97             String iname = i.getMnemonic();
98             if ("invokespecial".equals(iname)
99                     || "invokestatic".equals(iname)) {
100                 int idx = i.getByte(2);
101                 System.out.println("Verifying " + cname + ":" + mname +
102                         " instruction:" + iname + " index @" + idx);
103                 CPInfo cpinfo = cp.get(idx);
104                 if (cpinfo instanceof ConstantPool.CONSTANT_Methodref_info) {
105                     throw new RuntimeException("unexpected CP type expected "
106                             + "InterfaceMethodRef, got MethodRef, " + cname
107                             + ", " + mname);
108                 }
109             }
110         }
111     }
112 
checkMethod(ClassFile cf, String mthd)113     static int checkMethod(ClassFile cf, String mthd) throws Exception {
114         if (cf.major_version < 52) {
115             throw new RuntimeException("unexpected class file version, in "
116                     + cf.getName() + "expected 52, got " + cf.major_version);
117         }
118         int count = 0;
119         for (Method m : cf.methods) {
120             String mname = m.getName(cf.constant_pool);
121             if (mname.equals(mthd)) {
122                 for (Attribute a : m.attributes) {
123                     if ("Code".equals(a.getName(cf.constant_pool))) {
124                         count++;
125                         checkMethod(cf.getName(), mname, cf.constant_pool,
126                                 (Code_attribute) a);
127                     }
128                 }
129             }
130         }
131         return count;
132     }
133 
verifyInvokerBytecodeGenerator()134     static void verifyInvokerBytecodeGenerator() throws Exception {
135         int count = 0;
136         int mcount = 0;
137         try (DirectoryStream<Path> ds = newDirectoryStream(new File(".").toPath(),
138                 // filter in lambda proxy classes
139                 "A$I$$Lambda$*.class")) {
140             for (Path p : ds) {
141                 System.out.println(p.toFile());
142                 ClassFile cf = ClassFile.read(p.toFile());
143                 // Check those methods implementing Supplier.get
144                 mcount += checkMethod(cf, "get");
145                 count++;
146             }
147         }
148         if (count < 3) {
149             throw new RuntimeException("unexpected number of files, "
150                     + "expected atleast 3 files, but got only " + count);
151         }
152         if (mcount < 3) {
153             throw new RuntimeException("unexpected number of methods, "
154                     + "expected atleast 3 methods, but got only " + mcount);
155         }
156     }
157 
verifyASM()158     static void verifyASM() throws Exception {
159         ClassWriter cw = new ClassWriter(0);
160         cw.visit(V1_8, ACC_PUBLIC, "X", null, "java/lang/Object", null);
161         MethodVisitor mv = cw.visitMethod(ACC_STATIC, "foo",
162                 "()V", null, null);
163         mv.visitMaxs(2, 1);
164         mv.visitMethodInsn(INVOKESTATIC,
165                 "java/util/function/Function.class",
166                 "identity", "()Ljava/util/function/Function;", true);
167         mv.visitInsn(RETURN);
168         cw.visitEnd();
169         byte[] carray = cw.toByteArray();
170         // for debugging
171         // write((new File("X.class")).toPath(), carray, CREATE, TRUNCATE_EXISTING);
172 
173         // verify using javap/classfile reader
174         ClassFile cf = ClassFile.read(new ByteArrayInputStream(carray));
175         int mcount = checkMethod(cf, "foo");
176         if (mcount < 1) {
177             throw new RuntimeException("unexpected method count, expected 1" +
178                     "but got " + mcount);
179         }
180     }
181 
main(String... args)182     public static void main(String... args) throws Exception {
183         init();
184         verifyInvokerBytecodeGenerator();
185         verifyASM();
186     }
187 }
188