jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
changeset 26464 65b37da18e06
parent 25991 e48157b42439
child 26465 5ff735dd0d52
--- 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);