1 /*
  2  * Copyright (c) 2013, 2023, 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     static final Path DUMP_LAMBDA_PROXY_CLASS_FILES = Path.of("DUMP_LAMBDA_PROXY_CLASS_FILES");
 55 
 56     static final File TestFile = new File("A.java");
 57 
 58     static void init() {
 59         emitCode();
 60         LUtils.compile(TestFile.getName());
 61         LUtils.TestResult tr = LUtils.doExec(LUtils.JAVA_CMD.getAbsolutePath(),
 62                 "-Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true",
 63                 "-cp", ".", "A");
 64         if (tr.exitValue != 0) {
 65             System.out.println("Error: " + tr.toString());
 66             throw new RuntimeException("could not create proxy classes");
 67         }
 68     }
 69 
 70     static void emitCode() {
 71         ArrayList<String> scratch = new ArrayList<>();
 72         scratch.add("import java.util.function.*;");
 73         scratch.add("class A {");
 74         scratch.add("   interface I {");
 75         scratch.add("       default Supplier<Integer> a() { return () -> 1; }");
 76         scratch.add("       default Supplier<Integer> b(int i) { return () -> i; }");
 77         scratch.add("       default Supplier<Integer> c(int i) { return () -> m(i); }");
 78         scratch.add("       int m(int i);");
 79         scratch.add("       static Integer d() { return 0; }");
 80         scratch.add("   }");
 81         scratch.add("   static class C implements I {");
 82         scratch.add("       public int m(int i) { return i;}");
 83         scratch.add("   }");
 84         scratch.add("   public static void main(String[] args) {");
 85         scratch.add("       I i = new C();");
 86         scratch.add("       i.a();");
 87         scratch.add("       i.b(1);");
 88         scratch.add("       i.c(1);");
 89         scratch.add("       I.d();");
 90         scratch.add("   }");
 91         scratch.add("}");
 92         LUtils.createFile(TestFile, scratch);
 93     }
 94 
 95     static void checkMethod(String cname, String mname, ConstantPool cp,
 96             Code_attribute code) throws ConstantPool.InvalidIndex {
 97         for (Instruction i : code.getInstructions()) {
 98             String iname = i.getMnemonic();
 99             if ("invokespecial".equals(iname)
100                     || "invokestatic".equals(iname)) {
101                 int idx = i.getByte(2);
102                 System.out.println("Verifying " + cname + ":" + mname +
103                         " instruction:" + iname + " index @" + idx);
104                 CPInfo cpinfo = cp.get(idx);
105                 if (cpinfo instanceof ConstantPool.CONSTANT_Methodref_info) {
106                     throw new RuntimeException("unexpected CP type expected "
107                             + "InterfaceMethodRef, got MethodRef, " + cname
108                             + ", " + mname);
109                 }
110             }
111         }
112     }
113 
114     static int checkMethod(ClassFile cf, String mthd) throws Exception {
115         if (cf.major_version < 52) {
116             throw new RuntimeException("unexpected class file version, in "
117                     + cf.getName() + "expected 52, got " + cf.major_version);
118         }
119         int count = 0;
120         for (Method m : cf.methods) {
121             String mname = m.getName(cf.constant_pool);
122             if (mname.equals(mthd)) {
123                 for (Attribute a : m.attributes) {
124                     if ("Code".equals(a.getName(cf.constant_pool))) {
125                         count++;
126                         checkMethod(cf.getName(), mname, cf.constant_pool,
127                                 (Code_attribute) a);
128                     }
129                 }
130             }
131         }
132         return count;
133     }
134 
135     static void verifyInvokerBytecodeGenerator() throws Exception {
136         int count = 0;
137         int mcount = 0;
138         try (DirectoryStream<Path> ds = newDirectoryStream(DUMP_LAMBDA_PROXY_CLASS_FILES,
139                 // filter in lambda proxy classes
140                 "A$I$$Lambda.*.class")) {
141             for (Path p : ds) {
142                 System.out.println(p.toFile());
143                 ClassFile cf = ClassFile.read(p.toFile());
144                 // Check those methods implementing Supplier.get
145                 mcount += checkMethod(cf, "get");
146                 count++;
147             }
148         }
149         if (count < 3) {
150             throw new RuntimeException("unexpected number of files, "
151                     + "expected atleast 3 files, but got only " + count);
152         }
153         if (mcount < 3) {
154             throw new RuntimeException("unexpected number of methods, "
155                     + "expected atleast 3 methods, but got only " + mcount);
156         }
157     }
158 
159     static void verifyASM() throws Exception {
160         ClassWriter cw = new ClassWriter(0);
161         cw.visit(V1_8, ACC_PUBLIC, "X", null, "java/lang/Object", null);
162         MethodVisitor mv = cw.visitMethod(ACC_STATIC, "foo",
163                 "()V", null, null);
164         mv.visitMaxs(2, 1);
165         mv.visitMethodInsn(INVOKESTATIC,
166                 "java/util/function/Function.class",
167                 "identity", "()Ljava/util/function/Function;", true);
168         mv.visitInsn(RETURN);
169         cw.visitEnd();
170         byte[] carray = cw.toByteArray();
171         // for debugging
172         // write((new File("X.class")).toPath(), carray, CREATE, TRUNCATE_EXISTING);
173 
174         // verify using javap/classfile reader
175         ClassFile cf = ClassFile.read(new ByteArrayInputStream(carray));
176         int mcount = checkMethod(cf, "foo");
177         if (mcount < 1) {
178             throw new RuntimeException("unexpected method count, expected 1" +
179                     "but got " + mcount);
180         }
181     }
182 
183     public static void main(String... args) throws Exception {
184         init();
185         verifyInvokerBytecodeGenerator();
186         verifyASM();
187     }
188 }