changeset 5847:e5e6ed8b2de2

Runtime clean-up #1: restructuring of class generator
author Robert Field <Robert.Field@oracle.com>
date Fri, 10 Aug 2012 13:55:09 -0700
parents fa0760fb5e00
children 5f2ae9c265f0 e197bd7653e3
files src/share/classes/java/lang/invoke/InnerClassGenerator.java src/share/classes/java/lang/invoke/LambdaMetafactory.java src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java
diffstat 3 files changed, 521 insertions(+), 434 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/InnerClassGenerator.java	Tue Jul 31 19:04:29 2012 -0400
+++ b/src/share/classes/java/lang/invoke/InnerClassGenerator.java	Fri Aug 10 13:55:09 2012 -0700
@@ -25,23 +25,18 @@
 
 package java.lang.invoke;
 
-import org.openjdk.org.objectweb.asm.*;
-import sun.misc.Unsafe;
-
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.security.ProtectionDomain;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-
+import org.openjdk.org.objectweb.asm.*;
 import static org.openjdk.org.objectweb.asm.Opcodes.*;
-import static org.openjdk.org.objectweb.asm.Type.*;
+import sun.misc.Unsafe;
 
 /**
  * InnerClassGenerator
@@ -57,268 +52,98 @@
     private static final String NAME_SERIALIZED_LAMBDA = "com/oracle/java/lang/invoke/SerializedLambdaImpl";
     private static final String NAME_CTOR = "<init>";
 
-    // These line up with the indexes of ASM's Type.getSort
-    private static final int NUM_PRIMITIVE_SORTS = 9;
-
-    // Indexed by Type.getSort; Type of primitive sort
-    private static final Type[] TYPE_FROM_SORT = {
-            VOID_TYPE,
-            BOOLEAN_TYPE,
-            CHAR_TYPE,
-            BYTE_TYPE,
-            SHORT_TYPE,
-            INT_TYPE,
-            FLOAT_TYPE,
-            LONG_TYPE,
-            DOUBLE_TYPE,
-    };
-
-    // Indexed by Type.getSort; class name of wrapper class for that primitive sort
-    private static final String[] NAME_PRIMITIVE_WRAPPER = {
-            "java/lang/Void",
-            "java/lang/Boolean",
-            "java/lang/Character",
-            "java/lang/Byte",
-            "java/lang/Short",
-            "java/lang/Integer",
-            "java/lang/Float",
-            "java/lang/Long",
-            "java/lang/Double",
-    };
-
-    // Indexed by Type.getSort; signature character for that primitive sort
-    private static final String[] PRIMITIVE_SIG = {
-            "V",
-            "Z",
-            "C",
-            "B",
-            "S",
-            "I",
-            "F",
-            "J",
-            "D",
-    };
-
-    // Indexed by Type.getSort; name of the unboxing method that primitive sort
-    private static final String[] NAME_UNBOX_METHOD = {
-            "---",
-            "booleanValue",
-            "charValue",
-            "byteValue",
-            "shortValue",
-            "intValue",
-            "floatValue",
-            "longValue",
-            "doubleValue",
-    };
-
-    // Same for all primitives; name of the boxing method
-    private static final String NAME_BOX_METHOD = "valueOf";
-
-    // Table of opcodes for widening primitive conversions; NOP = no conversion
-    private static final int[][] wideningOpcodes = new int[NUM_PRIMITIVE_SORTS][NUM_PRIMITIVE_SORTS];
-
-    // Map from class name to corresponding primitive sort
-    private static final Map<String, Type> unboxed = new HashMap<>();
-
-    static {
-        for (int i=0; i<NUM_PRIMITIVE_SORTS; i++)
-            for (int j=0; j<NUM_PRIMITIVE_SORTS; j++)
-                wideningOpcodes[i][j] = NOP;
-        wideningOpcodes[Type.BYTE][Type.LONG] = I2L;
-        wideningOpcodes[Type.SHORT][Type.LONG] = I2L;
-        wideningOpcodes[Type.INT][Type.LONG] = I2L;
-        wideningOpcodes[Type.CHAR][Type.LONG] = I2L;
-        wideningOpcodes[Type.FLOAT][Type.LONG] = F2L;
-
-        wideningOpcodes[Type.BYTE][Type.FLOAT] = I2F;
-        wideningOpcodes[Type.SHORT][Type.FLOAT] = I2F;
-        wideningOpcodes[Type.INT][Type.FLOAT] = I2F;
-        wideningOpcodes[Type.CHAR][Type.FLOAT] = I2F;
-        wideningOpcodes[Type.LONG][Type.FLOAT] = L2F;
-
-        wideningOpcodes[Type.BYTE][Type.DOUBLE] = I2D;
-        wideningOpcodes[Type.SHORT][Type.DOUBLE] = I2D;
-        wideningOpcodes[Type.INT][Type.DOUBLE] = I2D;
-        wideningOpcodes[Type.CHAR][Type.DOUBLE] = I2D;
-        wideningOpcodes[Type.FLOAT][Type.DOUBLE] = F2D;
-        wideningOpcodes[Type.LONG][Type.DOUBLE] = L2D;
-
-        for (int i=0; i<NUM_PRIMITIVE_SORTS; i++)
-            unboxed.put(NAME_PRIMITIVE_WRAPPER[i], TYPE_FROM_SORT[i]);
-    }
-
     private static final Unsafe unsafe = Unsafe.getUnsafe();
 
     // Used to ensure that each spun class name is unique
     private static final AtomicInteger counter = new AtomicInteger(0);
 
-    public static <T> Class<? extends T> spinInnerClass(Class<T> sam,
-                                                        MethodHandleInfo samInfo,
-                                                        MethodType constructorType,
-                                                        MethodType functionDescType,
+    private final Class<?> targetClass;
+    private final int implKind;
+    private final Class<?> implMethodClass;
+    private final String implMethodName;
+    private final String implMethodDesc;
+    private final boolean implIsInstanceMethod;
+    private final ClassWriter cw;
+    private final String[] argNames;
+    private final Type[] argTypes;
+    private final String lambdaClassName;
+    private final String constructorDesc;
+    
+    public InnerClassGenerator(         MethodType constructorType,
                                                         MethodHandleInfo implInfo,
                                                         boolean implIsInstanceMethod,
-                                                        boolean serializable,
                                                         Class<?> targetClass) {
-        int implKind = implInfo.getReferenceKind();
+        this.targetClass = targetClass;
+        this.implIsInstanceMethod = implIsInstanceMethod;
+        implKind = implInfo.getReferenceKind();
+        implMethodClass = implInfo.getDeclaringClass();
+        implMethodName = implInfo.getName();
+        implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
+        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);
+        } 
+    }
+
+    public <T> Class<? extends T> spinInnerClass(Class<T> samClass,
+                                                        MethodHandleInfo samInfo,
+                                                        MethodType functionDescType,
+                                                        boolean serializable) {
         String samMethodName = samInfo.getName();
-        Class<?> implMethodClass = implInfo.getDeclaringClass();
-        String implMethodName = implInfo.getName();
-        String implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
         String functionalMethodDesc = functionDescType.toMethodDescriptorString();
-        String constructorDesc = constructorType.toMethodDescriptorString();
+ 
+        Method[] methods = samClass.getMethods(); // @@@ Use reflection for expedience, but in production should do better
+        String samName = samClass.getName().replace('.', '/');
+        // @@@ Class name generation is wrong for many method refs -- use containing class, not target class
+        Type samType = Type.getType(samClass);
 
-        Method[] methods = sam.getMethods(); // @@@ Use reflection for expedience, but in production should do better
-        String samName = sam.getName().replace('.', '/');
-        // @@@ Class name generation is wrong for many method refs -- use containing class, not target class
-        String wrapperName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
-        Type samType = Type.getType(sam);
-
-        // @@@ Redo manual depth computation later, and remove COMPUTE_MAXES
-        final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-
-        cw.visit(CLASSFILE_VERSION, ACC_PUBLIC + ACC_SUPER, wrapperName, null, NAME_MAGIC_ACCESSOR_IMPL,
+        cw.visit(CLASSFILE_VERSION, ACC_PUBLIC + ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL,
                  serializable ? new String[]{samName, NAME_SERIALIZABLE} : new String[]{samName});
 
-        Type[] argTypes = Type.getArgumentTypes(constructorDesc);
-        String[] argNames = new String[argTypes.length];
-        for (int i = 0; i < argTypes.length; i++)
-            argNames[i] = "arg$" + (i + 1);
- 
         // Generate fields
         for (int i = 0; i < argTypes.length; i++) {
             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, argNames[i], argTypes[i].getDescriptor(), null, null);
             fv.visitEnd();
         }
-
-        // Generate constructor
-        MethodVisitor ctor = cw.visitMethod(ACC_PUBLIC, NAME_CTOR, constructorDesc, null, null);
-        ctor.visitCode();
-        ctor.visitVarInsn(ALOAD, 0);
-        ctor.visitMethodInsn(INVOKESPECIAL, NAME_MAGIC_ACCESSOR_IMPL, NAME_CTOR, Type.getMethodDescriptor(Type.VOID_TYPE));
-        int totalSize = 0;
-        int fixedDepth = 2;
-        int lvIndex = 0;
-        for (int i = 0; i < argTypes.length; i++) {
-            ctor.visitVarInsn(ALOAD, 0);
-            ctor.visitVarInsn(argTypes[i].getOpcode(ILOAD), lvIndex + 1);
-            lvIndex += argTypes[i].getSize();
-            ctor.visitFieldInsn(PUTFIELD, wrapperName, argNames[i], argTypes[i].getDescriptor());
-            totalSize += argTypes[i].getSize();
-        }
-        ctor.visitInsn(RETURN);
-        ctor.visitMaxs(Math.max(fixedDepth, 1 + totalSize), Math.max(2, 1 + totalSize));
-        ctor.visitEnd();
+        
+        generateConstructor(constructorDesc);
 
         // Generate methods
         List<Method> methodsDone = new ArrayList<>();
         Methods:
         for (Method m : methods) {
-            if (!m.getName().equals(samMethodName) || isObjectMethod(m))
+            if (!m.getName().equals(samMethodName) || isObjectMethod(m)) {
                 continue Methods;
-            for (Method md : methodsDone)
-                if (sameMethod(m, md))
+            }
+            for (Method md : methodsDone) {
+                if (sameMethod(m, md)) {
                     continue Methods;
+                }
+            }
+            methodsDone.add(m);
 
-            methodsDone.add(m);
             // @@@ Should make some semblance of attempt to check compatibility of SAM signatures
-
-            Class<?>[] exceptionTypes = m.getExceptionTypes();
-            String[] exceptionNames = new String[exceptionTypes.length];
-            for (int i = 0; i < exceptionTypes.length; i++)
-                exceptionNames[i] = exceptionTypes[i].getName().replace('.', '/');
-            String methodDescriptor = Type.getMethodDescriptor(m);
-            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, m.getName(), methodDescriptor, null, exceptionNames);
-            mv.visitCode();
-
-            totalSize = 0;
-            for (int i = 0; i < argTypes.length; i++) {
-                totalSize += argTypes[i].getSize();
-            }
-
-            int invokeOp;
-            switch (implKind) {
-                case MethodHandleInfo.REF_invokeStatic:
-                    invokeOp = INVOKESTATIC;
-                    break;
-                case MethodHandleInfo.REF_newInvokeSpecial:
-                    mv.visitTypeInsn(NEW, implMethodClass.getName().replace('.', '/'));
-                    mv.visitInsn(DUP);
-                    invokeOp = INVOKESPECIAL;
-                    totalSize += 2;
-                    break;
-                case MethodHandleInfo.REF_invokeVirtual:
-                    invokeOp = INVOKEVIRTUAL;
-                    break;
-                case MethodHandleInfo.REF_invokeInterface:
-                    invokeOp = INVOKEINTERFACE;
-                    break;
-                default:
-                    throw new InternalError("Unexpected invocation kind: " + implKind);
-            }
-
-            // Must be after NEW (in the newInvokeSpecial case)
-            for (int i = 0; i < argTypes.length; i++) {
-                mv.visitVarInsn(ALOAD, 0);
-                mv.visitFieldInsn(GETFIELD, wrapperName, argNames[i], argTypes[i].getDescriptor());
-            }
-            
-            boolean samIncludesReceiver = implIsInstanceMethod && argTypes.length==0;
-
-            totalSize += generateCallForwarder(mv, Type.getArgumentTypes(m), Type.getArgumentTypes(implMethodDesc), Type.getArgumentTypes(functionalMethodDesc),
-                                               Type.getReturnType(m), Type.getReturnType(implMethodDesc),
-                                               samIncludesReceiver, Type.getType(implInfo.getDeclaringClass()),
-                                               invokeOp, implMethodClass.getName().replace('.', '/'),
-                                               implMethodName, implMethodDesc);
-            mv.visitMaxs(Math.max(Type.getReturnType(m).getSize(), totalSize), 1 + totalSize);
-            mv.visitEnd();
+            generateForwardingMethod(m, functionalMethodDesc);
         }
 
         if (serializable) {
-            String samMethodDesc = samInfo.getMethodType().toMethodDescriptorString();
-            MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, null, null);
-
-            mv.visitCode();
-            mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
-            mv.visitInsn(DUP);
-            mv.visitLdcInsn(samType);
-            mv.visitLdcInsn(samMethodName);
-            mv.visitLdcInsn(samMethodDesc);
-            mv.visitLdcInsn(Type.getType(implMethodClass));
-            mv.visitLdcInsn(implMethodName);
-            mv.visitLdcInsn(implMethodDesc);
-
-            iconst(mv, argTypes.length);
-            mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT);
-            totalSize = 0;
-            for (int i = 0; i < argTypes.length; i++) {
-                mv.visitInsn(DUP);
-                iconst(mv, i);
-                mv.visitVarInsn(ALOAD, 0);
-                mv.visitFieldInsn(GETFIELD, wrapperName, argNames[i], argTypes[i].getDescriptor());
-                boxIfPrimitive(mv, argTypes[i]);
-                mv.visitInsn(AASTORE);
-                totalSize += argTypes[i].getSize();
-            }
-            mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
-                               "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V");
-            mv.visitInsn(ARETURN);
-            mv.visitMaxs(11 + totalSize, 1);
-            mv.visitEnd();
+            generateSerializationMethod(samInfo, samType, samMethodName);
         }
 
         cw.visitEnd();
 
+        return defineGeneratedClass(cw.toByteArray());
+    }
 
-//        new ClassReader(cw.toByteArray()).accept(new TraceClassVisitor(new PrintWriter(System.out)), 0);
-//        CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), true, new PrintWriter(System.out));
-
-        final byte[] classBytes = cw.toByteArray();
+    private <T> Class<? extends T> defineGeneratedClass(final byte[] classBytes) {
         // @@@ Get rid of this in production
         if (System.getProperty("dump.generated") != null) {
-            System.out.printf("Loaded: %s (%d bytes) %n", wrapperName, classBytes.length);
-            try (FileOutputStream fos = new FileOutputStream(wrapperName.replace('/', '.') + ".class")) {
+            System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName, classBytes.length);
+            try (FileOutputStream fos = new FileOutputStream(lambdaClassName.replace('/', '.') + ".class")) {
                 fos.write(classBytes);
             } catch (IOException ex) {
                 Logger.getLogger(InnerClassGenerator.class.getName()).log(Level.SEVERE, null, ex);
@@ -327,216 +152,165 @@
         
         ClassLoader loader = targetClass.getClassLoader();
         ProtectionDomain pd = (loader == null)? null : targetClass.getProtectionDomain();
-        return (Class <? extends T>) unsafe.defineClass(wrapperName, classBytes, 0, classBytes.length, loader, pd);
+        return (Class <? extends T>) unsafe.defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd);
     }
     
-    /**
-     * Copied from org.objectweb.asm.commons.InstructionAdapter.iconst() replace
-     * with that when/if in the JDK. ASM: a very small and fast Java bytecode
-     * manipulation framework Copyright (c) 2000-2005 INRIA, France Telecom All
-     * rights reserved.
-     */
-    private static void iconst(final MethodVisitor mv, final int cst) {
-        if (cst >= -1 && cst <= 5) {
-            mv.visitInsn(Opcodes.ICONST_0 + cst);
-        } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
-            mv.visitIntInsn(Opcodes.BIPUSH, cst);
-        } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
-            mv.visitIntInsn(Opcodes.SIPUSH, cst);
-        } else {
-            mv.visitLdcInsn(cst);
-        }
-    }
-
     private static boolean sameMethod(Method m1, Method m2) {
         Class<?>[] p1 = m1.getParameterTypes();
         Class<?>[] p2 = m2.getParameterTypes();
-        if (p1.length != p2.length)
+        if (p1.length != p2.length) {
             return false;
-        for (int i = 0; i < p1.length; i++)
-            if (!p1[i].equals(p2[i]))
+        }
+        for (int i = 0; i < p1.length; i++) {
+            if (!p1[i].equals(p2[i])) {
                 return false;
-        if (!m1.getReturnType().equals(m2.getReturnType()))
+            }
+        }
+        if (!m1.getReturnType().equals(m2.getReturnType())) {
             return false;
+        }
         return true;
     }
     
-    private static String boxingDescriptor(int typeSort) {
-        return String.format("(%s)L%s;", PRIMITIVE_SIG[typeSort], NAME_PRIMITIVE_WRAPPER[typeSort]);
+    private void generateConstructor(String constructorDesc) {
+        // Generate constructor
+        MethodVisitor ctor = cw.visitMethod(ACC_PUBLIC, NAME_CTOR, constructorDesc, null, null);
+        ctor.visitCode();
+        ctor.visitVarInsn(ALOAD, 0);
+        ctor.visitMethodInsn(INVOKESPECIAL, NAME_MAGIC_ACCESSOR_IMPL, NAME_CTOR, Type.getMethodDescriptor(Type.VOID_TYPE));
+        int lvIndex = 0;
+        for (int i = 0; i < argTypes.length; 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());
+        }
+        ctor.visitInsn(RETURN);
+        ctor.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+        ctor.visitEnd();
     }
 
-    private static String unboxingDescriptor(int typeSort) {
-        return String.format("()%s", PRIMITIVE_SIG[typeSort]);
+    private void generateSerializationMethod(MethodHandleInfo samInfo, Type samType, String samMethodName) {
+        String samMethodDesc = samInfo.getMethodType().toMethodDescriptorString();
+        TypeConvertingMethodAdapter mv = new TypeConvertingMethodAdapter(cw.visitMethod(ACC_PRIVATE + ACC_FINAL, NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, null, null));
+
+        mv.visitCode();
+        mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
+        mv.dup();
+        mv.visitLdcInsn(samType);
+        mv.visitLdcInsn(samMethodName);
+        mv.visitLdcInsn(samMethodDesc);
+        mv.visitLdcInsn(Type.getType(implMethodClass));
+        mv.visitLdcInsn(implMethodName);
+        mv.visitLdcInsn(implMethodDesc);
+
+        mv.iconst(argTypes.length);
+        mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT);
+        for (int i = 0; i < argTypes.length; i++) {
+            mv.dup();
+            mv.iconst(i);
+            mv.visitVarInsn(ALOAD, 0);
+            mv.getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
+            mv.boxIfPrimitive(argTypes[i]);
+            mv.visitInsn(AASTORE);
+        }
+        mv.invokespecial(NAME_SERIALIZED_LAMBDA, NAME_CTOR,
+                           "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V");
+        mv.visitInsn(ARETURN);
+        mv.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+        mv.visitEnd();
     }
 
-    private static void boxIfPrimitive(MethodVisitor mv, Type argType) {
-        int sort = argType.getSort();
-        if (sort < NAME_PRIMITIVE_WRAPPER.length) {
-            mv.visitMethodInsn(INVOKESTATIC, NAME_PRIMITIVE_WRAPPER[sort], NAME_BOX_METHOD, boxingDescriptor(sort));
+    private void generateForwardingMethod(Method m, String functionalMethodDesc) throws InternalError {
+        Class<?>[] exceptionTypes = m.getExceptionTypes();
+        String[] exceptionNames = new String[exceptionTypes.length];
+        for (int i = 0; i < exceptionTypes.length; i++) {
+            exceptionNames[i] = exceptionTypes[i].getName().replace('.', '/');
+        }
+        String methodDescriptor = Type.getMethodDescriptor(m);
+        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, m.getName(), methodDescriptor, null, exceptionNames);
+        new ForwardingMethodGenerator(mv).generate(m, functionalMethodDesc);
+    }
+
+    private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
+
+        private final String implMethodClassName = implMethodClass.getName().replace('.', '/');
+
+        ForwardingMethodGenerator(MethodVisitor mv) {
+            super(mv);
+        }
+
+        void generate(Method m, String functionalMethodDesc) throws InternalError {
+            visitCode();
+
+            if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
+                visitTypeInsn(NEW, implMethodClassName);
+                dup();
+            }
+            for (int i = 0; i < argTypes.length; i++) {
+                visitVarInsn(ALOAD, 0);
+                getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
+            }
+
+            convertArgumentTypes(Type.getArgumentTypes(m), Type.getArgumentTypes(functionalMethodDesc));
+
+            // Invoke the method we want to forward to
+            visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc);
+
+            // Convert the return value (if any) and return it
+            // @@@ if adapting from non-void to void, need to pop result off stack
+            Type samReturnType = Type.getReturnType(m);
+            if (!samReturnType.equals(TYPE_VOID)) {
+                convertType(Type.getReturnType(implMethodDesc), samReturnType, samReturnType);
+            }
+            areturn(samReturnType);
+
+            visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+            visitEnd();
+        }
+
+        void convertArgumentTypes(Type[] samArgumentTypes, Type[] functionalArgumentTypes) {
+            int lvIndex = 0;
+            boolean samIncludesReceiver = implIsInstanceMethod && argTypes.length == 0;
+            int samReceiverLength = samIncludesReceiver ? 1 : 0;
+            if (samIncludesReceiver) {
+                // push receiver
+                Type rcvrType = samArgumentTypes[0];
+                Type functionalType = functionalArgumentTypes[0];
+
+                load(lvIndex + 1, rcvrType);
+                lvIndex += rcvrType.getSize();
+                convertType(rcvrType, Type.getType(implMethodClass), functionalType);
+            }
+            Type[] implArgumentTypes = Type.getArgumentTypes(implMethodDesc);
+            int argOffset = implArgumentTypes.length - samArgumentTypes.length;
+            for (int i = samReceiverLength; i < samArgumentTypes.length; i++) {
+                Type argType = samArgumentTypes[i];
+                Type targetType = implArgumentTypes[argOffset + i];
+                Type functionalType = functionalArgumentTypes[i];
+
+                load(lvIndex + 1, argType);
+                lvIndex += argType.getSize();
+                convertType(argType, targetType, functionalType);
+            }
+        }
+
+        int invocationOpcode() throws InternalError {
+            switch (implKind) {
+                case MethodHandleInfo.REF_invokeStatic:
+                    return INVOKESTATIC;
+                case MethodHandleInfo.REF_newInvokeSpecial:
+                    return INVOKESPECIAL;
+                 case MethodHandleInfo.REF_invokeVirtual:
+                    return INVOKEVIRTUAL;
+                case MethodHandleInfo.REF_invokeInterface:
+                    return INVOKEINTERFACE;
+                default:
+                    throw new InternalError("Unexpected invocation kind: " + implKind);
+            }
         }
     }
     
-    private static class TypeConverter {
-
-        final MethodVisitor mv;
-
-        TypeConverter(MethodVisitor mv) {
-            this.mv = mv;
-        }
-        
-        void widen(Type sType, Type tType) {
-            int sSort = sType.getSort();
-            int tSort = tType.getSort();
-            if (sSort != tSort) {
-                int opcode = wideningOpcodes[sSort][tSort];
-                if (opcode != NOP) {
-                    mv.visitInsn(opcode);
-                }
-            }
-        }
-
-        void box(Type type) {
-            int sort = type.getSort();
-            mv.visitMethodInsn(INVOKESTATIC, 
-                    NAME_PRIMITIVE_WRAPPER[sort], 
-                    NAME_BOX_METHOD, 
-                    boxingDescriptor(sort));
-        }
-
-        void unbox(Type type) {
-            int sort = type.getSort();
-            mv.visitMethodInsn(INVOKEVIRTUAL, 
-                    NAME_PRIMITIVE_WRAPPER[sort],
-                    NAME_UNBOX_METHOD[sort], 
-                    unboxingDescriptor(sort));
-        }
-
-        void cast(Type sType, Type tType) {
-            cast(sType.getInternalName(), tType.getInternalName());
-        }
-
-        void cast(String sName, Type type) {
-            cast(sName, type.getInternalName());
-        }
-
-        void cast(Type sType, String tName) {
-            cast(sType.getInternalName(), tName);
-        }
-
-        void cast(String sName, String tName) {
-            if (!tName.equals(sName) && !tName.equals(NAME_OBJECT)) {
-                mv.visitTypeInsn(CHECKCAST, tName);
-            }
-        }
-
-        boolean isPrimitive(Type type) {
-            return type.getSort() < NUM_PRIMITIVE_SORTS;
-        }
-
-        Type unboxedType(Type type) {
-            String n = type.getInternalName();
-            return unboxed.get(n);
-        }
-        
-        String boxedType(Type type) {
-            return NAME_PRIMITIVE_WRAPPER[type.getSort()];
-        }
-
-        void convert(Type argType, Type targetType, Type functionalType) {
-            if (argType.equals(VOID_TYPE)) {
-                return;
-            }
-            
-            if (isPrimitive(argType)) {
-                if (isPrimitive(targetType)) {
-                    // Both primitives: widening
-                    widen(argType, targetType);
-                } else {
-                    // Primitive argument to reference target
-                    Type tpType = unboxedType(targetType);
-                    if (tpType != null) {
-                        // The target is a boxed primitive type, widen to get there before boxing
-                        widen(argType, tpType);
-                        box(tpType);
-                    } else {
-                        // Otherwise, box and cast
-                        box(argType);
-                        cast(boxedType(argType), targetType);
-                    }
-                }
-            } else {
-                Type sType;
-                if (isPrimitive(functionalType)) {
-                    //@@@ Shold not be possible
-                    sType = argType;
-                } else {
-                    // Cast to convert to possibly more specific type, and generate CCE for invalid arg
-                    sType = functionalType;
-                    cast(argType, functionalType);
-                }
-                if (isPrimitive(targetType)) {
-                    // Reference argument to primitive target
-                    Type spType = unboxedType(sType);
-                    if (spType != null) {
-                        // The source type is a boxed primitive, unbox and widen
-                        unbox(spType);
-                        widen(spType, targetType);
-                    } else {
-                        //@@@ Should not be possible
-                        // The source type is not primitive, cast to boxed target type, and unbox
-                        cast(sType, boxedType(targetType));
-                        unbox(targetType);
-                    }
-                } else {
-                    // Both reference types: just case to target type
-                    cast(sType, targetType);
-                }
-            }
-        }
-    }
-
-    private static void convertType(MethodVisitor mv, Type argType, Type targetType, Type functionalType) {
-        if (!argType.equals(targetType)) {
-            new TypeConverter(mv).convert(argType,  targetType,  functionalType);
-        }
-    }
-
-    private static int generateCallForwarder(MethodVisitor mv,
-                                             Type[] samArgumentTypes, Type[] implArgumentTypes, Type[] functionalArgumentTypes,
-                                             Type samReturnType, Type implReturnType,
-                                             boolean samIncludesReceiver, Type implClassType,
-                                             int invocationOpcode, String targetOwner, String targetName, String targetDesc) {
-        int depth = 0;
-        int lvIndex = 0;
-        int samReceiverLength = samIncludesReceiver? 1 : 0;
-        if (samIncludesReceiver) {
-            // push receiver
-            Type rcvrType = samArgumentTypes[0];
-            Type functionalType = functionalArgumentTypes[0];
-            mv.visitVarInsn(rcvrType.getOpcode(Opcodes.ILOAD), lvIndex + 1);
-            lvIndex += rcvrType.getSize();
-            convertType(mv, rcvrType, implClassType, functionalType);
-            depth += rcvrType.getSize(); // @@@ probably wrong            
-        }
-        int argOffset = implArgumentTypes.length - samArgumentTypes.length;
-        for (int i = samReceiverLength; i < samArgumentTypes.length; i++) {
-            Type argType = samArgumentTypes[i];
-            Type targetType = implArgumentTypes[argOffset + i];
-            Type functionalType = functionalArgumentTypes[i];
-            mv.visitVarInsn(argType.getOpcode(Opcodes.ILOAD), lvIndex + 1);
-            lvIndex += argType.getSize();
-            convertType(mv, argType, targetType, functionalType);
-            depth += argType.getSize(); // @@@ probably wrong
-        }
-        mv.visitMethodInsn(invocationOpcode, targetOwner, targetName, targetDesc);
-        // @@@ if adapting from non-void to void, need to pop result off stack
-        if (!samReturnType.equals(TYPE_VOID)) {
-            convertType(mv, implReturnType, samReturnType, samReturnType);
-        }
-        mv.visitInsn(samReturnType.getOpcode(IRETURN));
-        return depth;
-    }
-
     private static final Method METHOD_HASH_CODE;
     private static final Method METHOD_EQUALS;
     private static final Method METHOD_CLONE;
--- a/src/share/classes/java/lang/invoke/LambdaMetafactory.java	Tue Jul 31 19:04:29 2012 -0400
+++ b/src/share/classes/java/lang/invoke/LambdaMetafactory.java	Fri Aug 10 13:55:09 2012 -0700
@@ -270,8 +270,8 @@
             Class<?> implParamType = implType.parameterType(i);
             Class<?> capturedParamType = invokedType.parameterType(i + capturedStart);
             if (!capturedParamType.equals(implParamType)) {
-                throw new LambdaConversionException(String.format("Type mismatch in captured lambda parameter %d: expecting %s, found %s",
-                                                                  i, capturedParamType, implParamType));
+                throw new LambdaConversionException(
+                        String.format("Type mismatch in captured lambda parameter %d: expecting %s, found %s", i, capturedParamType, implParamType));
             }
         }
         // Check for adaptation match on SAM arguments
@@ -279,9 +279,10 @@
         for (int i=implFromCaptured; i<implArity; i++) {
             Class<?> implParamType = implType.parameterType(i);
             Class<?> samParamType = samType.parameterType(i + samOffset);
-            if (!isAdaptableTo(samParamType, implParamType))
-                throw new LambdaConversionException(String.format("Type mismatch for lambda argument %d: %s is not convertible to %s",
-                                                    i, samParamType, implParamType));
+            if (!isAdaptableTo(samParamType, implParamType)) {
+                throw new LambdaConversionException(
+                        String.format("Type mismatch for lambda argument %d: %s is not convertible to %s", i, samParamType, implParamType));
+            }
         }
 
         // Adaptation match: return type
@@ -290,9 +291,10 @@
                 (implInfo.getReferenceKind() == MethodHandleInfo.REF_newInvokeSpecial)
                   ? implDefiningClass
                   : implType.returnType();
-        if (!isAdaptableToAsReturn(actualReturnType, expectedType))
-            throw new LambdaConversionException(String.format("Type mismatch for lambda return: %s is not convertible to %s",
-                                                              actualReturnType, expectedType));
+        if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
+            throw new LambdaConversionException(
+                    String.format("Type mismatch for lambda return: %s is not convertible to %s", actualReturnType, expectedType));
+        }
     }
 
     /**
@@ -309,11 +311,12 @@
 
         final Class<?> samBase = invokedType.returnType();
 
-        final Class<?> innerClass = InnerClassGenerator.spinInnerClass(samBase, samInfo, constructorType, functionDescType,
+        final InnerClassGenerator icg = new InnerClassGenerator(constructorType, 
                                                                  implInfo,
                                                                  implIsInstanceMethod,
-                                                                 Serializable.class.isAssignableFrom(samBase),
                                                                  caller.lookupClass());
+        final Class<?> innerClass = icg.spinInnerClass(samBase, samInfo, functionDescType,
+                                                                 Serializable.class.isAssignableFrom(samBase));
         if (invokedType.parameterCount() == 0) {
             return new ConstantCallSite(MethodHandles.constant(samBase, innerClass.newInstance()));
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java	Fri Aug 10 13:55:09 2012 -0700
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2012, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package java.lang.invoke;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.openjdk.org.objectweb.asm.*;
+import static org.openjdk.org.objectweb.asm.Opcodes.*;
+import static org.openjdk.org.objectweb.asm.Type.*;
+
+class TypeConvertingMethodAdapter extends MethodVisitor {
+    
+    TypeConvertingMethodAdapter(MethodVisitor mv) {
+        super(ASM4, mv);
+    }
+
+    private static final String NAME_OBJECT = "java/lang/Object";
+
+    // These line up with the indexes of ASM's Type.getSort
+    private static final int NUM_PRIMITIVE_SORTS = 9;
+
+    // Indexed by Type.getSort; Type of primitive sort
+    private static final Type[] TYPE_FROM_SORT = {
+            VOID_TYPE,
+            BOOLEAN_TYPE,
+            CHAR_TYPE,
+            BYTE_TYPE,
+            SHORT_TYPE,
+            INT_TYPE,
+            FLOAT_TYPE,
+            LONG_TYPE,
+            DOUBLE_TYPE,
+    };
+
+    // Indexed by Type.getSort; class name of wrapper class for that primitive sort
+    private static final String[] NAME_PRIMITIVE_WRAPPER = {
+            "java/lang/Void",
+            "java/lang/Boolean",
+            "java/lang/Character",
+            "java/lang/Byte",
+            "java/lang/Short",
+            "java/lang/Integer",
+            "java/lang/Float",
+            "java/lang/Long",
+            "java/lang/Double",
+    };
+
+    // Indexed by Type.getSort; signature character for that primitive sort
+    private static final String[] PRIMITIVE_SIG = {
+            "V",
+            "Z",
+            "C",
+            "B",
+            "S",
+            "I",
+            "F",
+            "J",
+            "D",
+    };
+
+    // Indexed by Type.getSort; name of the unboxing method that primitive sort
+    private static final String[] NAME_UNBOX_METHOD = {
+            "---",
+            "booleanValue",
+            "charValue",
+            "byteValue",
+            "shortValue",
+            "intValue",
+            "floatValue",
+            "longValue",
+            "doubleValue",
+    };
+
+    // Same for all primitives; name of the boxing method
+    private static final String NAME_BOX_METHOD = "valueOf";
+
+    // Table of opcodes for widening primitive conversions; NOP = no conversion
+    private static final int[][] wideningOpcodes = new int[NUM_PRIMITIVE_SORTS][NUM_PRIMITIVE_SORTS];
+
+    // Map from class name to corresponding primitive sort
+    private static final Map<String, Type> unboxed = new HashMap<>();
+
+    static {
+        for (int i=0; i<NUM_PRIMITIVE_SORTS; i++) {
+            for (int j=0; j<NUM_PRIMITIVE_SORTS; j++) {
+                wideningOpcodes[i][j] = NOP;
+            }
+        }
+        wideningOpcodes[Type.BYTE][Type.LONG] = I2L;
+        wideningOpcodes[Type.SHORT][Type.LONG] = I2L;
+        wideningOpcodes[Type.INT][Type.LONG] = I2L;
+        wideningOpcodes[Type.CHAR][Type.LONG] = I2L;
+        wideningOpcodes[Type.FLOAT][Type.LONG] = F2L;
+
+        wideningOpcodes[Type.BYTE][Type.FLOAT] = I2F;
+        wideningOpcodes[Type.SHORT][Type.FLOAT] = I2F;
+        wideningOpcodes[Type.INT][Type.FLOAT] = I2F;
+        wideningOpcodes[Type.CHAR][Type.FLOAT] = I2F;
+        wideningOpcodes[Type.LONG][Type.FLOAT] = L2F;
+
+        wideningOpcodes[Type.BYTE][Type.DOUBLE] = I2D;
+        wideningOpcodes[Type.SHORT][Type.DOUBLE] = I2D;
+        wideningOpcodes[Type.INT][Type.DOUBLE] = I2D;
+        wideningOpcodes[Type.CHAR][Type.DOUBLE] = I2D;
+        wideningOpcodes[Type.FLOAT][Type.DOUBLE] = F2D;
+        wideningOpcodes[Type.LONG][Type.DOUBLE] = L2D;
+
+        for (int i=0; i<NUM_PRIMITIVE_SORTS; i++) {
+            unboxed.put(NAME_PRIMITIVE_WRAPPER[i], TYPE_FROM_SORT[i]);
+        }
+    }
+
+    private static String boxingDescriptor(int typeSort) {
+        return String.format("(%s)L%s;", PRIMITIVE_SIG[typeSort], NAME_PRIMITIVE_WRAPPER[typeSort]);
+    }
+
+    private static String unboxingDescriptor(int typeSort) {
+        return String.format("()%s", PRIMITIVE_SIG[typeSort]);
+    }
+
+    void boxIfPrimitive(Type type) {
+        int sort = type.getSort();
+        if (sort < NAME_PRIMITIVE_WRAPPER.length) {
+            box(type);
+        }
+    }
+
+    void widen(Type sType, Type tType) {
+        int sSort = sType.getSort();
+        int tSort = tType.getSort();
+        if (sSort != tSort) {
+            int opcode = wideningOpcodes[sSort][tSort];
+            if (opcode != NOP) {
+                visitInsn(opcode);
+            }
+        }
+    }
+
+    void box(Type type) {
+        int sort = type.getSort();
+        visitMethodInsn(INVOKESTATIC,
+                NAME_PRIMITIVE_WRAPPER[sort],
+                NAME_BOX_METHOD,
+                boxingDescriptor(sort));
+    }
+
+    void unbox(Type type) {
+        int sort = type.getSort();
+        visitMethodInsn(INVOKEVIRTUAL,
+                NAME_PRIMITIVE_WRAPPER[sort],
+                NAME_UNBOX_METHOD[sort],
+                unboxingDescriptor(sort));
+    }
+
+    void cast(Type sType, Type tType) {
+        cast(sType.getInternalName(), tType.getInternalName());
+    }
+
+    void cast(String sName, Type type) {
+        cast(sName, type.getInternalName());
+    }
+
+    void cast(Type sType, String tName) {
+        cast(sType.getInternalName(), tName);
+    }
+
+    void cast(String sName, String tName) {
+        if (!tName.equals(sName) && !tName.equals(NAME_OBJECT)) {
+            visitTypeInsn(CHECKCAST, tName);
+        }
+    }
+
+    boolean isPrimitive(Type type) {
+        return type.getSort() < NUM_PRIMITIVE_SORTS;
+    }
+
+    Type unboxedType(Type type) {
+        String n = type.getInternalName();
+        return unboxed.get(n);
+    }
+
+    String boxedType(Type type) {
+        return NAME_PRIMITIVE_WRAPPER[type.getSort()];
+    }
+
+    void convertType(Type argType, Type targetType, Type functionalType) {
+        if (argType.equals(targetType) || argType.equals(VOID_TYPE)) {
+            return;
+        }
+
+        if (isPrimitive(argType)) {
+            if (isPrimitive(targetType)) {
+                // Both primitives: widening
+                widen(argType, targetType);
+            } else {
+                // Primitive argument to reference target
+                Type tpType = unboxedType(targetType);
+                if (tpType != null) {
+                    // The target is a boxed primitive type, widen to get there before boxing
+                    widen(argType, tpType);
+                    box(tpType);
+                } else {
+                    // Otherwise, box and cast
+                    box(argType);
+                    cast(boxedType(argType), targetType);
+                }
+            }
+        } else {
+            Type sType;
+            if (isPrimitive(functionalType)) {
+                //@@@ Shold not be possible
+                sType = argType;
+            } else {
+                // Cast to convert to possibly more specific type, and generate CCE for invalid arg
+                sType = functionalType;
+                cast(argType, functionalType);
+            }
+            if (isPrimitive(targetType)) {
+                // Reference argument to primitive target
+                Type spType = unboxedType(sType);
+                if (spType != null) {
+                    // The source type is a boxed primitive, unbox and widen
+                    unbox(spType);
+                    widen(spType, targetType);
+                } else {
+                    //@@@ Should not be possible
+                    // The source type is not primitive, cast to boxed target type, and unbox
+                    cast(sType, boxedType(targetType));
+                    unbox(targetType);
+                }
+            } else {
+                // Both reference types: just case to target type
+                cast(sType, targetType);
+            }
+        }
+    }
+
+    /**
+     * The following methods are copied from org.objectweb.asm.commons.InstructionAdapter.
+     * Part of ASM: a very small and fast Java bytecode manipulation framework.
+     * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved.
+     *
+     * Subclass with that (removing these methods) if that package/class is ever
+     * added to the JDK.
+     */
+
+    void iconst(final int cst) {
+        if (cst >= -1 && cst <= 5) {
+            mv.visitInsn(Opcodes.ICONST_0 + cst);
+        } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
+            mv.visitIntInsn(Opcodes.BIPUSH, cst);
+        } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
+            mv.visitIntInsn(Opcodes.SIPUSH, cst);
+        } else {
+            mv.visitLdcInsn(cst);
+        }
+    }
+
+    void load(final int var, final Type type) {
+        mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), var);
+    }
+
+    void dup() {
+        mv.visitInsn(Opcodes.DUP);
+    }
+
+    void areturn(final Type t) {
+        mv.visitInsn(t.getOpcode(Opcodes.IRETURN));
+    }
+
+    void getfield(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        mv.visitFieldInsn(Opcodes.GETFIELD, owner, name, desc);
+    }
+
+    void invokespecial(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc);
+    }
+
+}
\ No newline at end of file