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