8027232: Update j.l.invoke code generating class files to use ASM enhancements for invocation of non-abstract methods on ifaces
Co-authored-by: Paul Sandoz <paul.sandoz@oracle.com> Reviewed-by: ksrini, rfield
This commit is contained in:
parent
23d71ee6ee
commit
8d462795cb
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -51,7 +51,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||||||
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
||||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
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 METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
|
||||||
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
|
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
|
||||||
private static final String NAME_CTOR = "<init>";
|
private static final String NAME_CTOR = "<init>";
|
||||||
@ -465,7 +465,9 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||||||
convertArgumentTypes(methodType);
|
convertArgumentTypes(methodType);
|
||||||
|
|
||||||
// Invoke the method we want to forward to
|
// 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
|
// Convert the return value (if any) and return it
|
||||||
// Note: if adapting from non-void to void, the 'return'
|
// Note: if adapting from non-void to void, the 'return'
|
||||||
|
@ -275,7 +275,7 @@ class InvokerBytecodeGenerator {
|
|||||||
*/
|
*/
|
||||||
private void classFilePrologue() {
|
private void classFilePrologue() {
|
||||||
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
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);
|
cw.visitSource(sourceFile, null);
|
||||||
|
|
||||||
String invokerDesc = invokerType.toMethodDescriptorString();
|
String invokerDesc = invokerType.toMethodDescriptorString();
|
||||||
@ -646,7 +646,8 @@ class InvokerBytecodeGenerator {
|
|||||||
// invocation
|
// invocation
|
||||||
if (member.isMethod()) {
|
if (member.isMethod()) {
|
||||||
mtype = member.getMethodType().toMethodDescriptorString();
|
mtype = member.getMethodType().toMethodDescriptorString();
|
||||||
mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype);
|
mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype,
|
||||||
|
member.getDeclaringClass().isInterface());
|
||||||
} else {
|
} else {
|
||||||
mtype = MethodType.toFieldDescriptorString(member.getFieldType());
|
mtype = MethodType.toFieldDescriptorString(member.getFieldType());
|
||||||
mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
|
mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -35,7 +35,7 @@ import static sun.invoke.util.Wrapper.*;
|
|||||||
class TypeConvertingMethodAdapter extends MethodVisitor {
|
class TypeConvertingMethodAdapter extends MethodVisitor {
|
||||||
|
|
||||||
TypeConvertingMethodAdapter(MethodVisitor mv) {
|
TypeConvertingMethodAdapter(MethodVisitor mv) {
|
||||||
super(Opcodes.ASM4, mv);
|
super(Opcodes.ASM5, mv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int NUM_WRAPPERS = Wrapper.values().length;
|
private static final int NUM_WRAPPERS = Wrapper.values().length;
|
||||||
|
184
jdk/test/java/lang/invoke/lambda/LambdaAsm.java
Normal file
184
jdk/test/java/lang/invoke/lambda/LambdaAsm.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user