# HG changeset patch # User briangoetz # Date 1383230228 14400 # Node ID 82ee370c3d7ea9d1032d78a9de7452c22dba9f28 # Parent bb4b1e1e390df22116c1ba741a37ce94cf2e2f98 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 diff -r bb4b1e1e390d -r 82ee370c3d7e src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java --- a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Thu Oct 31 11:59:09 2013 +0100 +++ b/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 = ""; + 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; + } + } + } diff -r bb4b1e1e390d -r 82ee370c3d7e src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java --- a/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Thu Oct 31 11:59:09 2013 +0100 +++ b/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) {