1 /* 2 * Copyright (c) 2013, 2015, 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.compiler 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 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 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 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 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 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 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 182 public static void main(String... args) throws Exception { 183 init(); 184 verifyInvokerBytecodeGenerator(); 185 verifyASM(); 186 } 187 }