OpenJDK / jdk / jdk10
changeset 21618:6632680b0683
8027232: Update j.l.invoke code generating class files to use ASM enhancements for invocation of non-abstract methods on ifaces
Reviewed-by: ksrini, rfield
Contributed-by: john.r.rose@oracle.com, paul.sandoz@oracle.com
author | ksrini |
---|---|
date | Wed, 06 Nov 2013 11:31:49 -0800 |
parents | 53b88e4ac6ed |
children | bd60e99ca531 |
files | jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java jdk/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java jdk/test/java/lang/invoke/lambda/LambdaAsm.java |
diffstat | 4 files changed, 194 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Wed Nov 06 11:22:15 2013 -0800 +++ b/jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Wed Nov 06 11:31:49 2013 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,7 +51,7 @@ /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { private static final Unsafe UNSAFE = Unsafe.getUnsafe(); - private static final int CLASSFILE_VERSION = 51; + private static final int CLASSFILE_VERSION = 52; private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE); private static final String JAVA_LANG_OBJECT = "java/lang/Object"; private static final String NAME_CTOR = "<init>"; @@ -465,7 +465,9 @@ convertArgumentTypes(methodType); // Invoke the method we want to forward to - visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc); + visitMethodInsn(invocationOpcode(), implMethodClassName, + implMethodName, implMethodDesc, + implDefiningClass.isInterface()); // Convert the return value (if any) and return it // Note: if adapting from non-void to void, the 'return'
--- a/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed Nov 06 11:22:15 2013 -0800 +++ b/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed Nov 06 11:31:49 2013 -0800 @@ -275,7 +275,7 @@ */ private void classFilePrologue() { cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); - cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null); + cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null); cw.visitSource(sourceFile, null); String invokerDesc = invokerType.toMethodDescriptorString(); @@ -646,7 +646,8 @@ // invocation if (member.isMethod()) { mtype = member.getMethodType().toMethodDescriptorString(); - mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype); + mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype, + member.getDeclaringClass().isInterface()); } else { mtype = MethodType.toFieldDescriptorString(member.getFieldType()); mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
--- a/jdk/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Wed Nov 06 11:22:15 2013 -0800 +++ b/jdk/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Wed Nov 06 11:31:49 2013 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ class TypeConvertingMethodAdapter extends MethodVisitor { TypeConvertingMethodAdapter(MethodVisitor mv) { - super(Opcodes.ASM4, mv); + super(Opcodes.ASM5, mv); } private static final int NUM_WRAPPERS = Wrapper.values().length;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/lambda/LambdaAsm.java Wed Nov 06 11:31:49 2013 -0800 @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8027232 + * @summary ensures that j.l.i.InvokerByteCodeGenerator and ASM visitMethodInsn + * generate bytecodes with correct constant pool references + * @compile -XDignore.symbol.file LambdaAsm.java LUtils.java + * @run main/othervm LambdaAsm + */ +import com.sun.tools.classfile.Attribute; +import com.sun.tools.classfile.ClassFile; +import com.sun.tools.classfile.Code_attribute; +import com.sun.tools.classfile.ConstantPool; +import com.sun.tools.classfile.ConstantPool.CPInfo; +import com.sun.tools.classfile.Instruction; +import com.sun.tools.classfile.Method; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.util.ArrayList; +import java.nio.file.DirectoryStream; +import java.nio.file.Path; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; + +import static java.nio.file.Files.*; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +public class LambdaAsm { + + static final File TestFile = new File("A.java"); + + static void init() { + emitCode(); + LUtils.compile(TestFile.getName()); + LUtils.TestResult tr = LUtils.doExec(LUtils.JAVA_CMD.getAbsolutePath(), + "-Djdk.internal.lambda.dumpProxyClasses=.", + "-cp", ".", "A"); + if (tr.exitValue != 0) { + System.out.println("Error: " + tr.toString()); + throw new RuntimeException("could not create proxy classes"); + } + } + + static void emitCode() { + ArrayList<String> scratch = new ArrayList<>(); + scratch.add("import java.util.function.*;"); + scratch.add("class A {"); + scratch.add(" interface I {"); + scratch.add(" default Supplier<Integer> a() { return () -> 1; }"); + scratch.add(" default Supplier<Integer> b(int i) { return () -> i; }"); + scratch.add(" default Supplier<Integer> c(int i) { return () -> m(i); }"); + scratch.add(" int m(int i);"); + scratch.add(" static Integer d() { return 0; }"); + scratch.add(" }"); + scratch.add(" static class C implements I {"); + scratch.add(" public int m(int i) { return i;}"); + scratch.add(" }"); + scratch.add(" public static void main(String[] args) {"); + scratch.add(" I i = new C();"); + scratch.add(" i.a();"); + scratch.add(" i.b(1);"); + scratch.add(" i.c(1);"); + scratch.add(" I.d();"); + scratch.add(" }"); + scratch.add("}"); + LUtils.createFile(TestFile, scratch); + } + + static void checkMethod(String cname, String mname, ConstantPool cp, + Code_attribute code) throws ConstantPool.InvalidIndex { + for (Instruction i : code.getInstructions()) { + String iname = i.getMnemonic(); + if ("invokespecial".equals(iname) + || "invokestatic".equals(iname)) { + int idx = i.getByte(2); + System.out.println("Verifying " + cname + ":" + mname + + " instruction:" + iname + " index @" + idx); + CPInfo cpinfo = cp.get(idx); + if (cpinfo instanceof ConstantPool.CONSTANT_Methodref_info) { + throw new RuntimeException("unexpected CP type expected " + + "InterfaceMethodRef, got MethodRef, " + cname + + ", " + mname); + } + } + } + } + + static int checkMethod(ClassFile cf, String mthd) throws Exception { + if (cf.major_version < 52) { + throw new RuntimeException("unexpected class file version, in " + + cf.getName() + "expected 52, got " + cf.major_version); + } + int count = 0; + for (Method m : cf.methods) { + String mname = m.getName(cf.constant_pool); + if (mname.equals(mthd)) { + for (Attribute a : m.attributes) { + if ("Code".equals(a.getName(cf.constant_pool))) { + count++; + checkMethod(cf.getName(), mname, cf.constant_pool, + (Code_attribute) a); + } + } + } + } + return count; + } + + static void verifyInvokerBytecodeGenerator() throws Exception { + int count = 0; + int mcount = 0; + try (DirectoryStream<Path> ds = newDirectoryStream(new File(".").toPath(), + // filter in lambda proxy classes + "A$I$$Lambda$?.class")) { + for (Path p : ds) { + System.out.println(p.toFile()); + ClassFile cf = ClassFile.read(p.toFile()); + // Check those methods implementing Supplier.get + mcount += checkMethod(cf, "get"); + count++; + } + } + if (count < 3) { + throw new RuntimeException("unexpected number of files, " + + "expected atleast 3 files, but got only " + count); + } + if (mcount < 3) { + throw new RuntimeException("unexpected number of methods, " + + "expected atleast 3 methods, but got only " + mcount); + } + } + + static void verifyASM() throws Exception { + ClassWriter cw = new ClassWriter(0); + cw.visit(V1_8, ACC_PUBLIC, "X", null, "java/lang/Object", null); + MethodVisitor mv = cw.visitMethod(ACC_STATIC, "foo", + "()V", null, null); + mv.visitMaxs(2, 1); + mv.visitMethodInsn(INVOKESTATIC, + "java/util/function/Function.class", + "identity", "()Ljava/util/function/Function;", true); + mv.visitInsn(RETURN); + cw.visitEnd(); + byte[] carray = cw.toByteArray(); + // for debugging + // write((new File("X.class")).toPath(), carray, CREATE, TRUNCATE_EXISTING); + + // verify using javap/classfile reader + ClassFile cf = ClassFile.read(new ByteArrayInputStream(carray)); + int mcount = checkMethod(cf, "foo"); + if (mcount < 1) { + throw new RuntimeException("unexpected method count, expected 1" + + "but got " + mcount); + } + } + + public static void main(String... args) throws Exception { + init(); + verifyInvokerBytecodeGenerator(); + verifyASM(); + } +}