8024637: Lambda linkage performance - use reflection instead of ASM to manipulate parameter types
authorbriangoetz
Thu, 31 Oct 2013 10:37:08 -0400
changeset 21424 47236f604347
parent 21423 6f6edad5b031
child 21425 2d49fa63ac15
8024637: Lambda linkage performance - use reflection instead of ASM to manipulate parameter types 8023984: Lambda linkage performance - use a method ref to a static factory instead of a ctor ref Reviewed-by: briangoetz, rfield Contributed-by: sergey.kuksenko@oracle.com
jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
jdk/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java
--- a/jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Thu Oct 31 11:59:09 2013 +0100
+++ b/jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Thu Oct 31 10:37:08 2013 -0400
@@ -26,6 +26,7 @@
 package java.lang.invoke;
 
 import jdk.internal.org.objectweb.asm.*;
+import sun.invoke.util.BytecodeDescriptor;
 import sun.misc.Unsafe;
 import sun.security.action.GetPropertyAction;
 
@@ -54,6 +55,7 @@
     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>";
+    private static final String NAME_FACTORY = "get$Lambda";
 
     //Serialization support
     private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
@@ -76,6 +78,8 @@
     private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
 
 
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
     // Used to ensure that each spun class name is unique
     private static final AtomicInteger counter = new AtomicInteger(0);
 
@@ -94,15 +98,12 @@
     private final String implMethodClassName;        // Name of type containing implementation "CC"
     private final String implMethodName;             // Name of implementation method "impl"
     private final String implMethodDesc;             // Type descriptor for implementation methods "(I)Ljava/lang/String;"
-    private final Type[] implMethodArgumentTypes;    // ASM types for implementation method parameters
-    private final Type implMethodReturnType;         // ASM type for implementation method return type "Ljava/lang/String;"
+    private final Class<?> implMethodReturnClass;    // class for implementaion method return type "Ljava/lang/String;"
     private final MethodType constructorType;        // Generated class constructor type "(CC)void"
-    private final String constructorDesc;            // Type descriptor for constructor "(LCC;)V"
     private final ClassWriter cw;                    // ASM class writer
-    private final Type[] argTypes;                   // ASM types for the constructor arguments
     private final String[] argNames;                 // Generated names for the constructor arguments
+    private final String[] argDescs;                 // Type descriptors for the constructor arguments
     private final String lambdaClassName;            // Generated name for the generated class "X$$Lambda$1"
-    private final Type[] instantiatedArgumentTypes;  // ASM types for the functional interface arguments
 
     /**
      * General meta-factory constructor, supporting both standard cases and
@@ -157,22 +158,23 @@
         implMethodClassName = implDefiningClass.getName().replace('.', '/');
         implMethodName = implInfo.getName();
         implMethodDesc = implMethodType.toMethodDescriptorString();
-        Type implMethodAsmType = Type.getMethodType(implMethodDesc);
-        implMethodArgumentTypes = implMethodAsmType.getArgumentTypes();
-        implMethodReturnType = (implKind == MethodHandleInfo.REF_newInvokeSpecial)
-                ? Type.getObjectType(implMethodClassName)
-                : implMethodAsmType.getReturnType();
+        implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial)
+                ? implDefiningClass
+                : implMethodType.returnType();
         constructorType = invokedType.changeReturnType(Void.TYPE);
-        constructorDesc = constructorType.toMethodDescriptorString();
         lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-        argTypes = Type.getArgumentTypes(constructorDesc);
-        argNames = new String[argTypes.length];
-        for (int i = 0; i < argTypes.length; i++) {
-            argNames[i] = "arg$" + (i + 1);
+        int parameterCount = invokedType.parameterCount();
+        if (parameterCount > 0) {
+            argNames = new String[parameterCount];
+            argDescs = new String[parameterCount];
+            for (int i = 0; i < parameterCount; i++) {
+                argNames[i] = "arg$" + (i + 1);
+                argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
+            }
+        } else {
+            argNames = argDescs = EMPTY_STRING_ARRAY;
         }
-        instantiatedArgumentTypes = Type.getArgumentTypes(
-                instantiatedMethodType.toMethodDescriptorString());
     }
 
     /**
@@ -222,8 +224,7 @@
             try {
                 return new ConstantCallSite(
                         MethodHandles.Lookup.IMPL_LOOKUP
-                             .findConstructor(innerClass, constructorType)
-                             .asType(constructorType.changeReturnType(samBase)));
+                             .findStatic(innerClass, NAME_FACTORY, invokedType));
             }
             catch (ReflectiveOperationException e) {
                 throw new LambdaConversionException("Exception finding constructor", e);
@@ -268,29 +269,31 @@
                  JAVA_LANG_OBJECT, interfaces);
 
         // Generate final fields to be filled in by constructor
-        for (int i = 0; i < argTypes.length; i++) {
+        for (int i = 0; i < argDescs.length; i++) {
             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
                                             argNames[i],
-                                            argTypes[i].getDescriptor(),
+                                            argDescs[i],
                                             null, null);
             fv.visitEnd();
         }
 
         generateConstructor();
 
+        if (invokedType.parameterCount() != 0) {
+            generateFactory();
+        }
+
         // Forward the SAM method
-        String methodDescriptor = samMethodType.toMethodDescriptorString();
         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
-                                          methodDescriptor, null, null);
-        new ForwardingMethodGenerator(mv).generate(methodDescriptor);
+                                          samMethodType.toMethodDescriptorString(), null, null);
+        new ForwardingMethodGenerator(mv).generate(samMethodType);
 
         // Forward the bridges
         if (additionalBridges != null) {
             for (MethodType mt : additionalBridges) {
-                methodDescriptor = mt.toMethodDescriptorString();
                 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
-                                    methodDescriptor, null, null);
-                new ForwardingMethodGenerator(mv).generate(methodDescriptor);
+                                    mt.toMethodDescriptorString(), null, null);
+                new ForwardingMethodGenerator(mv).generate(mt);
             }
         }
 
@@ -323,23 +326,43 @@
     }
 
     /**
+     * Generate the factory method for the class
+     */
+    private void generateFactory() {
+        MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
+        m.visitCode();
+        m.visitTypeInsn(NEW, lambdaClassName);
+        m.visitInsn(Opcodes.DUP);
+        int parameterCount = invokedType.parameterCount();
+        for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
+            Class<?> argType = invokedType.parameterType(typeIndex);
+            m.visitVarInsn(getLoadOpcode(argType), varIndex);
+            varIndex += getParameterSize(argType);
+        }
+        m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString());
+        m.visitInsn(ARETURN);
+        m.visitMaxs(-1, -1);
+        m.visitEnd();
+    }
+
+    /**
      * Generate the constructor for the class
      */
     private void generateConstructor() {
         // Generate constructor
         MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
-                                            constructorDesc, null, null);
+                                            constructorType.toMethodDescriptorString(), null, null);
         ctor.visitCode();
         ctor.visitVarInsn(ALOAD, 0);
         ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
                              METHOD_DESCRIPTOR_VOID);
-        int lvIndex = 0;
-        for (int i = 0; i < argTypes.length; i++) {
+        int parameterCount = invokedType.parameterCount();
+        for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
             ctor.visitVarInsn(ALOAD, 0);
-            ctor.visitVarInsn(argTypes[i].getOpcode(ILOAD), lvIndex + 1);
-            lvIndex += argTypes[i].getSize();
-            ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i],
-                                argTypes[i].getDescriptor());
+            Class<?> argType = invokedType.parameterType(i);
+            ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
+            lvIndex += getParameterSize(argType);
+            ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
         }
         ctor.visitInsn(RETURN);
         // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
@@ -369,16 +392,14 @@
         mv.visitLdcInsn(implInfo.getName());
         mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
         mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
-
-        mv.iconst(argTypes.length);
+        mv.iconst(argDescs.length);
         mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
-        for (int i = 0; i < argTypes.length; i++) {
+        for (int i = 0; i < argDescs.length; i++) {
             mv.visitInsn(DUP);
             mv.iconst(i);
             mv.visitVarInsn(ALOAD, 0);
-            mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i],
-                              argTypes[i].getDescriptor());
-            mv.boxIfTypePrimitive(argTypes[i]);
+            mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
+            mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
             mv.visitInsn(AASTORE);
         }
         mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
@@ -430,20 +451,19 @@
             super(mv);
         }
 
-        void generate(String methodDescriptor) {
+        void generate(MethodType methodType) {
             visitCode();
 
             if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
                 visitTypeInsn(NEW, implMethodClassName);
                 visitInsn(DUP);
             }
-            for (int i = 0; i < argTypes.length; i++) {
+            for (int i = 0; i < argNames.length; i++) {
                 visitVarInsn(ALOAD, 0);
-                visitFieldInsn(GETFIELD, lambdaClassName, argNames[i],
-                               argTypes[i].getDescriptor());
+                visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
             }
 
-            convertArgumentTypes(Type.getArgumentTypes(methodDescriptor));
+            convertArgumentTypes(methodType);
 
             // Invoke the method we want to forward to
             visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc);
@@ -451,46 +471,36 @@
             // Convert the return value (if any) and return it
             // Note: if adapting from non-void to void, the 'return'
             // instruction will pop the unneeded result
-            Type samReturnType = Type.getReturnType(methodDescriptor);
-            convertType(implMethodReturnType, samReturnType, samReturnType);
-            visitInsn(samReturnType.getOpcode(Opcodes.IRETURN));
+            Class<?> samReturnClass = methodType.returnType();
+            convertType(implMethodReturnClass, samReturnClass, samReturnClass);
+            visitInsn(getReturnOpcode(samReturnClass));
             // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
             visitMaxs(-1, -1);
             visitEnd();
         }
 
-        private void convertArgumentTypes(Type[] samArgumentTypes) {
+        private void convertArgumentTypes(MethodType samType) {
             int lvIndex = 0;
             boolean samIncludesReceiver = implIsInstanceMethod &&
-                                                   argTypes.length == 0;
+                                                   invokedType.parameterCount() == 0;
             int samReceiverLength = samIncludesReceiver ? 1 : 0;
             if (samIncludesReceiver) {
                 // push receiver
-                Type rcvrType = samArgumentTypes[0];
-                Type instantiatedRcvrType = instantiatedArgumentTypes[0];
-
-                visitVarInsn(rcvrType.getOpcode(ILOAD), lvIndex + 1);
-                lvIndex += rcvrType.getSize();
-                convertType(rcvrType, Type.getType(implDefiningClass), instantiatedRcvrType);
+                Class<?> rcvrType = samType.parameterType(0);
+                visitVarInsn(getLoadOpcode(rcvrType), lvIndex + 1);
+                lvIndex += getParameterSize(rcvrType);
+                convertType(rcvrType, implDefiningClass, instantiatedMethodType.parameterType(0));
             }
-            int argOffset = implMethodArgumentTypes.length - samArgumentTypes.length;
-            for (int i = samReceiverLength; i < samArgumentTypes.length; i++) {
-                Type argType = samArgumentTypes[i];
-                Type targetType = implMethodArgumentTypes[argOffset + i];
-                Type instantiatedArgType = instantiatedArgumentTypes[i];
-
-                visitVarInsn(argType.getOpcode(ILOAD), lvIndex + 1);
-                lvIndex += argType.getSize();
-                convertType(argType, targetType, instantiatedArgType);
+            int samParametersLength = samType.parameterCount();
+            int argOffset = implMethodType.parameterCount() - samParametersLength;
+            for (int i = samReceiverLength; i < samParametersLength; i++) {
+                Class<?> argType = samType.parameterType(i);
+                visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
+                lvIndex += getParameterSize(argType);
+                convertType(argType, implMethodType.parameterType(argOffset + i), instantiatedMethodType.parameterType(i));
             }
         }
 
-        private void convertType(Type argType, Type targetType, Type functionalType) {
-            convertType(argType.getDescriptor(),
-                        targetType.getDescriptor(),
-                        functionalType.getDescriptor());
-        }
-
         private int invocationOpcode() throws InternalError {
             switch (implKind) {
                 case MethodHandleInfo.REF_invokeStatic:
@@ -508,4 +518,43 @@
             }
         }
     }
+
+    static int getParameterSize(Class<?> c) {
+        if (c == Void.TYPE) {
+            return 0;
+        } else if (c == Long.TYPE || c == Double.TYPE) {
+            return 2;
+        }
+        return 1;
+    }
+
+    static int getLoadOpcode(Class<?> c) {
+        if(c == Void.TYPE) {
+            throw new InternalError("Unexpected void type of load opcode");
+        }
+        return ILOAD + getOpcodeOffset(c);
+    }
+
+    static int getReturnOpcode(Class<?> c) {
+        if(c == Void.TYPE) {
+            return RETURN;
+        }
+        return IRETURN + getOpcodeOffset(c);
+    }
+
+    private static int getOpcodeOffset(Class<?> c) {
+        if (c.isPrimitive()) {
+            if (c == Long.TYPE) {
+                return 1;
+            } else if (c == Float.TYPE) {
+                return 2;
+            } else if (c == Double.TYPE) {
+                return 3;
+            }
+            return 0;
+        } else {
+            return 4;
+        }
+    }
+
 }
--- a/jdk/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java	Thu Oct 31 11:59:09 2013 +0100
+++ b/jdk/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java	Thu Oct 31 10:37:08 2013 -0400
@@ -28,6 +28,7 @@
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.internal.org.objectweb.asm.Opcodes;
 import jdk.internal.org.objectweb.asm.Type;
+import sun.invoke.util.BytecodeDescriptor;
 import sun.invoke.util.Wrapper;
 import static sun.invoke.util.Wrapper.*;
 
@@ -204,27 +205,27 @@
     }
 
     /**
-     * Convert an argument of type 'argType' to be passed to 'targetType' assuring that it is 'functionalType'.
+     * Convert an argument of type 'arg' to be passed to 'target' assuring that it is 'functional'.
      * Insert the needed conversion instructions in the method code.
-     * @param argType
-     * @param targetType
-     * @param functionalType
+     * @param arg
+     * @param target
+     * @param functional
      */
-    void convertType(String dArg, String dTarget, String dFunctional) {
-        if (dArg.equals(dTarget)) {
+    void convertType(Class<?> arg, Class<?> target, Class<?> functional) {
+        if (arg.equals(target)) {
             return;
         }
-        Wrapper wArg = toWrapper(dArg);
-        Wrapper wTarget = toWrapper(dTarget);
-        if (wArg == VOID || wTarget == VOID) {
+        if (arg == Void.TYPE || target == Void.TYPE) {
             return;
         }
-        if (isPrimitive(wArg)) {
-            if (isPrimitive(wTarget)) {
+        if (arg.isPrimitive()) {
+            Wrapper wArg = Wrapper.forPrimitiveType(arg);
+            if (target.isPrimitive()) {
                 // Both primitives: widening
-                widen(wArg, wTarget);
+                widen(wArg, Wrapper.forPrimitiveType(target));
             } else {
                 // Primitive argument to reference target
+                String dTarget = BytecodeDescriptor.unparse(target);
                 Wrapper wPrimTarget = wrapperOrNullFromDescriptor(dTarget);
                 if (wPrimTarget != null) {
                     // The target is a boxed primitive type, widen to get there before boxing
@@ -237,16 +238,18 @@
                 }
             }
         } else {
+            String dArg = BytecodeDescriptor.unparse(arg);
             String dSrc;
-            Wrapper wFunctional = toWrapper(dFunctional);
-            if (isPrimitive(wFunctional)) {
+            if (functional.isPrimitive()) {
                 dSrc = dArg;
             } else {
                 // Cast to convert to possibly more specific type, and generate CCE for invalid arg
-                dSrc = dFunctional;
-                cast(dArg, dFunctional);
+                dSrc = BytecodeDescriptor.unparse(functional);
+                cast(dArg, dSrc);
             }
-            if (isPrimitive(wTarget)) {
+            String dTarget = BytecodeDescriptor.unparse(target);
+            if (target.isPrimitive()) {
+                Wrapper wTarget = toWrapper(dTarget);
                 // Reference argument to primitive target
                 Wrapper wps = wrapperOrNullFromDescriptor(dSrc);
                 if (wps != null) {