8037209: Improvements and cleanups to bytecode assembly for lambda forms
authorvlivanov
Wed, 10 Sep 2014 19:19:46 +0400
changeset 26464 65b37da18e06
parent 26463 d9800c220d8b
child 26465 5ff735dd0d52
8037209: Improvements and cleanups to bytecode assembly for lambda forms Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com
jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java
jdk/src/java.base/share/classes/sun/invoke/util/VerifyType.java
jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Sep 10 16:14:14 2014 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Sep 10 19:19:46 2014 +0400
@@ -260,9 +260,10 @@
         } else {
             names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]);
         }
+        assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]);
         Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
         assert(outArgs[outArgs.length-1] == names[GET_MEMBER]);  // look, shifted args!
-        int result = LambdaForm.LAST_RESULT;
+        int result = LAST_RESULT;
         if (doesAlloc) {
             assert(outArgs[outArgs.length-2] == names[NEW_OBJ]);  // got to move this one
             System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2);
@@ -277,6 +278,16 @@
         return lform;
     }
 
+    static Object findDirectMethodHandle(Name name) {
+        if (name.function == Lazy.NF_internalMemberName ||
+            name.function == Lazy.NF_internalMemberNameEnsureInit ||
+            name.function == Lazy.NF_constructorMethod) {
+            assert(name.arguments.length == 1);
+            return name.arguments[0];
+        }
+        return null;
+    }
+
     private static void maybeCompile(LambdaForm lform, MemberName m) {
         if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
             // Help along bootstrapping...
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Sep 10 16:14:14 2014 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Sep 10 19:19:46 2014 +0400
@@ -25,21 +25,20 @@
 
 package java.lang.invoke;
 
-import sun.invoke.util.VerifyAccess;
-import static java.lang.invoke.LambdaForm.*;
-
-import sun.invoke.util.Wrapper;
-
 import java.io.*;
 import java.util.*;
+import java.lang.reflect.Modifier;
 
 import jdk.internal.org.objectweb.asm.*;
 
-import java.lang.reflect.*;
+import static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.LambdaForm.BasicType.*;
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
-import static java.lang.invoke.LambdaForm.BasicType.*;
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyAccess;
 import sun.invoke.util.VerifyType;
+import sun.invoke.util.Wrapper;
 import sun.reflect.misc.ReflectUtil;
 
 /**
@@ -74,7 +73,11 @@
     private final LambdaForm lambdaForm;
     private final String     invokerName;
     private final MethodType invokerType;
-    private final int[] localsMap;
+
+    /** Info about local variables in compiled lambda form */
+    private final int[]       localsMap;    // index
+    private final BasicType[] localTypes;   // basic type
+    private final Class<?>[]  localClasses; // type
 
     /** ASM bytecode generation. */
     private ClassWriter cw;
@@ -83,6 +86,7 @@
     private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
     private static final Class<?> HOST_CLASS = LambdaForm.class;
 
+    /** Main constructor; other constructors delegate to this one. */
     private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
                                      String className, String invokerName, MethodType invokerType) {
         if (invokerName.contains(".")) {
@@ -98,18 +102,26 @@
         this.lambdaForm = lambdaForm;
         this.invokerName = invokerName;
         this.invokerType = invokerType;
-        this.localsMap = new int[localsMapSize];
+        this.localsMap = new int[localsMapSize+1];
+        // last entry of localsMap is count of allocated local slots
+        this.localTypes = new BasicType[localsMapSize+1];
+        this.localClasses = new Class<?>[localsMapSize+1];
     }
 
+    /** For generating LambdaForm interpreter entry points. */
     private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
         this(null, invokerType.parameterCount(),
              className, invokerName, invokerType);
         // Create an array to map name indexes to locals indexes.
+        localTypes[localTypes.length - 1] = V_TYPE;
         for (int i = 0; i < localsMap.length; i++) {
             localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i);
+            if (i < invokerType.parameterCount())
+                localTypes[i] = basicType(invokerType.parameterType(i));
         }
     }
 
+    /** For generating customized code for a single LambdaForm. */
     private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
         this(form, form.names.length,
              className, form.debugName, invokerType);
@@ -117,7 +129,11 @@
         Name[] names = form.names;
         for (int i = 0, index = 0; i < localsMap.length; i++) {
             localsMap[i] = index;
-            index += names[i].type.basicTypeSlots();
+            if (i < names.length) {
+                BasicType type = names[i].type();
+                index += type.basicTypeSlots();
+                localTypes[i] = type;
+            }
         }
     }
 
@@ -148,7 +164,6 @@
 
     static void maybeDump(final String className, final byte[] classFile) {
         if (DUMP_CLASS_FILES) {
-            System.out.println("dump: " + className);
             java.security.AccessController.doPrivileged(
             new java.security.PrivilegedAction<Void>() {
                 public Void run() {
@@ -156,6 +171,7 @@
                         String dumpName = className;
                         //dumpName = dumpName.replace('/', '-');
                         File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class");
+                        System.out.println("dump: " + dumpFile);
                         dumpFile.getParentFile().mkdirs();
                         FileOutputStream file = new FileOutputStream(dumpFile);
                         file.write(classFile);
@@ -204,7 +220,7 @@
 
     String constantPlaceholder(Object arg) {
         String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
-        if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>";  // debugging aid
+        if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(arg) + ">>";  // debugging aid
         if (cpPatches.containsKey(cpPlaceholder)) {
             throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
         }
@@ -225,6 +241,17 @@
         return res;
     }
 
+    private static String debugString(Object arg) {
+        if (arg instanceof MethodHandle) {
+            MethodHandle mh = (MethodHandle) arg;
+            MemberName member = mh.internalMemberName();
+            if (member != null)
+                return member.toString();
+            return mh.debugString();
+        }
+        return arg.toString();
+    }
+
     /**
      * Extract the number of constant pool entries from a given class file.
      *
@@ -400,6 +427,30 @@
         emitStoreInsn(L_TYPE, index);
     }
 
+    private void freeFrameLocal(int oldFrameLocal) {
+        int i = indexForFrameLocal(oldFrameLocal);
+        if (i < 0)  return;
+        BasicType type = localTypes[i];
+        int newFrameLocal = makeLocalTemp(type);
+        mv.visitVarInsn(loadInsnOpcode(type), oldFrameLocal);
+        mv.visitVarInsn(storeInsnOpcode(type), newFrameLocal);
+        assert(localsMap[i] == oldFrameLocal);
+        localsMap[i] = newFrameLocal;
+        assert(indexForFrameLocal(oldFrameLocal) < 0);
+    }
+    private int indexForFrameLocal(int frameLocal) {
+        for (int i = 0; i < localsMap.length; i++) {
+            if (localsMap[i] == frameLocal && localTypes[i] != V_TYPE)
+                return i;
+        }
+        return -1;
+    }
+    private int makeLocalTemp(BasicType type) {
+        int frameLocal = localsMap[localsMap.length - 1];
+        localsMap[localsMap.length - 1] = frameLocal + type.basicTypeSlots();
+        return frameLocal;
+    }
+
     /**
      * Emit a boxing call.
      *
@@ -421,41 +472,79 @@
         String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
         String name  = wrapper.primitiveSimpleName() + "Value";
         String desc  = "()" + wrapper.basicTypeChar();
-        mv.visitTypeInsn(Opcodes.CHECKCAST, owner);
+        emitReferenceCast(wrapper.wrapperType(), null);
         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false);
     }
 
     /**
-     * Emit an implicit conversion.
+     * Emit an implicit conversion for an argument which must be of the given pclass.
+     * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface.
      *
      * @param ptype type of value present on stack
      * @param pclass type of value required on stack
+     * @param arg compile-time representation of value on stack (Node, constant) or null if none
      */
-    private void emitImplicitConversion(BasicType ptype, Class<?> pclass) {
+    private void emitImplicitConversion(BasicType ptype, Class<?> pclass, Object arg) {
         assert(basicType(pclass) == ptype);  // boxing/unboxing handled by caller
         if (pclass == ptype.basicTypeClass() && ptype != L_TYPE)
             return;   // nothing to do
         switch (ptype) {
-        case L_TYPE:
-            if (VerifyType.isNullConversion(Object.class, pclass))
+            case L_TYPE:
+                if (VerifyType.isNullConversion(Object.class, pclass, false)) {
+                    if (PROFILE_LEVEL > 0)
+                        emitReferenceCast(Object.class, arg);
+                    return;
+                }
+                emitReferenceCast(pclass, arg);
+                return;
+            case I_TYPE:
+                if (!VerifyType.isNullConversion(int.class, pclass, false))
+                    emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass));
                 return;
-            if (isStaticallyNameable(pclass)) {
-                mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
-            } else {
-                mv.visitLdcInsn(constantPlaceholder(pclass));
-                mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
-                mv.visitInsn(Opcodes.SWAP);
-                mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "castReference", CLL_SIG, false);
-                if (pclass.isArray())
-                    mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
+        }
+        throw newInternalError("bad implicit conversion: tc="+ptype+": "+pclass);
+    }
+
+    /** Update localClasses type map.  Return true if the information is already present. */
+    private boolean assertStaticType(Class<?> cls, Name n) {
+        int local = n.index();
+        Class<?> aclass = localClasses[local];
+        if (aclass != null && (aclass == cls || cls.isAssignableFrom(aclass))) {
+            return true;  // type info is already present
+        } else if (aclass == null || aclass.isAssignableFrom(cls)) {
+            localClasses[local] = cls;  // type info can be improved
+        }
+        return false;
+    }
+
+    private void emitReferenceCast(Class<?> cls, Object arg) {
+        Name writeBack = null;  // local to write back result
+        if (arg instanceof Name) {
+            Name n = (Name) arg;
+            if (assertStaticType(cls, n))
+                return;  // this cast was already performed
+            if (lambdaForm.useCount(n) > 1) {
+                // This guy gets used more than once.
+                writeBack = n;
             }
-            return;
-        case I_TYPE:
-            if (!VerifyType.isNullConversion(int.class, pclass))
-                emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass));
-            return;
         }
-        throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass);
+        if (isStaticallyNameable(cls)) {
+            String sig = getInternalName(cls);
+            mv.visitTypeInsn(Opcodes.CHECKCAST, sig);
+        } else {
+            mv.visitLdcInsn(constantPlaceholder(cls));
+            mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
+            mv.visitInsn(Opcodes.SWAP);
+            mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "castReference", CLL_SIG, false);
+            if (Object[].class.isAssignableFrom(cls))
+                mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
+            else if (PROFILE_LEVEL > 0)
+                mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ);
+        }
+        if (writeBack != null) {
+            mv.visitInsn(Opcodes.DUP);
+            emitAstoreInsn(writeBack.index());
+        }
     }
 
     /**
@@ -477,7 +566,11 @@
     }
 
     private static String getInternalName(Class<?> c) {
-        assert(VerifyAccess.isTypeVisible(c, Object.class));
+        if (c == Object.class)             return OBJ;
+        else if (c == Object[].class)      return OBJARY;
+        else if (c == Class.class)         return CLS;
+        else if (c == MethodHandle.class)  return MH;
+        assert(VerifyAccess.isTypeVisible(c, Object.class)) : c.getName();
         return c.getName().replace('.', '/');
     }
 
@@ -506,39 +599,32 @@
 
         // iterate over the form's names, generating bytecode instructions for each
         // start iterating at the first name following the arguments
+        Name onStack = null;
         for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
             Name name = lambdaForm.names[i];
             MemberName member = name.function.member();
+            Class<?> rtype = name.function.methodType().returnType();
+
+            emitStoreResult(onStack);
+            onStack = name;  // unless otherwise modified below
 
             if (isSelectAlternative(i)) {
-                emitSelectAlternative(name, lambdaForm.names[i + 1]);
+                onStack = emitSelectAlternative(name, lambdaForm.names[i + 1]);
                 i++;  // skip MH.invokeBasic of the selectAlternative result
             } else if (isGuardWithCatch(i)) {
-                emitGuardWithCatch(i);
+                onStack = emitGuardWithCatch(i);
                 i = i+2; // Jump to the end of GWC idiom
+            } else if (isNewArray(rtype, name)) {
+                emitNewArray(rtype, name);
             } else if (isStaticallyInvocable(member)) {
-                emitStaticInvoke(member, name);
+                emitStaticInvoke(name);
             } else {
                 emitInvoke(name);
             }
-
-            // Update cached form name's info in case an intrinsic spanning multiple names was encountered.
-            name = lambdaForm.names[i];
-            member = name.function.member();
-
-            // store the result from evaluating to the target name in a local if required
-            // (if this is the last value, i.e., the one that is going to be returned,
-            // avoid store/load/return and just return)
-            if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) {
-                // return value - do nothing
-            } else if (name.type != V_TYPE) {
-                // non-void: actually assign
-                emitStoreInsn(name.type, name.index());
-            }
         }
 
         // return statement
-        emitReturn();
+        emitReturn(onStack);
 
         classFileEpilogue();
         bogusMethod(lambdaForm);
@@ -552,25 +638,24 @@
      * Emit an invoke for the given name.
      */
     void emitInvoke(Name name) {
+        assert(!isLinkerMethodInvoke(name));  // should use the static path for these
         if (true) {
             // push receiver
             MethodHandle target = name.function.resolvedHandle;
             assert(target != null) : name.exprString();
             mv.visitLdcInsn(constantPlaceholder(target));
-            mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+            emitReferenceCast(MethodHandle.class, target);
         } else {
             // load receiver
             emitAloadInsn(0);
-            mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+            emitReferenceCast(MethodHandle.class, null);
             mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
             mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
             // TODO more to come
         }
 
         // push arguments
-        for (int i = 0; i < name.arguments.length; i++) {
-            emitPushArgument(name, i);
-        }
+        emitPushArguments(name);
 
         // invocation
         MethodType type = name.function.methodType();
@@ -585,6 +670,10 @@
         //MethodHandle.class already covered
     };
 
+    static boolean isStaticallyInvocable(Name name) {
+        return isStaticallyInvocable(name.function.member());
+    }
+
     static boolean isStaticallyInvocable(MemberName member) {
         if (member == null)  return false;
         if (member.isConstructor())  return false;
@@ -611,6 +700,8 @@
     }
 
     static boolean isStaticallyNameable(Class<?> cls) {
+        if (cls == Object.class)
+            return true;
         while (cls.isArray())
             cls = cls.getComponentType();
         if (cls.isPrimitive())
@@ -631,12 +722,17 @@
         return false;
     }
 
+    void emitStaticInvoke(Name name) {
+        emitStaticInvoke(name.function.member(), name);
+    }
+
     /**
      * Emit an invoke for the given name, using the MemberName directly.
      */
     void emitStaticInvoke(MemberName member, Name name) {
         assert(member.equals(name.function.member()));
-        String cname = getInternalName(member.getDeclaringClass());
+        Class<?> defc = member.getDeclaringClass();
+        String cname = getInternalName(defc);
         String mname = member.getName();
         String mtype;
         byte refKind = member.getReferenceKind();
@@ -653,9 +749,7 @@
         }
 
         // push arguments
-        for (int i = 0; i < name.arguments.length; i++) {
-            emitPushArgument(name, i);
-        }
+        emitPushArguments(name);
 
         // invocation
         if (member.isMethod()) {
@@ -666,6 +760,54 @@
             mtype = MethodType.toFieldDescriptorString(member.getFieldType());
             mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
         }
+        // Issue a type assertion for the result, so we can avoid casts later.
+        if (name.type == L_TYPE) {
+            Class<?> rtype = member.getInvocationType().returnType();
+            assert(!rtype.isPrimitive());
+            if (rtype != Object.class && !rtype.isInterface()) {
+                assertStaticType(rtype, name);
+            }
+        }
+    }
+
+    boolean isNewArray(Class<?> rtype, Name name) {
+        return rtype.isArray() &&
+                isStaticallyNameable(rtype) &&
+                isArrayBuilder(name.function.resolvedHandle) &&
+                name.arguments.length > 0;
+    }
+
+    void emitNewArray(Class<?> rtype, Name name) throws InternalError {
+        Class<?> arrayElementType = rtype.getComponentType();
+        emitIconstInsn(name.arguments.length);
+        int xas;
+        if (!arrayElementType.isPrimitive()) {
+            mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType));
+            xas = Opcodes.AASTORE;
+        } else {
+            int tc;
+            switch (Wrapper.forPrimitiveType(arrayElementType)) {
+            case BOOLEAN: tc = Opcodes.T_BOOLEAN; xas = Opcodes.BASTORE; break;
+            case BYTE:    tc = Opcodes.T_BYTE;    xas = Opcodes.BASTORE; break;
+            case CHAR:    tc = Opcodes.T_CHAR;    xas = Opcodes.CASTORE; break;
+            case SHORT:   tc = Opcodes.T_SHORT;   xas = Opcodes.SASTORE; break;
+            case INT:     tc = Opcodes.T_INT;     xas = Opcodes.IASTORE; break;
+            case LONG:    tc = Opcodes.T_LONG;    xas = Opcodes.LASTORE; break;
+            case FLOAT:   tc = Opcodes.T_FLOAT;   xas = Opcodes.FASTORE; break;
+            case DOUBLE:  tc = Opcodes.T_DOUBLE;  xas = Opcodes.DASTORE; break;
+            default:      throw new InternalError(rtype.getName());
+            }
+            mv.visitIntInsn(Opcodes.NEWARRAY, tc);
+        }
+        // store arguments
+        for (int i = 0; i < name.arguments.length; i++) {
+            mv.visitInsn(Opcodes.DUP);
+            emitIconstInsn(i);
+            emitPushArgument(name, i);
+            mv.visitInsn(xas);
+        }
+        // the array is left on the stack
+        assertStaticType(rtype, name);
     }
     int refKindOpcode(byte refKind) {
         switch (refKind) {
@@ -681,6 +823,24 @@
         throw new InternalError("refKind="+refKind);
     }
 
+    static boolean isArrayBuilder(MethodHandle fn) {
+        if (fn == null)
+            return false;
+        MethodType mtype = fn.type();
+        Class<?> rtype = mtype.returnType();
+        Class<?> arrayElementType = rtype.getComponentType();
+        if (arrayElementType == null)
+            return false;
+        List<Class<?>> ptypes = mtype.parameterList();
+        int size = ptypes.size();
+        if (!ptypes.equals(Collections.nCopies(size, arrayElementType)))
+            return false;
+        // Assume varargsArray caches pointers.
+        if (fn != ValueConversions.varargsArray(rtype, size))
+            return false;
+        return true;
+    }
+
     /**
      * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
      */
@@ -708,6 +868,21 @@
     }
 
     /**
+     * Check if MemberName is a call to MethodHandle.linkToStatic, etc.
+     */
+    private boolean isLinkerMethodInvoke(Name name) {
+        if (name.function == null)
+            return false;
+        if (name.arguments.length < 1)
+            return false;  // must have MH argument
+        MemberName member = name.function.member();
+        return member != null &&
+               member.getDeclaringClass() == MethodHandle.class &&
+               !member.isPublic() && member.isStatic() &&
+               member.getName().startsWith("linkTo");
+    }
+
+    /**
      * Check if i-th name is a call to MethodHandleImpl.selectAlternative.
      */
     private boolean isSelectAlternative(int pos) {
@@ -755,7 +930,9 @@
      *     t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
      * }</pre></blockquote>
      */
-    private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
+    private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
+        assert isStaticallyInvocable(invokeBasicName);
+
         Name receiver = (Name) invokeBasicName.arguments[0];
 
         Label L_fallback = new Label();
@@ -763,15 +940,15 @@
 
         // load test result
         emitPushArgument(selectAlternativeName, 0);
-        mv.visitInsn(Opcodes.ICONST_1);
 
         // if_icmpne L_fallback
-        mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
+        mv.visitJumpInsn(Opcodes.IFEQ, L_fallback);
 
         // invoke selectAlternativeName.arguments[1]
+        Class<?>[] preForkClasses = localClasses.clone();
         emitPushArgument(selectAlternativeName, 1);  // get 2nd argument of selectAlternative
         emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
-        emitInvoke(invokeBasicName);
+        emitStaticInvoke(invokeBasicName);
 
         // goto L_done
         mv.visitJumpInsn(Opcodes.GOTO, L_done);
@@ -780,12 +957,17 @@
         mv.visitLabel(L_fallback);
 
         // invoke selectAlternativeName.arguments[2]
+        System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length);
         emitPushArgument(selectAlternativeName, 2);  // get 3rd argument of selectAlternative
         emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
-        emitInvoke(invokeBasicName);
+        emitStaticInvoke(invokeBasicName);
 
         // L_done:
         mv.visitLabel(L_done);
+        // for now do not bother to merge typestate; just reset to the dominator state
+        System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length);
+
+        return invokeBasicName;  // return what's on stack
     }
 
     /**
@@ -808,7 +990,7 @@
       *      return a3.invokeBasic(ex, a6, a7);
       *  }}
       */
-    private void emitGuardWithCatch(int pos) {
+    private Name emitGuardWithCatch(int pos) {
         Name args    = lambdaForm.names[pos];
         Name invoker = lambdaForm.names[pos+1];
         Name result  = lambdaForm.names[pos+2];
@@ -859,6 +1041,12 @@
         mv.visitInsn(Opcodes.ATHROW);
 
         mv.visitLabel(L_done);
+
+        return result;
+    }
+
+    private void emitPushArguments(Name args) {
+        emitPushArguments(args, 0);
     }
 
     private void emitPushArguments(Name args, int start) {
@@ -878,7 +1066,7 @@
         if (arg instanceof Name) {
             Name n = (Name) arg;
             emitLoadInsn(n.type, n.index());
-            emitImplicitConversion(n.type, ptype);
+            emitImplicitConversion(n.type, ptype, n);
         } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) {
             emitConst(arg);
         } else {
@@ -886,15 +1074,25 @@
                 emitConst(arg);
             } else {
                 mv.visitLdcInsn(constantPlaceholder(arg));
-                emitImplicitConversion(L_TYPE, ptype);
+                emitImplicitConversion(L_TYPE, ptype, arg);
             }
         }
     }
 
     /**
+     * Store the name to its local, if necessary.
+     */
+    private void emitStoreResult(Name name) {
+        if (name != null && name.type != V_TYPE) {
+            // non-void: actually assign
+            emitStoreInsn(name.type, name.index());
+        }
+    }
+
+    /**
      * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
      */
-    private void emitReturn() {
+    private void emitReturn(Name onStack) {
         // return statement
         Class<?> rclass = invokerType.returnType();
         BasicType rtype = lambdaForm.returnType();
@@ -907,12 +1105,11 @@
             LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
 
             // put return value on the stack if it is not already there
-            if (lambdaForm.result != lambdaForm.names.length - 1 ||
-                    lambdaForm.result < lambdaForm.arity) {
-                emitLoadInsn(rn.type, lambdaForm.result);
+            if (rn != onStack) {
+                emitLoadInsn(rtype, lambdaForm.result);
             }
 
-            emitImplicitConversion(rtype, rclass);
+            emitImplicitConversion(rtype, rclass, rn);
 
             // generate actual return statement
             emitReturnInsn(rtype);
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Wed Sep 10 16:14:14 2014 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Wed Sep 10 19:19:46 2014 +0400
@@ -464,7 +464,7 @@
         return sig.indexOf('_');
     }
     static BasicType signatureReturn(String sig) {
-        return basicType(sig.charAt(signatureArity(sig)+1));
+        return basicType(sig.charAt(signatureArity(sig) + 1));
     }
     static boolean isValidSignature(String sig) {
         int arity = sig.indexOf('_');
@@ -582,7 +582,7 @@
             isCompiled = true;
             return vmentry;
         } catch (Error | Exception ex) {
-            throw newInternalError("compileToBytecode", ex);
+            throw newInternalError("compileToBytecode: " + this, ex);
         }
     }
 
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 16:14:14 2014 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 19:19:46 2014 +0400
@@ -109,7 +109,7 @@
         }
         static String name(Class<?> arrayClass, boolean isSetter) {
             Class<?> elemClass = arrayClass.getComponentType();
-            if (elemClass == null)  throw new IllegalArgumentException();
+            if (elemClass == null)  throw newIllegalArgumentException("not an array", arrayClass);
             return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass);
         }
         static final boolean USE_WEAKLY_TYPED_ARRAY_ACCESSORS = false;  // FIXME: decide
@@ -179,13 +179,17 @@
         for (int i = 0; i <= INARG_COUNT; i++) {
             Class<?> src = (i == INARG_COUNT) ? dstType.returnType() : srcType.parameterType(i);
             Class<?> dst = (i == INARG_COUNT) ? srcType.returnType() : dstType.parameterType(i);
-            if (!VerifyType.isNullConversion(src, dst) ||
+            if (!VerifyType.isNullConversion(src, dst, false) ||
                 level <= 1 && dst.isInterface() && !dst.isAssignableFrom(src)) {
                 needConv[i] = true;
                 conversions++;
             }
         }
         boolean retConv = needConv[INARG_COUNT];
+        if (retConv && srcType.returnType() == void.class) {
+            retConv = false;
+            conversions--;
+        }
 
         final int IN_MH         = 0;
         final int INARG_BASE    = 1;
@@ -193,6 +197,7 @@
         final int NAME_LIMIT    = INARG_LIMIT + conversions + 1;
         final int RETURN_CONV   = (!retConv ? -1         : NAME_LIMIT - 1);
         final int OUT_CALL      = (!retConv ? NAME_LIMIT : RETURN_CONV) - 1;
+        final int RESULT        = (srcType.returnType() == void.class ? -1 : NAME_LIMIT - 1);
 
         // Now build a LambdaForm.
         MethodType lambdaType = srcType.basicType().invokerType();
@@ -230,7 +235,7 @@
                 if (dst.isPrimitive()) {
                     // Caller has boxed a primitive.  Unbox it for the target.
                     Wrapper w = Wrapper.forPrimitiveType(dst);
-                    if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType())) {
+                    if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType(), false)) {
                         fn = ValueConversions.unbox(dst);
                     } else if (src == Object.class || !Wrapper.isWrapperType(src)) {
                         // Examples:  Object->int, Number->int, Comparable->int; Byte->int, Character->int
@@ -289,7 +294,7 @@
             assert(RETURN_CONV == names.length-1);
         }
 
-        LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names);
+        LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names, RESULT);
         return SimpleMethodHandle.make(srcType, form);
     }
 
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java	Wed Sep 10 16:14:14 2014 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java	Wed Sep 10 19:19:46 2014 +0400
@@ -46,8 +46,10 @@
     static final boolean TRACE_INTERPRETER;
     static final boolean TRACE_METHOD_LINKAGE;
     static final Integer COMPILE_THRESHOLD;
+    static final int PROFILE_LEVEL;
+
     static {
-        final Object[] values = { false, false, false, false, null };
+        final Object[] values = { false, false, false, false, null, null };
         AccessController.doPrivileged(new PrivilegedAction<Void>() {
                 public Void run() {
                     values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
@@ -55,6 +57,7 @@
                     values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
                     values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
                     values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
+                    values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0);
                     return null;
                 }
             });
@@ -63,6 +66,7 @@
         TRACE_INTERPRETER         = (Boolean) values[2];
         TRACE_METHOD_LINKAGE      = (Boolean) values[3];
         COMPILE_THRESHOLD         = (Integer) values[4];
+        PROFILE_LEVEL             = (Integer) values[5];
     }
 
     /** Tell if any of the debugging switches are turned on.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Wed Sep 10 16:14:14 2014 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Wed Sep 10 19:19:46 2014 +0400
@@ -729,13 +729,13 @@
 
     /*non-public*/
     boolean isViewableAs(MethodType newType) {
-        if (!VerifyType.isNullConversion(returnType(), newType.returnType()))
+        if (!VerifyType.isNullConversion(returnType(), newType.returnType(), true))
             return false;
         int argc = parameterCount();
         if (argc != newType.parameterCount())
             return false;
         for (int i = 0; i < argc; i++) {
-            if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i)))
+            if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i), true))
                 return false;
         }
         return true;
--- a/jdk/src/java.base/share/classes/sun/invoke/util/VerifyType.java	Wed Sep 10 16:14:14 2014 +0100
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/VerifyType.java	Wed Sep 10 19:19:46 2014 +0400
@@ -40,18 +40,38 @@
     /**
      * True if a value can be stacked as the source type and unstacked as the
      * destination type, without violating the JVM's type consistency.
+     * <p>
+     * If both types are references, we apply the verifier's subclass check
+     * (or subtyping, if keepInterfaces).
+     * If the src type is a type guaranteed to be null (Void) it can be converted
+     * to any other reference type.
+     * <p>
+     * If both types are primitives, we apply the verifier's primitive conversions.
+     * These do not include Java conversions such as long to double, since those
+     * require computation and (in general) stack depth changes.
+     * But very simple 32-bit viewing changes, such as byte to int,
+     * are null conversions, because they do not require any computation.
+     * These conversions are from any type to a wider type up to 32 bits,
+     * as long as the conversion is not signed to unsigned (byte to char).
+     * <p>
+     * The primitive type 'void' does not interconvert with any other type,
+     * even though it is legal to drop any type from the stack and "return void".
+     * The stack effects, though are different between void and any other type,
+     * so it is safer to report a non-trivial conversion.
      *
      * @param src the type of a stacked value
      * @param dst the type by which we'd like to treat it
+     * @param keepInterfaces if false, we treat any interface as if it were Object
      * @return whether the retyping can be done without motion or reformatting
      */
-    public static boolean isNullConversion(Class<?> src, Class<?> dst) {
+    public static boolean isNullConversion(Class<?> src, Class<?> dst, boolean keepInterfaces) {
         if (src == dst)            return true;
         // Verifier allows any interface to be treated as Object:
-        if (dst.isInterface())     dst = Object.class;
-        if (src.isInterface())     src = Object.class;
-        if (src == dst)            return true;  // check again
-        if (dst == void.class)     return true;  // drop any return value
+        if (!keepInterfaces) {
+            if (dst.isInterface())  dst = Object.class;
+            if (src.isInterface())  src = Object.class;
+            if (src == dst)         return true;  // check again
+        }
         if (isNullType(src))       return !dst.isPrimitive();
         if (!src.isPrimitive())    return dst.isAssignableFrom(src);
         if (!dst.isPrimitive())    return false;
@@ -82,25 +102,13 @@
      * Is the given type java.lang.Null or an equivalent null-only type?
      */
     public static boolean isNullType(Class<?> type) {
-        if (type == null)  return false;
-        return type == NULL_CLASS
-            // This one may also be used as a null type.
-            // TO DO: Decide if we really want to legitimize it here.
-            // Probably we do, unless java.lang.Null really makes it into Java 7
-            //|| type == Void.class
-            // Locally known null-only class:
-            || type == Empty.class
-            ;
-    }
-    private static final Class<?> NULL_CLASS;
-    static {
-        Class<?> nullClass = null;
-        try {
-            nullClass = Class.forName("java.lang.Null");
-        } catch (ClassNotFoundException ex) {
-            // OK, we'll cope
-        }
-        NULL_CLASS = nullClass;
+        // Any reference statically typed as Void is guaranteed to be null.
+        // Therefore, it can be safely treated as a value of any
+        // other type that admits null, i.e., a reference type.
+        if (type == Void.class)  return true;
+        // Locally known null-only class:
+        if (type == Empty.class)  return true;
+        return false;
     }
 
     /**
@@ -111,14 +119,14 @@
      * @param recv the type of the method handle receiving the call
      * @return whether the retyping can be done without motion or reformatting
      */
-    public static boolean isNullConversion(MethodType call, MethodType recv) {
+    public static boolean isNullConversion(MethodType call, MethodType recv, boolean keepInterfaces) {
         if (call == recv)  return true;
         int len = call.parameterCount();
         if (len != recv.parameterCount())  return false;
         for (int i = 0; i < len; i++)
-            if (!isNullConversion(call.parameterType(i), recv.parameterType(i)))
+            if (!isNullConversion(call.parameterType(i), recv.parameterType(i), keepInterfaces))
                 return false;
-        return isNullConversion(recv.returnType(), call.returnType());
+        return isNullConversion(recv.returnType(), call.returnType(), keepInterfaces);
     }
 
     /**
--- a/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java	Wed Sep 10 16:14:14 2014 +0100
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java	Wed Sep 10 19:19:46 2014 +0400
@@ -230,14 +230,6 @@
      */
     public <T> T zero(Class<T> type) { return convert(zero, type); }
 
-//    /** Produce a wrapper for the given wrapper or primitive type. */
-//    public static Wrapper valueOf(Class<?> type) {
-//        if (isPrimitiveType(type))
-//            return forPrimitiveType(type);
-//        else
-//            return forWrapperType(type);
-//    }
-
     /** Return the wrapper that wraps values of the given type.
      *  The type may be {@code Object}, meaning the {@code OBJECT} wrapper.
      *  Otherwise, the type must be a primitive.