6939861: JVM should handle more conversion operations
authorjrose
Thu, 12 May 2011 19:27:49 -0700
changeset 9646 5ebbe5ab084f
parent 9645 dabb5e4edc4c
child 9647 9fc3d991dc63
child 9730 e4b334d47f4b
6939861: JVM should handle more conversion operations Summary: Integrate JDK code with JVM-supplied ricochet frames. Reviewed-by: never, twisti
jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java
jdk/src/share/classes/java/lang/invoke/FilterGeneric.java
jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java
jdk/src/share/classes/java/lang/invoke/FromGeneric.java
jdk/src/share/classes/java/lang/invoke/Invokers.java
jdk/src/share/classes/java/lang/invoke/MemberName.java
jdk/src/share/classes/java/lang/invoke/MethodHandle.java
jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java
jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java
jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java
jdk/src/share/classes/java/lang/invoke/MethodHandles.java
jdk/src/share/classes/java/lang/invoke/MethodType.java
jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java
jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java
jdk/src/share/classes/java/lang/invoke/ToGeneric.java
jdk/src/share/classes/sun/invoke/util/ValueConversions.java
jdk/src/share/classes/sun/invoke/util/VerifyType.java
jdk/src/share/classes/sun/invoke/util/Wrapper.java
jdk/test/java/lang/invoke/6998541/Test6998541.java
jdk/test/java/lang/invoke/InvokeGenericTest.java
jdk/test/java/lang/invoke/MethodHandlesTest.java
jdk/test/java/lang/invoke/RicochetTest.java
jdk/test/sun/invoke/util/ValueConversionsTest.java
--- a/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java	Thu May 12 19:27:49 2011 -0700
@@ -27,6 +27,7 @@
 
 import sun.invoke.util.VerifyType;
 import sun.invoke.util.Wrapper;
+import sun.invoke.util.ValueConversions;
 import java.util.Arrays;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.MethodHandleStatics.*;
@@ -55,29 +56,35 @@
         this(target, newType, conv, null);
     }
 
+    int getConversion() { return conversion; }
+
     // TO DO:  When adapting another MH with a null conversion, clone
     // the target and change its type, instead of adding another layer.
 
     /** Can a JVM-level adapter directly implement the proposed
      *  argument conversions, as if by MethodHandles.convertArguments?
      */
-    static boolean canPairwiseConvert(MethodType newType, MethodType oldType) {
+    static boolean canPairwiseConvert(MethodType newType, MethodType oldType, int level) {
         // same number of args, of course
         int len = newType.parameterCount();
         if (len != oldType.parameterCount())
             return false;
 
-        // Check return type.  (Not much can be done with it.)
+        // Check return type.
         Class<?> exp = newType.returnType();
         Class<?> ret = oldType.returnType();
-        if (!VerifyType.isNullConversion(ret, exp))
-            return false;
+        if (!VerifyType.isNullConversion(ret, exp)) {
+            if (!convOpSupported(OP_COLLECT_ARGS))
+                return false;
+            if (!canConvertArgument(ret, exp, level))
+                return false;
+        }
 
         // Check args pairwise.
         for (int i = 0; i < len; i++) {
             Class<?> src = newType.parameterType(i); // source type
             Class<?> dst = oldType.parameterType(i); // destination type
-            if (!canConvertArgument(src, dst))
+            if (!canConvertArgument(src, dst, level))
                 return false;
         }
 
@@ -87,11 +94,14 @@
     /** Can a JVM-level adapter directly implement the proposed
      *  argument conversion, as if by MethodHandles.convertArguments?
      */
-    static boolean canConvertArgument(Class<?> src, Class<?> dst) {
+    static boolean canConvertArgument(Class<?> src, Class<?> dst, int level) {
         // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
         // so we don't need to repeat so much decision making.
         if (VerifyType.isNullConversion(src, dst)) {
             return true;
+        } else if (convOpSupported(OP_COLLECT_ARGS)) {
+            // If we can build filters, we can convert anything to anything.
+            return true;
         } else if (src.isPrimitive()) {
             if (dst.isPrimitive())
                 return canPrimCast(src, dst);
@@ -99,7 +109,7 @@
                 return canBoxArgument(src, dst);
         } else {
             if (dst.isPrimitive())
-                return canUnboxArgument(src, dst);
+                return canUnboxArgument(src, dst, level);
             else
                 return true;  // any two refs can be interconverted
         }
@@ -109,21 +119,20 @@
      * Create a JVM-level adapter method handle to conform the given method
      * handle to the similar newType, using only pairwise argument conversions.
      * For each argument, convert incoming argument to the exact type needed.
-     * Only null conversions are allowed on the return value (until
-     * the JVM supports ricochet adapters).
-     * The argument conversions allowed are casting, unboxing,
+     * The argument conversions allowed are casting, boxing and unboxing,
      * integral widening or narrowing, and floating point widening or narrowing.
      * @param newType required call type
      * @param target original method handle
+     * @param level which strength of conversion is allowed
      * @return an adapter to the original handle with the desired new type,
      *          or the original target if the types are already identical
      *          or null if the adaptation cannot be made
      */
-    static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target) {
+    static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target, int level) {
         MethodType oldType = target.type();
         if (newType == oldType)  return target;
 
-        if (!canPairwiseConvert(newType, oldType))
+        if (!canPairwiseConvert(newType, oldType, level))
             return null;
         // (after this point, it is an assertion error to fail to convert)
 
@@ -138,9 +147,14 @@
                 break;
             }
         }
+
+        Class<?> needReturn = newType.returnType();
+        Class<?> haveReturn = oldType.returnType();
+        boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
+
         // Now build a chain of one or more adapters.
-        MethodHandle adapter = target;
-        MethodType midType = oldType.changeReturnType(newType.returnType());
+        MethodHandle adapter = target, adapter2;
+        MethodType midType = oldType;
         for (int i = 0; i <= lastConv; i++) {
             Class<?> src = newType.parameterType(i); // source type
             Class<?> dst = midType.parameterType(i); // destination type
@@ -149,22 +163,23 @@
                 continue;
             }
             // Work the current type backward toward the desired caller type:
-            if (i != lastConv) {
-                midType = midType.changeParameterType(i, src);
-            } else {
+            midType = midType.changeParameterType(i, src);
+            if (i == lastConv) {
                 // When doing the last (or only) real conversion,
                 // force all remaining null conversions to happen also.
-                assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src)));
-                midType = newType;
+                MethodType lastMidType = newType;
+                if (retConv)  lastMidType = lastMidType.changeReturnType(haveReturn);
+                assert(VerifyType.isNullConversion(lastMidType, midType));
+                midType = lastMidType;
             }
 
             // Tricky case analysis follows.
             // It parallels canConvertArgument() above.
             if (src.isPrimitive()) {
                 if (dst.isPrimitive()) {
-                    adapter = makePrimCast(midType, adapter, i, dst);
+                    adapter2 = makePrimCast(midType, adapter, i, dst);
                 } else {
-                    adapter = makeBoxArgument(midType, adapter, i, dst);
+                    adapter2 = makeBoxArgument(midType, adapter, i, src);
                 }
             } else {
                 if (dst.isPrimitive()) {
@@ -174,29 +189,53 @@
                     // conversions supported by reflect.Method.invoke.
                     // Those conversions require a big nest of if/then/else logic,
                     // which we prefer to make a user responsibility.
-                    adapter = makeUnboxArgument(midType, adapter, i, dst);
+                    adapter2 = makeUnboxArgument(midType, adapter, i, dst, level);
                 } else {
                     // Simple reference conversion.
                     // Note:  Do not check for a class hierarchy relation
                     // between src and dst.  In all cases a 'null' argument
                     // will pass the cast conversion.
-                    adapter = makeCheckCast(midType, adapter, i, dst);
+                    adapter2 = makeCheckCast(midType, adapter, i, dst);
                 }
             }
-            assert(adapter != null);
-            assert(adapter.type() == midType);
+            assert(adapter2 != null) : Arrays.asList(src, dst, midType, adapter, i, target, newType);
+            assert(adapter2.type() == midType);
+            adapter = adapter2;
+        }
+        if (retConv) {
+            adapter2 = makeReturnConversion(adapter, haveReturn, needReturn);
+            assert(adapter2 != null);
+            adapter = adapter2;
         }
         if (adapter.type() != newType) {
             // Only trivial conversions remain.
-            adapter = makeRetypeOnly(newType, adapter);
-            assert(adapter != null);
+            adapter2 = makeRetypeOnly(newType, adapter);
+            assert(adapter2 != null);
+            adapter = adapter2;
             // Actually, that's because there were no non-trivial ones:
-            assert(lastConv == -1);
+            assert(lastConv == -1 || retConv);
         }
         assert(adapter.type() == newType);
         return adapter;
     }
 
+    private static MethodHandle makeReturnConversion(MethodHandle target, Class<?> haveReturn, Class<?> needReturn) {
+        MethodHandle adjustReturn;
+        if (haveReturn == void.class) {
+            // synthesize a zero value for the given void
+            Object zero = Wrapper.forBasicType(needReturn).zero();
+            adjustReturn = MethodHandles.constant(needReturn, zero);
+        } else {
+            MethodType needConversion = MethodType.methodType(needReturn, haveReturn);
+            adjustReturn = MethodHandles.identity(needReturn).asType(needConversion);
+        }
+        if (!canCollectArguments(adjustReturn.type(), target.type(), 0, false)) {
+            assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this code is deprecated
+            throw new InternalError("NYI");
+        }
+        return makeCollectArguments(adjustReturn, target, 0, false);
+    }
+
     /**
      * Create a JVM-level adapter method handle to permute the arguments
      * of the given method.
@@ -224,7 +263,7 @@
         if (argumentMap.length != oldType.parameterCount())
             throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
         if (nullPermutation) {
-            MethodHandle res = makePairwiseConvert(newType, target);
+            MethodHandle res = makePairwiseConvert(newType, target, 0);
             // well, that was easy
             if (res == null)
                 throw newIllegalArgumentException("cannot convert pairwise: "+newType);
@@ -310,11 +349,25 @@
         return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT;
     }
 
+    static int extractStackMove(int convOp) {
+        int spChange = convOp >> CONV_STACK_MOVE_SHIFT;
+        return spChange / MethodHandleNatives.JVM_STACK_MOVE_UNIT;
+    }
+
+    static int extractStackMove(MethodHandle target) {
+        if (target instanceof AdapterMethodHandle) {
+            AdapterMethodHandle amh = (AdapterMethodHandle) target;
+            return extractStackMove(amh.getConversion());
+        } else {
+            return 0;
+        }
+    }
+
     /** Construct an adapter conversion descriptor for a single-argument conversion. */
     private static long makeConv(int convOp, int argnum, int src, int dest) {
-        assert(src  == (src  & 0xF));
-        assert(dest == (dest & 0xF));
-        assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF);
+        assert(src  == (src  & CONV_TYPE_MASK));
+        assert(dest == (dest & CONV_TYPE_MASK));
+        assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF || convOp == OP_COLLECT_ARGS);
         int stackMove = type2size(dest) - type2size(src);
         return ((long) argnum << 32 |
                 (long) convOp << CONV_OP_SHIFT |
@@ -323,11 +376,10 @@
                 insertStackMove(stackMove)
                 );
     }
-    private static long makeConv(int convOp, int argnum, int stackMove) {
-        assert(convOp >= OP_DUP_ARGS && convOp <= OP_SPREAD_ARGS);
+    private static long makeDupConv(int convOp, int argnum, int stackMove) {
+        // simple argument motion, requiring one slot to specify
+        assert(convOp == OP_DUP_ARGS || convOp == OP_DROP_ARGS);
         byte src = 0, dest = 0;
-        if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS)
-            src = dest = T_OBJECT;
         return ((long) argnum << 32 |
                 (long) convOp << CONV_OP_SHIFT |
                 (int)  src    << CONV_SRC_TYPE_SHIFT |
@@ -336,7 +388,8 @@
                 );
     }
     private static long makeSwapConv(int convOp, int srcArg, byte type, int destSlot) {
-        assert(convOp >= OP_SWAP_ARGS && convOp <= OP_ROT_ARGS);
+        // more complex argument motion, requiring two slots to specify
+        assert(convOp == OP_SWAP_ARGS || convOp == OP_ROT_ARGS);
         return ((long) srcArg << 32 |
                 (long) convOp << CONV_OP_SHIFT |
                 (int)  type   << CONV_SRC_TYPE_SHIFT |
@@ -344,6 +397,18 @@
                 (int)  destSlot << CONV_VMINFO_SHIFT
                 );
     }
+    private static long makeSpreadConv(int convOp, int argnum, int src, int dest, int stackMove) {
+        // spreading or collecting, at a particular slot location
+        assert(convOp == OP_SPREAD_ARGS || convOp == OP_COLLECT_ARGS || convOp == OP_FOLD_ARGS);
+        // src  = spread ? T_OBJECT (for array)  : common type of collected args (else void)
+        // dest = spread ? element type of array : result type of collector (can be void)
+        return ((long) argnum << 32 |
+                (long) convOp << CONV_OP_SHIFT |
+                (int)  src    << CONV_SRC_TYPE_SHIFT |
+                (int)  dest   << CONV_DEST_TYPE_SHIFT |
+                insertStackMove(stackMove)
+                );
+    }
     private static long makeConv(int convOp) {
         assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW);
         return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT);   // stackMove, src, dst all zero
@@ -570,14 +635,10 @@
     static boolean canPrimCast(Class<?> src, Class<?> dst) {
         if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
             return false;
-        } else if (Wrapper.forPrimitiveType(dst).isFloating()) {
-            // both must be floating types
-            return Wrapper.forPrimitiveType(src).isFloating();
         } else {
-            // both are integral, and all combinations work fine
-            assert(Wrapper.forPrimitiveType(src).isIntegral() &&
-                   Wrapper.forPrimitiveType(dst).isIntegral());
-            return true;
+            boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
+            boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
+            return !(sflt | dflt);  // no float support at present
         }
     }
 
@@ -589,6 +650,29 @@
      */
     static MethodHandle makePrimCast(MethodType newType, MethodHandle target,
                 int arg, Class<?> convType) {
+        Class<?> src = newType.parameterType(arg);
+        if (canPrimCast(src, convType))
+            return makePrimCastOnly(newType, target, arg, convType);
+        Class<?> dst = convType;
+        boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
+        boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
+        if (sflt | dflt) {
+            MethodHandle convMethod;
+            if (sflt)
+                convMethod = ((src == double.class)
+                        ? ValueConversions.convertFromDouble(dst)
+                        : ValueConversions.convertFromFloat(dst));
+            else
+                convMethod = ((dst == double.class)
+                        ? ValueConversions.convertToDouble(src)
+                        : ValueConversions.convertToFloat(src));
+            long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
+            return new AdapterMethodHandle(target, newType, conv, convMethod);
+        }
+        throw new InternalError("makePrimCast");
+    }
+    static MethodHandle makePrimCastOnly(MethodType newType, MethodHandle target,
+                int arg, Class<?> convType) {
         MethodType oldType = target.type();
         if (!canPrimCast(newType, oldType, arg, convType))
             return null;
@@ -602,7 +686,7 @@
      *  The convType is the unboxed type; it can be either a primitive or wrapper.
      */
     static boolean canUnboxArgument(MethodType newType, MethodType targetType,
-                int arg, Class<?> convType) {
+                int arg, Class<?> convType, int level) {
         if (!convOpSupported(OP_REF_TO_PRIM))  return false;
         Class<?> src = newType.parameterType(arg);
         Class<?> dst = targetType.parameterType(arg);
@@ -616,21 +700,31 @@
         return (diff == arg+1);  // arg is sole non-trivial diff
     }
     /** Can an primitive unboxing adapter validly convert src to dst? */
-    static boolean canUnboxArgument(Class<?> src, Class<?> dst) {
-        return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive());
+    static boolean canUnboxArgument(Class<?> src, Class<?> dst, int level) {
+        assert(dst.isPrimitive());
+        // if we have JVM support for boxing, we can also do complex unboxing
+        if (convOpSupported(OP_PRIM_TO_REF))  return true;
+        Wrapper dw = Wrapper.forPrimitiveType(dst);
+        // Level 0 means cast and unbox.  This works on any reference.
+        if (level == 0)  return !src.isPrimitive();
+        assert(level >= 0 && level <= 2);
+        // Levels 1 and 2 allow widening and/or narrowing conversions.
+        // These are not supported directly by the JVM.
+        // But if the input reference is monomorphic, we can do it.
+        return dw.wrapperType() == src;
     }
 
     /** Factory method:  Unbox the given argument.
      *  Return null if this cannot be done.
      */
     static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target,
-                int arg, Class<?> convType) {
+                int arg, Class<?> convType, int level) {
         MethodType oldType = target.type();
         Class<?> src = newType.parameterType(arg);
         Class<?> dst = oldType.parameterType(arg);
         Class<?> boxType = Wrapper.asWrapperType(convType);
         Class<?> primType = Wrapper.asPrimitiveType(convType);
-        if (!canUnboxArgument(newType, oldType, arg, convType))
+        if (!canUnboxArgument(newType, oldType, arg, convType, level))
             return null;
         MethodType castDone = newType;
         if (!VerifyType.isNullConversion(src, boxType))
@@ -642,19 +736,46 @@
         return makeCheckCast(newType, adapter, arg, boxType);
     }
 
+    /** Can a boxing conversion validly convert src to dst? */
+    static boolean canBoxArgument(MethodType newType, MethodType targetType,
+                int arg, Class<?> convType) {
+        if (!convOpSupported(OP_PRIM_TO_REF))  return false;
+        Class<?> src = newType.parameterType(arg);
+        Class<?> dst = targetType.parameterType(arg);
+        Class<?> boxType = Wrapper.asWrapperType(convType);
+        convType = Wrapper.asPrimitiveType(convType);
+        if (!canCheckCast(boxType, dst)
+                || boxType == convType
+                || !VerifyType.isNullConversion(src, convType))
+            return false;
+        int diff = diffTypes(newType, targetType, false);
+        return (diff == arg+1);  // arg is sole non-trivial diff
+    }
+
     /** Can an primitive boxing adapter validly convert src to dst? */
     static boolean canBoxArgument(Class<?> src, Class<?> dst) {
         if (!convOpSupported(OP_PRIM_TO_REF))  return false;
-        throw new UnsupportedOperationException("NYI");
+        return (src.isPrimitive() && !dst.isPrimitive());
     }
 
-    /** Factory method:  Unbox the given argument.
+    /** Factory method:  Box the given argument.
      *  Return null if this cannot be done.
      */
     static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target,
                 int arg, Class<?> convType) {
-        // this is difficult to do in the JVM because it must GC
-        return null;
+        MethodType oldType = target.type();
+        Class<?> src = newType.parameterType(arg);
+        Class<?> dst = oldType.parameterType(arg);
+        Class<?> boxType = Wrapper.asWrapperType(convType);
+        Class<?> primType = Wrapper.asPrimitiveType(convType);
+        if (!canBoxArgument(newType, oldType, arg, convType)) {
+            return null;
+        }
+        if (!VerifyType.isNullConversion(boxType, dst))
+            target = makeCheckCast(oldType.changeParameterType(arg, boxType), target, arg, dst);
+        MethodHandle boxerMethod = ValueConversions.box(Wrapper.forPrimitiveType(primType));
+        long conv = makeConv(OP_PRIM_TO_REF, arg, basicType(primType), T_OBJECT);
+        return new AdapterMethodHandle(target, newType, conv, boxerMethod);
     }
 
     /** Can an adapter simply drop arguments to convert the target to newType? */
@@ -699,7 +820,7 @@
         int slotCount   = keep1InSlot - dropSlot;
         assert(slotCount >= dropArgCount);
         assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount());
-        long conv = makeConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
+        long conv = makeDupConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
         return new AdapterMethodHandle(target, newType, conv);
     }
 
@@ -739,7 +860,7 @@
         int keep1InSlot = newType.parameterSlotDepth(dupArgPos);
         int slotCount   = keep1InSlot - dupSlot;
         assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount());
-        long conv = makeConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
+        long conv = makeDupConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
         return new AdapterMethodHandle(target, newType, conv);
     }
 
@@ -900,7 +1021,7 @@
         for (int i = 0; i < spreadArgCount; i++) {
             Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
             Class<?> dst = targetType.parameterType(spreadArgPos + i);
-            if (src == null || !VerifyType.isNullConversion(src, dst))
+            if (src == null || !canConvertArgument(src, dst, 1))
                 return false;
         }
         return true;
@@ -910,24 +1031,100 @@
     /** Factory method:  Spread selected argument. */
     static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target,
                 Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
+        // FIXME: Get rid of newType; derive new arguments from structure of spreadArgType
         MethodType targetType = target.type();
         if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
             return null;
+        // dest is not significant; remove?
+        int dest = T_VOID;
+        for (int i = 0; i < spreadArgCount; i++) {
+            Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
+            if (arg == null)  arg = Object.class;
+            int dest2 = basicType(arg);
+            if      (dest == T_VOID)  dest = dest2;
+            else if (dest != dest2)   dest = T_VOID;
+            if (dest == T_VOID)  break;
+            targetType = targetType.changeParameterType(spreadArgPos + i, arg);
+        }
+        target = target.asType(targetType);
+        int arrayArgSize = 1;  // always a reference
         // in  arglist: [0: ...keep1 | spos: spreadArg | spos+1:      keep2... ]
         // out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ]
         int keep2OutPos  = spreadArgPos + spreadArgCount;
-        int spreadSlot   = targetType.parameterSlotDepth(keep2OutPos);
-        int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos);
-        int slotCount    = keep1OutSlot - spreadSlot;
-        assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+1));
+        int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos);   // leading edge of |spread...|
+        int spreadSlot   = targetType.parameterSlotDepth(keep2OutPos);    // trailing edge of |spread...|
+        assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+arrayArgSize));
+        int slotCount    = keep1OutSlot - spreadSlot;                     // slots in |spread...|
         assert(slotCount >= spreadArgCount);
-        long conv = makeConv(OP_SPREAD_ARGS, spreadArgPos, slotCount-1);
+        int stackMove = - arrayArgSize + slotCount;  // pop array, push N slots
+        long conv = makeSpreadConv(OP_SPREAD_ARGS, spreadArgPos, T_OBJECT, dest, stackMove);
         MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType);
         assert(res.type().parameterType(spreadArgPos) == spreadArgType);
         return res;
     }
 
-    // TO DO: makeCollectArguments, makeFlyby, makeRicochet
+    /** Can an adapter collect a series of arguments, replacing them by zero or one results? */
+    static boolean canCollectArguments(MethodType targetType,
+                MethodType collectorType, int collectArgPos, boolean retainOriginalArgs) {
+        if (!convOpSupported(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS))  return false;
+        int collectArgCount = collectorType.parameterCount();
+        Class<?> rtype = collectorType.returnType();
+        assert(rtype == void.class || targetType.parameterType(collectArgPos) == rtype)
+                // [(Object)Object[], (Object[])Object[], 0, 1]
+                : Arrays.asList(targetType, collectorType, collectArgPos, collectArgCount)
+                ;
+        return true;
+    }
+
+    /** Factory method:  Collect or filter selected argument(s). */
+    static MethodHandle makeCollectArguments(MethodHandle target,
+                MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
+        assert(canCollectArguments(target.type(), collector.type(), collectArgPos, retainOriginalArgs));
+        MethodType targetType = target.type();
+        MethodType collectorType = collector.type();
+        int collectArgCount = collectorType.parameterCount();
+        Class<?> collectValType = collectorType.returnType();
+        int collectValCount = (collectValType == void.class ? 0 : 1);
+        int collectValSlots = collectorType.returnSlotCount();
+        MethodType newType = targetType
+                .dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
+        if (!retainOriginalArgs) {
+            newType = newType
+                .insertParameterTypes(collectArgPos, collectorType.parameterList());
+        } else {
+            // parameter types at the fold point must be the same
+            assert(diffParamTypes(newType, collectArgPos, targetType, collectValCount, collectArgCount, false) == 0)
+                : Arrays.asList(target, collector, collectArgPos, retainOriginalArgs);
+        }
+        // in  arglist: [0: ...keep1 | cpos: collect...  | cpos+cacount: keep2... ]
+        // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
+        // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ]
+        int keep2InPos   = collectArgPos + collectArgCount;
+        int keep1InSlot  = newType.parameterSlotDepth(collectArgPos);  // leading edge of |collect...|
+        int collectSlot  = newType.parameterSlotDepth(keep2InPos);     // trailing edge of |collect...|
+        int slotCount    = keep1InSlot - collectSlot;                  // slots in |collect...|
+        assert(slotCount >= collectArgCount);
+        assert(collectSlot == targetType.parameterSlotDepth(
+                collectArgPos + collectValCount + (retainOriginalArgs ? collectArgCount : 0) ));
+        int dest = basicType(collectValType);
+        int src = T_VOID;
+        // src is not significant; remove?
+        for (int i = 0; i < collectArgCount; i++) {
+            int src2 = basicType(collectorType.parameterType(i));
+            if      (src == T_VOID)  src = src2;
+            else if (src != src2)    src = T_VOID;
+            if (src == T_VOID)  break;
+        }
+        int stackMove = collectValSlots;  // push 0..2 results
+        if (!retainOriginalArgs)  stackMove -= slotCount; // pop N arguments
+        int lastCollectArg = keep2InPos-1;
+        long conv = makeSpreadConv(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS,
+                                   lastCollectArg, src, dest, stackMove);
+        MethodHandle res = new AdapterMethodHandle(target, newType, conv, collector);
+        assert(res.type().parameterList().subList(collectArgPos, collectArgPos+collectArgCount)
+                .equals(collector.type().parameterList()));
+        return res;
+    }
 
     @Override
     public String toString() {
--- a/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java	Thu May 12 19:27:49 2011 -0700
@@ -61,6 +61,10 @@
         return ad;
     }
 
+    static {
+        assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this class is deprecated
+    }
+
     Adapter makeInstance(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
         Adapter ad = getAdapter(kind, pos);
         return ad.makeInstance(ad.prototypeEntryPoint(), filter, target);
--- a/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java	Thu May 12 19:27:49 2011 -0700
@@ -67,6 +67,10 @@
         this.target = target;
     }
 
+    static {
+        assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this class is deprecated
+    }
+
     public static MethodHandle make(MethodHandle filter, MethodHandle target) {
         if (filter == null)  return target;
         if (target == null)  return filter;
--- a/jdk/src/share/classes/java/lang/invoke/FromGeneric.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FromGeneric.java	Thu May 12 19:27:49 2011 -0700
@@ -98,6 +98,10 @@
         this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
     }
 
+    static {
+        assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this class is deprecated
+    }
+
     /**
      * The typed target will be called according to targetType.
      * The adapter code will in fact see the raw result from internalType,
@@ -112,10 +116,10 @@
             assert(iret == Object.class);
             return ValueConversions.identity();
         } else if (wrap.primitiveType() == iret) {
-            return ValueConversions.box(wrap, false);
+            return ValueConversions.box(wrap);
         } else {
             assert(tret == double.class ? iret == long.class : iret == int.class);
-            return ValueConversions.boxRaw(wrap, false);
+            return ValueConversions.boxRaw(wrap);
         }
     }
 
@@ -135,7 +139,7 @@
         MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
         MethodHandle fixArgs = MethodHandleImpl.convertArguments(
                                  invoker, Invokers.invokerType(fixArgsType),
-                                 invoker.type(), null);
+                                 invoker.type(), 0);
         if (fixArgs == null)
             throw new InternalError("bad fixArgs");
         // reinterpret the calling sequence as raw:
@@ -230,7 +234,7 @@
     }
 
     static Adapter buildAdapterFromBytecodes(MethodType internalType) {
-        throw new UnsupportedOperationException("NYI");
+        throw new UnsupportedOperationException("NYI "+internalType);
     }
 
     /**
--- a/jdk/src/share/classes/java/lang/invoke/Invokers.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/Invokers.java	Thu May 12 19:27:49 2011 -0700
@@ -82,7 +82,7 @@
         MethodHandle invoker = generalInvoker;
         if (invoker != null)  return invoker;
         MethodType generalType = targetType.generic();
-        invoker = MethodHandles.convertArguments(invoker1, invokerType(generalType));
+        invoker = invoker1.asType(invokerType(generalType));
         generalInvoker = invoker;
         return invoker;
     }
@@ -95,7 +95,7 @@
         if (erasedType == targetType.generic())
             invoker = generalInvoker();
         else
-            invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType));
+            invoker = invoker1.asType(invokerType(erasedType));
         erasedInvoker = invoker;
         return invoker;
     }
--- a/jdk/src/share/classes/java/lang/invoke/MemberName.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MemberName.java	Thu May 12 19:27:49 2011 -0700
@@ -525,7 +525,7 @@
     /** A factory type for resolving member names with the help of the VM.
      *  TBD: Define access-safe public constructors for this factory.
      */
-    public static class Factory {
+    /*non-public*/ static class Factory {
         private Factory() { } // singleton pattern
         static Factory INSTANCE = new Factory();
 
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java	Thu May 12 19:27:49 2011 -0700
@@ -26,6 +26,7 @@
 package java.lang.invoke;
 
 
+import sun.invoke.util.ValueConversions;
 import static java.lang.invoke.MethodHandleStatics.*;
 
 /**
@@ -706,6 +707,9 @@
      * @see MethodHandles#convertArguments
      */
     public MethodHandle asType(MethodType newType) {
+        if (!type.isConvertibleTo(newType)) {
+            throw new WrongMethodTypeException("cannot convert "+type+" to "+newType);
+        }
         return MethodHandles.convertArguments(this, newType);
     }
 
@@ -748,13 +752,9 @@
     public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
         Class<?> arrayElement = arrayType.getComponentType();
         if (arrayElement == null)  throw newIllegalArgumentException("not an array type");
-        MethodType oldType = type();
-        int nargs = oldType.parameterCount();
+        int nargs = type().parameterCount();
         if (nargs < arrayLength)  throw newIllegalArgumentException("bad spread array length");
-        int keepPosArgs = nargs - arrayLength;
-        MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs);
-        newType = newType.insertParameterTypes(keepPosArgs, arrayType);
-        return MethodHandles.spreadArguments(this, newType);
+        return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
     }
 
     /**
@@ -797,15 +797,18 @@
      * @see #asVarargsCollector
      */
     public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
+        asCollectorChecks(arrayType, arrayLength);
+        MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
+        return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
+    }
+
+    private  void asCollectorChecks(Class<?> arrayType, int arrayLength) {
         Class<?> arrayElement = arrayType.getComponentType();
-        if (arrayElement == null)  throw newIllegalArgumentException("not an array type");
-        MethodType oldType = type();
-        int nargs = oldType.parameterCount();
-        if (nargs == 0)  throw newIllegalArgumentException("no trailing argument");
-        MethodType newType = oldType.dropParameterTypes(nargs-1, nargs);
-        newType = newType.insertParameterTypes(nargs-1,
-                    java.util.Collections.<Class<?>>nCopies(arrayLength, arrayElement));
-        return MethodHandles.collectArguments(this, newType);
+        if (arrayElement == null)
+            throw newIllegalArgumentException("not an array type", arrayType);
+        int nargs = type().parameterCount();
+        if (nargs == 0 || !type().parameterType(nargs-1).isAssignableFrom(arrayType))
+            throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
     }
 
     /**
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu May 12 19:27:49 2011 -0700
@@ -121,11 +121,11 @@
             if (nargs < INVOKES.length) {
                 MethodHandle invoke = INVOKES[nargs];
                 MethodType conType = CON_TYPES[nargs];
-                MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, null);
+                MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, 0);
                 if (gcon == null)  return null;
                 MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon);
                 assert(galloc.type() == newType.generic());
-                return convertArguments(galloc, newType, galloc.type(), null);
+                return convertArguments(galloc, newType, galloc.type(), 0);
             } else {
                 MethodHandle invoke = VARARGS_INVOKE;
                 MethodType conType = CON_TYPES[nargs];
@@ -256,8 +256,8 @@
                 FieldAccessor.ahandle(arrayClass, true)
             };
             if (mhs[0].type().parameterType(0) == Class.class) {
-                mhs[0] = MethodHandles.insertArguments(mhs[0], 0, elemClass);
-                mhs[1] = MethodHandles.insertArguments(mhs[1], 0, elemClass);
+                mhs[0] = mhs[0].bindTo(elemClass);
+                mhs[1] = mhs[1].bindTo(elemClass);
             }
             synchronized (FieldAccessor.ARRAY_CACHE) {}  // memory barrier
             FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
@@ -372,7 +372,7 @@
             if (evclass != vclass || (!isStatic && ecclass != cclass)) {
                 MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
                 strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
-                mh = MethodHandles.convertArguments(mh, strongType);
+                mh = convertArguments(mh, strongType, 0);
             }
             return mh;
         }
@@ -439,8 +439,8 @@
             }
             if (caclass != null) {
                 MethodType strongType = FieldAccessor.atype(caclass, isSetter);
-                mh = MethodHandles.insertArguments(mh, 0, caclass);
-                mh = MethodHandles.convertArguments(mh, strongType);
+                mh = mh.bindTo(caclass);
+                mh = convertArguments(mh, strongType, 0);
             }
             return mh;
         }
@@ -465,7 +465,7 @@
                     dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) {
                     MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0);
                     MethodType newType = target.type().dropParameterTypes(0, 1);
-                    return convertArguments(bmh, newType, bmh.type(), null);
+                    return convertArguments(bmh, newType, bmh.type(), 0);
                 }
             }
         }
@@ -486,301 +486,378 @@
         return new BoundMethodHandle(target, receiver, argnum);
     }
 
-    static MethodHandle convertArguments(MethodHandle target,
+    static MethodHandle permuteArguments(MethodHandle target,
                                                 MethodType newType,
                                                 MethodType oldType,
                                                 int[] permutationOrNull) {
         assert(oldType.parameterCount() == target.type().parameterCount());
-        if (permutationOrNull != null) {
-            int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
-            if (permutationOrNull.length != outargs)
-                throw newIllegalArgumentException("wrong number of arguments in permutation");
-            // Make the individual outgoing argument types match up first.
-            Class<?>[] callTypeArgs = new Class<?>[outargs];
-            for (int i = 0; i < outargs; i++)
-                callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
-            MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
-            target = convertArguments(target, callType, oldType, null);
-            assert(target != null);
-            oldType = target.type();
-            List<Integer> goal = new ArrayList<Integer>();  // i*TOKEN
-            List<Integer> state = new ArrayList<Integer>(); // i*TOKEN
-            List<Integer> drops = new ArrayList<Integer>(); // not tokens
-            List<Integer> dups = new ArrayList<Integer>();  // not tokens
-            final int TOKEN = 10; // to mark items which are symbolic only
-            // state represents the argument values coming into target
-            for (int i = 0; i < outargs; i++) {
-                state.add(permutationOrNull[i] * TOKEN);
+        int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
+        if (permutationOrNull.length != outargs)
+            throw newIllegalArgumentException("wrong number of arguments in permutation");
+        // Make the individual outgoing argument types match up first.
+        Class<?>[] callTypeArgs = new Class<?>[outargs];
+        for (int i = 0; i < outargs; i++)
+            callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
+        MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
+        target = convertArguments(target, callType, oldType, 0);
+        assert(target != null);
+        oldType = target.type();
+        List<Integer> goal = new ArrayList<Integer>();  // i*TOKEN
+        List<Integer> state = new ArrayList<Integer>(); // i*TOKEN
+        List<Integer> drops = new ArrayList<Integer>(); // not tokens
+        List<Integer> dups = new ArrayList<Integer>();  // not tokens
+        final int TOKEN = 10; // to mark items which are symbolic only
+        // state represents the argument values coming into target
+        for (int i = 0; i < outargs; i++) {
+            state.add(permutationOrNull[i] * TOKEN);
+        }
+        // goal represents the desired state
+        for (int i = 0; i < inargs; i++) {
+            if (state.contains(i * TOKEN)) {
+                goal.add(i * TOKEN);
+            } else {
+                // adapter must initially drop all unused arguments
+                drops.add(i);
             }
-            // goal represents the desired state
-            for (int i = 0; i < inargs; i++) {
-                if (state.contains(i * TOKEN)) {
-                    goal.add(i * TOKEN);
-                } else {
-                    // adapter must initially drop all unused arguments
-                    drops.add(i);
+        }
+        // detect duplications
+        while (state.size() > goal.size()) {
+            for (int i2 = 0; i2 < state.size(); i2++) {
+                int arg1 = state.get(i2);
+                int i1 = state.indexOf(arg1);
+                if (i1 != i2) {
+                    // found duplicate occurrence at i2
+                    int arg2 = (inargs++) * TOKEN;
+                    state.set(i2, arg2);
+                    dups.add(goal.indexOf(arg1));
+                    goal.add(arg2);
                 }
             }
-            // detect duplications
-            while (state.size() > goal.size()) {
-                for (int i2 = 0; i2 < state.size(); i2++) {
-                    int arg1 = state.get(i2);
-                    int i1 = state.indexOf(arg1);
-                    if (i1 != i2) {
-                        // found duplicate occurrence at i2
-                        int arg2 = (inargs++) * TOKEN;
-                        state.set(i2, arg2);
-                        dups.add(goal.indexOf(arg1));
-                        goal.add(arg2);
+        }
+        assert(state.size() == goal.size());
+        int size = goal.size();
+        while (!state.equals(goal)) {
+            // Look for a maximal sequence of adjacent misplaced arguments,
+            // and try to rotate them into place.
+            int bestRotArg = -10 * TOKEN, bestRotLen = 0;
+            int thisRotArg = -10 * TOKEN, thisRotLen = 0;
+            for (int i = 0; i < size; i++) {
+                int arg = state.get(i);
+                // Does this argument match the current run?
+                if (arg == thisRotArg + TOKEN) {
+                    thisRotArg = arg;
+                    thisRotLen += 1;
+                    if (bestRotLen < thisRotLen) {
+                        bestRotLen = thisRotLen;
+                        bestRotArg = thisRotArg;
+                    }
+                } else {
+                    // The old sequence (if any) stops here.
+                    thisRotLen = 0;
+                    thisRotArg = -10 * TOKEN;
+                    // But maybe a new one starts here also.
+                    int wantArg = goal.get(i);
+                    final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION;
+                    if (arg != wantArg &&
+                        arg >= wantArg - TOKEN * MAX_ARG_ROTATION &&
+                        arg <= wantArg + TOKEN * MAX_ARG_ROTATION) {
+                        thisRotArg = arg;
+                        thisRotLen = 1;
                     }
                 }
             }
-            assert(state.size() == goal.size());
-            int size = goal.size();
-            while (!state.equals(goal)) {
-                // Look for a maximal sequence of adjacent misplaced arguments,
-                // and try to rotate them into place.
-                int bestRotArg = -10 * TOKEN, bestRotLen = 0;
-                int thisRotArg = -10 * TOKEN, thisRotLen = 0;
-                for (int i = 0; i < size; i++) {
-                    int arg = state.get(i);
-                    // Does this argument match the current run?
-                    if (arg == thisRotArg + TOKEN) {
-                        thisRotArg = arg;
-                        thisRotLen += 1;
-                        if (bestRotLen < thisRotLen) {
-                            bestRotLen = thisRotLen;
-                            bestRotArg = thisRotArg;
-                        }
-                    } else {
-                        // The old sequence (if any) stops here.
-                        thisRotLen = 0;
-                        thisRotArg = -10 * TOKEN;
-                        // But maybe a new one starts here also.
-                        int wantArg = goal.get(i);
-                        final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION;
-                        if (arg != wantArg &&
-                            arg >= wantArg - TOKEN * MAX_ARG_ROTATION &&
-                            arg <= wantArg + TOKEN * MAX_ARG_ROTATION) {
-                            thisRotArg = arg;
-                            thisRotLen = 1;
-                        }
+            if (bestRotLen >= 2) {
+                // Do a rotation if it can improve argument positioning
+                // by at least 2 arguments.  This is not always optimal,
+                // but it seems to catch common cases.
+                int dstEnd = state.indexOf(bestRotArg);
+                int srcEnd = goal.indexOf(bestRotArg);
+                int rotBy = dstEnd - srcEnd;
+                int dstBeg = dstEnd - (bestRotLen - 1);
+                int srcBeg = srcEnd - (bestRotLen - 1);
+                assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs
+                // Make a span which covers both source and destination.
+                int rotBeg = Math.min(dstBeg, srcBeg);
+                int rotEnd = Math.max(dstEnd, srcEnd);
+                int score = 0;
+                for (int i = rotBeg; i <= rotEnd; i++) {
+                    if ((int)state.get(i) != (int)goal.get(i))
+                        score += 1;
+                }
+                List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1);
+                Collections.rotate(rotSpan, -rotBy);  // reverse direction
+                for (int i = rotBeg; i <= rotEnd; i++) {
+                    if ((int)state.get(i) != (int)goal.get(i))
+                        score -= 1;
+                }
+                if (score >= 2) {
+                    // Improved at least two argument positions.  Do it.
+                    List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
+                    Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy);
+                    MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes);
+                    MethodHandle nextTarget
+                            = AdapterMethodHandle.makeRotateArguments(rotType, target,
+                                    rotBeg, rotSpan.size(), rotBy);
+                    if (nextTarget != null) {
+                        //System.out.println("Rot: "+rotSpan+" by "+rotBy);
+                        target = nextTarget;
+                        oldType = rotType;
+                        continue;
                     }
                 }
-                if (bestRotLen >= 2) {
-                    // Do a rotation if it can improve argument positioning
-                    // by at least 2 arguments.  This is not always optimal,
-                    // but it seems to catch common cases.
-                    int dstEnd = state.indexOf(bestRotArg);
-                    int srcEnd = goal.indexOf(bestRotArg);
-                    int rotBy = dstEnd - srcEnd;
-                    int dstBeg = dstEnd - (bestRotLen - 1);
-                    int srcBeg = srcEnd - (bestRotLen - 1);
-                    assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs
-                    // Make a span which covers both source and destination.
-                    int rotBeg = Math.min(dstBeg, srcBeg);
-                    int rotEnd = Math.max(dstEnd, srcEnd);
-                    int score = 0;
-                    for (int i = rotBeg; i <= rotEnd; i++) {
-                        if ((int)state.get(i) != (int)goal.get(i))
-                            score += 1;
-                    }
-                    List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1);
-                    Collections.rotate(rotSpan, -rotBy);  // reverse direction
-                    for (int i = rotBeg; i <= rotEnd; i++) {
-                        if ((int)state.get(i) != (int)goal.get(i))
-                            score -= 1;
-                    }
-                    if (score >= 2) {
-                        // Improved at least two argument positions.  Do it.
-                        List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
-                        Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy);
-                        MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes);
-                        MethodHandle nextTarget
-                                = AdapterMethodHandle.makeRotateArguments(rotType, target,
-                                        rotBeg, rotSpan.size(), rotBy);
-                        if (nextTarget != null) {
-                            //System.out.println("Rot: "+rotSpan+" by "+rotBy);
-                            target = nextTarget;
-                            oldType = rotType;
-                            continue;
-                        }
-                    }
-                    // Else de-rotate, and drop through to the swap-fest.
-                    Collections.rotate(rotSpan, rotBy);
-                }
+                // Else de-rotate, and drop through to the swap-fest.
+                Collections.rotate(rotSpan, rotBy);
+            }
 
-                // Now swap like the wind!
-                List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
-                for (int i = 0; i < size; i++) {
-                    // What argument do I want here?
-                    int arg = goal.get(i);
-                    if (arg != state.get(i)) {
-                        // Where is it now?
-                        int j = state.indexOf(arg);
-                        Collections.swap(ptypes, i, j);
-                        MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes);
-                        target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j);
-                        if (target == null)  throw newIllegalArgumentException("cannot swap");
-                        assert(target.type() == swapType);
-                        oldType = swapType;
-                        Collections.swap(state, i, j);
-                    }
+            // Now swap like the wind!
+            List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
+            for (int i = 0; i < size; i++) {
+                // What argument do I want here?
+                int arg = goal.get(i);
+                if (arg != state.get(i)) {
+                    // Where is it now?
+                    int j = state.indexOf(arg);
+                    Collections.swap(ptypes, i, j);
+                    MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes);
+                    target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j);
+                    if (target == null)  throw newIllegalArgumentException("cannot swap");
+                    assert(target.type() == swapType);
+                    oldType = swapType;
+                    Collections.swap(state, i, j);
                 }
-                // One pass of swapping must finish the job.
-                assert(state.equals(goal));
+            }
+            // One pass of swapping must finish the job.
+            assert(state.equals(goal));
+        }
+        while (!dups.isEmpty()) {
+            // Grab a contiguous trailing sequence of dups.
+            int grab = dups.size() - 1;
+            int dupArgPos = dups.get(grab), dupArgCount = 1;
+            while (grab - 1 >= 0) {
+                int dup0 = dups.get(grab - 1);
+                if (dup0 != dupArgPos - 1)  break;
+                dupArgPos -= 1;
+                dupArgCount += 1;
+                grab -= 1;
+            }
+            //if (dupArgCount > 1)  System.out.println("Dup: "+dups.subList(grab, dups.size()));
+            dups.subList(grab, dups.size()).clear();
+            // In the new target type drop that many args from the tail:
+            List<Class<?>> ptypes = oldType.parameterList();
+            ptypes = ptypes.subList(0, ptypes.size() - dupArgCount);
+            MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes);
+            target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount);
+            if (target == null)
+                throw newIllegalArgumentException("cannot dup");
+            oldType = target.type();
+        }
+        while (!drops.isEmpty()) {
+            // Grab a contiguous initial sequence of drops.
+            int dropArgPos = drops.get(0), dropArgCount = 1;
+            while (dropArgCount < drops.size()) {
+                int drop1 = drops.get(dropArgCount);
+                if (drop1 != dropArgPos + dropArgCount)  break;
+                dropArgCount += 1;
             }
-            while (!dups.isEmpty()) {
-                // Grab a contiguous trailing sequence of dups.
-                int grab = dups.size() - 1;
-                int dupArgPos = dups.get(grab), dupArgCount = 1;
-                while (grab - 1 >= 0) {
-                    int dup0 = dups.get(grab - 1);
-                    if (dup0 != dupArgPos - 1)  break;
-                    dupArgPos -= 1;
-                    dupArgCount += 1;
-                    grab -= 1;
-                }
-                //if (dupArgCount > 1)  System.out.println("Dup: "+dups.subList(grab, dups.size()));
-                dups.subList(grab, dups.size()).clear();
-                // In the new target type drop that many args from the tail:
-                List<Class<?>> ptypes = oldType.parameterList();
-                ptypes = ptypes.subList(0, ptypes.size() - dupArgCount);
-                MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes);
-                target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount);
-                if (target == null)
-                    throw newIllegalArgumentException("cannot dup");
-                oldType = target.type();
+            //if (dropArgCount > 1)  System.out.println("Drop: "+drops.subList(0, dropArgCount));
+            drops.subList(0, dropArgCount).clear();
+            List<Class<?>> dropTypes = newType.parameterList()
+                    .subList(dropArgPos, dropArgPos + dropArgCount);
+            MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes);
+            target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount);
+            if (target == null)  throw newIllegalArgumentException("cannot drop");
+            oldType = target.type();
+        }
+        return convertArguments(target, newType, oldType, 0);
+    }
+
+    /*non-public*/ static
+    MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) {
+        MethodType oldType = target.type();
+        if (oldType.equals(newType))
+            return target;
+        assert(level > 1 || oldType.isConvertibleTo(newType));
+        MethodHandle retFilter = null;
+        Class<?> oldRT = oldType.returnType();
+        Class<?> newRT = newType.returnType();
+        if (!VerifyType.isNullConversion(oldRT, newRT)) {
+            if (oldRT == void.class) {
+                Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT;
+                retFilter = ValueConversions.zeroConstantFunction(wrap);
+            } else {
+                retFilter = MethodHandles.identity(newRT);
+                retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level);
             }
-            while (!drops.isEmpty()) {
-                // Grab a contiguous initial sequence of drops.
-                int dropArgPos = drops.get(0), dropArgCount = 1;
-                while (dropArgCount < drops.size()) {
-                    int drop1 = drops.get(dropArgCount);
-                    if (drop1 != dropArgPos + dropArgCount)  break;
-                    dropArgCount += 1;
-                }
-                //if (dropArgCount > 1)  System.out.println("Drop: "+drops.subList(0, dropArgCount));
-                drops.subList(0, dropArgCount).clear();
-                List<Class<?>> dropTypes = newType.parameterList()
-                        .subList(dropArgPos, dropArgPos + dropArgCount);
-                MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes);
-                target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount);
-                if (target == null)  throw newIllegalArgumentException("cannot drop");
-                oldType = target.type();
-            }
+            newType = newType.changeReturnType(oldRT);
+        }
+        MethodHandle res = null;
+        Exception ex = null;
+        try {
+            res = convertArguments(target, newType, oldType, level);
+        } catch (IllegalArgumentException ex1) {
+            ex = ex1;
         }
+        if (res == null) {
+            WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target);
+            wmt.initCause(ex);
+            throw wmt;
+        }
+        if (retFilter != null)
+            res = MethodHandles.filterReturnValue(res, retFilter);
+        return res;
+    }
+
+    static MethodHandle convertArguments(MethodHandle target,
+                                                MethodType newType,
+                                                MethodType oldType,
+                                                int level) {
+        assert(oldType.parameterCount() == target.type().parameterCount());
         if (newType == oldType)
             return target;
         if (oldType.parameterCount() != newType.parameterCount())
-            throw newIllegalArgumentException("mismatched parameter count");
-        MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target);
+            throw newIllegalArgumentException("mismatched parameter count", oldType, newType);
+        MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target, level);
         if (res != null)
             return res;
+        // We can come here in the case of target(int)void => (Object)void,
+        // because the unboxing logic for Object => int is complex.
         int argc = oldType.parameterCount();
+        assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this code is deprecated
         // The JVM can't do it directly, so fill in the gap with a Java adapter.
         // TO DO: figure out what to put here from case-by-case experience
         // Use a heavier method:  Convert all the arguments to Object,
         // then back to the desired types.  We might have to use Java-based
         // method handles to do this.
         MethodType objType = MethodType.genericMethodType(argc);
-        MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target);
+        MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target, level);
         if (objTarget == null)
             objTarget = FromGeneric.make(target);
-        res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget);
+        res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget, level);
         if (res != null)
             return res;
         return ToGeneric.make(newType, objTarget);
     }
 
+    static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) {
+        MethodType oldType = target.type();
+        int nargs = oldType.parameterCount();
+        int keepPosArgs = nargs - arrayLength;
+        MethodType newType = oldType
+                .dropParameterTypes(keepPosArgs, nargs)
+                .insertParameterTypes(keepPosArgs, arrayType);
+        return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength);
+    }
+    static MethodHandle spreadArguments(MethodHandle target, MethodType newType, int spreadArgPos) {
+        int arrayLength = target.type().parameterCount() - spreadArgPos;
+        return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength);
+    }
     static MethodHandle spreadArguments(MethodHandle target,
                                                MethodType newType,
-                                               int spreadArg) {
+                                               int spreadArgPos,
+                                               Class<?> arrayType,
+                                               int arrayLength) {
         // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
         MethodType oldType = target.type();
         // spread the last argument of newType to oldType
-        int spreadCount = oldType.parameterCount() - spreadArg;
-        Class<Object[]> spreadArgType = Object[].class;
-        MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, spreadArgType, spreadArg, spreadCount);
-        if (res != null)
-            return res;
-        // try an intermediate adapter
-        Class<?> spreadType = null;
-        if (spreadArg < 0 || spreadArg >= newType.parameterCount()
-            || !VerifyType.isSpreadArgType(spreadType = newType.parameterType(spreadArg)))
-            throw newIllegalArgumentException("no restarg in "+newType);
-        Class<?>[] ptypes = oldType.parameterArray();
-        for (int i = 0; i < spreadCount; i++)
-            ptypes[spreadArg + i] = VerifyType.spreadArgElementType(spreadType, i);
-        MethodType midType = MethodType.methodType(newType.returnType(), ptypes);
-        // after spreading, some arguments may need further conversion
-        MethodHandle target2 = convertArguments(target, midType, oldType, null);
-        if (target2 == null)
-            throw new UnsupportedOperationException("NYI: convert "+midType+" =calls=> "+oldType);
-        res = AdapterMethodHandle.makeSpreadArguments(newType, target2, spreadArgType, spreadArg, spreadCount);
-        if (res != null)
-            return res;
-        res = SpreadGeneric.make(target2, spreadCount);
-        if (res != null)
-            res = convertArguments(res, newType, res.type(), null);
+        assert(arrayLength == oldType.parameterCount() - spreadArgPos);
+        assert(newType.parameterType(spreadArgPos) == arrayType);
+        MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength);
+        if (res == null)  throw new IllegalArgumentException("spread on "+target+" with "+arrayType.getSimpleName());
         return res;
     }
 
     static MethodHandle collectArguments(MethodHandle target,
+                                                int collectArg,
+                                                MethodHandle collector) {
+        MethodType type = target.type();
+        Class<?> collectType = collector.type().returnType();
+        if (collectType != type.parameterType(collectArg))
+            target = target.asType(type.changeParameterType(collectArg, collectType));
+        MethodType newType = type
+                .dropParameterTypes(collectArg, collectArg+1)
+                .insertParameterTypes(collectArg, collector.type().parameterArray());
+        return collectArguments(target, newType, collectArg, collector);
+    }
+    static MethodHandle collectArguments(MethodHandle target,
                                                 MethodType newType,
                                                 int collectArg,
                                                 MethodHandle collector) {
         MethodType oldType = target.type();     // (a...,c)=>r
-        if (collector == null) {
-            int numCollect = newType.parameterCount() - oldType.parameterCount() + 1;
-            collector = ValueConversions.varargsArray(numCollect);
-        }
         //         newType                      // (a..., b...)=>r
         MethodType colType = collector.type();  // (b...)=>c
         //         oldType                      // (a..., b...)=>r
         assert(newType.parameterCount() == collectArg + colType.parameterCount());
         assert(oldType.parameterCount() == collectArg + 1);
-        MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null);
-        MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, null);
-        if (gtarget == null || gcollector == null)  return null;
-        MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget);
-        MethodHandle result = convertArguments(gresult, newType, gresult.type(), null);
+        MethodHandle result = null;
+        if (AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false)) {
+            result = AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false);
+        }
+        if (result == null) {
+            assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this code is deprecated
+            MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, 0);
+            MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, 0);
+            if (gtarget == null || gcollector == null)  return null;
+            MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget);
+            result = convertArguments(gresult, newType, gresult.type(), 0);
+        }
         return result;
     }
 
     static MethodHandle filterArgument(MethodHandle target,
-                                              int pos,
-                                              MethodHandle filter) {
-        MethodType ttype = target.type(), gttype = ttype.generic();
+                                       int pos,
+                                       MethodHandle filter) {
+        MethodType ttype = target.type();
+        MethodType ftype = filter.type();
+        assert(ftype.parameterCount() == 1);
+        MethodType rtype = ttype.changeParameterType(pos, ftype.parameterType(0));
+        MethodType gttype = ttype.generic();
         if (ttype != gttype) {
-            target = convertArguments(target, gttype, ttype, null);
+            target = convertArguments(target, gttype, ttype, 0);
             ttype = gttype;
         }
-        MethodType ftype = filter.type(), gftype = ftype.generic();
-        if (ftype.parameterCount() != 1)
-            throw new InternalError();
+        MethodType gftype = ftype.generic();
         if (ftype != gftype) {
-            filter = convertArguments(filter, gftype, ftype, null);
+            filter = convertArguments(filter, gftype, ftype, 0);
             ftype = gftype;
         }
-        if (ftype == ttype) {
+        MethodHandle result = null;
+        if (AdapterMethodHandle.canCollectArguments(ttype, ftype, pos, false)) {
+            result = AdapterMethodHandle.makeCollectArguments(target, filter, pos, false);
+        }
+        if (result == null) {
+            assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this code is deprecated
+            if (ftype == ttype) {
             // simple unary case
-            return FilterOneArgument.make(filter, target);
+                result = FilterOneArgument.make(filter, target);
+            } else {
+                result = FilterGeneric.makeArgumentFilter(pos, filter, target);
+            }
         }
-        return FilterGeneric.makeArgumentFilter(pos, filter, target);
+        if (result.type() != rtype)
+            result = result.asType(rtype);
+        return result;
     }
 
     static MethodHandle foldArguments(MethodHandle target,
-                                             MethodType newType,
-                                             MethodHandle combiner) {
+                                      MethodType newType,
+                                      int foldPos,
+                                      MethodHandle combiner) {
         MethodType oldType = target.type();
         MethodType ctype = combiner.type();
-        MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null);
-        MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, null);
+        if (AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true)) {
+            MethodHandle res = AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true);
+            if (res != null)  return res;
+        }
+        assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this code is deprecated
+        if (foldPos != 0)  return null;
+        MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, 0);
+        MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, 0);
+        if (ctype.returnType() == void.class) {
+            gtarget = dropArguments(gtarget, oldType.generic().insertParameterTypes(foldPos, Object.class), foldPos);
+        }
         if (gtarget == null || gcombiner == null)  return null;
         MethodHandle gresult = FilterGeneric.makeArgumentFolder(gcombiner, gtarget);
-        MethodHandle result = convertArguments(gresult, newType, gresult.type(), null);
-        return result;
+        return convertArguments(gresult, newType, gresult.type(), 0);
     }
 
     static
@@ -802,6 +879,7 @@
             this.target = target;
             this.fallback = fallback;
         }
+        // FIXME: Build the control flow out of foldArguments.
         static MethodHandle make(MethodHandle test, MethodHandle target, MethodHandle fallback) {
             MethodType type = target.type();
             int nargs = type.parameterCount();
@@ -809,12 +887,12 @@
                 MethodHandle invoke = INVOKES[nargs];
                 MethodType gtype = type.generic();
                 assert(invoke.type().dropParameterTypes(0,1) == gtype);
-                MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), null);
-                MethodHandle gtarget = convertArguments(target, gtype, type, null);
-                MethodHandle gfallback = convertArguments(fallback, gtype, type, null);
+                MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), 0);
+                MethodHandle gtarget = convertArguments(target, gtype, type, 0);
+                MethodHandle gfallback = convertArguments(fallback, gtype, type, 0);
                 if (gtest == null || gtarget == null || gfallback == null)  return null;
                 MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback);
-                return convertArguments(gguard, type, gtype, null);
+                return convertArguments(gguard, type, gtype, 0);
             } else {
                 MethodHandle invoke = VARARGS_INVOKE;
                 MethodType gtype = MethodType.genericMethodType(1);
@@ -925,8 +1003,9 @@
         GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
             this(INVOKES[target.type().parameterCount()], target, exType, catcher);
         }
-       GuardWithCatch(MethodHandle invoker,
-                      MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
+        // FIXME: Build the control flow out of foldArguments.
+        GuardWithCatch(MethodHandle invoker,
+                       MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
             super(invoker);
             this.target = target;
             this.exType = exType;
@@ -1057,11 +1136,11 @@
         if (nargs < GuardWithCatch.INVOKES.length) {
             MethodType gtype = type.generic();
             MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
-            MethodHandle gtarget = convertArguments(target, gtype, type, null);
-            MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, null);
+            MethodHandle gtarget = convertArguments(target, gtype, type, 0);
+            MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 0);
             MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher);
             if (gtarget == null || gcatcher == null || gguard == null)  return null;
-            return convertArguments(gguard, type, gtype, null);
+            return convertArguments(gguard, type, gtype, 0);
         } else {
             MethodType gtype = MethodType.genericMethodType(0, true);
             MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java	Thu May 12 19:27:49 2011 -0700
@@ -115,6 +115,8 @@
 
     /** Which conv-ops are implemented by the JVM? */
     static final int CONV_OP_IMPLEMENTED_MASK;
+    /** Derived mode flag.  Only false on some old JVM implementations. */
+    static final boolean HAVE_RICOCHET_FRAMES;
 
     private static native void registerNatives();
     static {
@@ -141,6 +143,7 @@
         if (CONV_OP_IMPLEMENTED_MASK_ == 0)
             CONV_OP_IMPLEMENTED_MASK_ = DEFAULT_CONV_OP_IMPLEMENTED_MASK;
         CONV_OP_IMPLEMENTED_MASK = CONV_OP_IMPLEMENTED_MASK_;
+        HAVE_RICOCHET_FRAMES = (CONV_OP_IMPLEMENTED_MASK & (1<<OP_COLLECT_ARGS)) != 0;
     }
 
     // All compile-time constants go here.
@@ -186,25 +189,26 @@
          */
         static final int
             OP_RETYPE_ONLY   = 0x0, // no argument changes; straight retype
-            OP_RETYPE_RAW    = 0x1, // no argument changes; straight retype
+            OP_RETYPE_RAW    = 0x1, // straight retype, trusted (void->int, Object->T)
             OP_CHECK_CAST    = 0x2, // ref-to-ref conversion; requires a Class argument
             OP_PRIM_TO_PRIM  = 0x3, // converts from one primitive to another
             OP_REF_TO_PRIM   = 0x4, // unboxes a wrapper to produce a primitive
-            OP_PRIM_TO_REF   = 0x5, // boxes a primitive into a wrapper (NYI)
+            OP_PRIM_TO_REF   = 0x5, // boxes a primitive into a wrapper
             OP_SWAP_ARGS     = 0x6, // swap arguments (vminfo is 2nd arg)
             OP_ROT_ARGS      = 0x7, // rotate arguments (vminfo is displaced arg)
             OP_DUP_ARGS      = 0x8, // duplicates one or more arguments (at TOS)
             OP_DROP_ARGS     = 0x9, // remove one or more argument slots
-            OP_COLLECT_ARGS  = 0xA, // combine one or more arguments into a varargs (NYI)
+            OP_COLLECT_ARGS  = 0xA, // combine arguments using an auxiliary function
             OP_SPREAD_ARGS   = 0xB, // expand in place a varargs array (of known size)
-            OP_FLYBY         = 0xC, // operate first on reified argument list (NYI)
-            OP_RICOCHET      = 0xD, // run an adapter chain on the return value (NYI)
+            OP_FOLD_ARGS     = 0xC, // combine but do not remove arguments; prepend result
+            //OP_UNUSED_13   = 0xD, // unused code, perhaps for reified argument lists
             CONV_OP_LIMIT    = 0xE; // limit of CONV_OP enumeration
         /** Shift and mask values for decoding the AMH.conversion field.
          *  These numbers are shared with the JVM for creating AMHs.
          */
         static final int
             CONV_OP_MASK     = 0xF00, // this nybble contains the conversion op field
+            CONV_TYPE_MASK   = 0x0F,  // fits T_ADDRESS and below
             CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
             CONV_VMINFO_SHIFT     =  0, // position of bits in CONV_VMINFO_MASK
             CONV_OP_SHIFT         =  8, // position of bits in CONV_OP_MASK
@@ -244,8 +248,9 @@
             T_LONG     = 11,
             T_OBJECT   = 12,
             //T_ARRAY    = 13
-            T_VOID     = 14;
+            T_VOID     = 14,
             //T_ADDRESS  = 15
+            T_ILLEGAL  = 99;
 
         /**
          * Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
@@ -273,16 +278,29 @@
             try {
                 Field con = Constants.class.getDeclaredField(name);
                 int jval = con.getInt(null);
-                if (jval != vmval)
-                    throw new InternalError(name+": JVM has "+vmval+" while Java has "+jval);
+                if (jval == vmval)  continue;
+                String err = (name+": JVM has "+vmval+" while Java has "+jval);
+                if (name.equals("CONV_OP_LIMIT")) {
+                    System.err.println("warning: "+err);
+                    continue;
+                }
+                throw new InternalError(err);
             } catch (Exception ex) {
+                if (ex instanceof NoSuchFieldException) {
+                    String err = (name+": JVM has "+vmval+" which Java does not define");
+                    // ignore exotic ops the JVM cares about; we just wont issue them
+                    if (name.startsWith("OP_") || name.startsWith("GC_")) {
+                        System.err.println("warning: "+err);
+                        continue;
+                    }
+                }
                 throw new InternalError(name+": access failed, got "+ex);
             }
         }
         return true;
     }
     static {
-        verifyConstants();
+        assert(verifyConstants());
     }
 
     // Up-calls from the JVM.
@@ -323,15 +341,39 @@
      * The JVM wants to raise an exception.  Here's the path.
      */
     static void raiseException(int code, Object actual, Object required) {
-        String message;
+        String message = null;
+        switch (code) {
+        case 190: // arraylength
+            try {
+                String reqLength = "";
+                if (required instanceof AdapterMethodHandle) {
+                    int conv = ((AdapterMethodHandle)required).getConversion();
+                    int spChange = AdapterMethodHandle.extractStackMove(conv);
+                    reqLength = " of length "+(spChange+1);
+                }
+                int actualLength = actual == null ? 0 : java.lang.reflect.Array.getLength(actual);
+                message = "required array"+reqLength+", but encountered wrong length "+actualLength;
+                break;
+            } catch (IllegalArgumentException ex) {
+            }
+            required = Object[].class;  // should have been an array
+            code = 192; // checkcast
+            break;
+        }
         // disregard the identity of the actual object, if it is not a class:
-        if (!(actual instanceof Class) && !(actual instanceof MethodType))
-            actual = actual.getClass();
-        if (actual != null)
-            message = "required "+required+" but encountered "+actual;
-        else
-            message = "required "+required;
+        if (message == null) {
+            if (!(actual instanceof Class) && !(actual instanceof MethodType))
+                actual = actual.getClass();
+           if (actual != null)
+               message = "required "+required+" but encountered "+actual;
+           else
+               message = "required "+required;
+        }
         switch (code) {
+        case 190: // arraylength
+            throw new ArrayIndexOutOfBoundsException(message);
+        case 50: //_aaload
+            throw new ClassCastException(message);
         case 192: // checkcast
             throw new ClassCastException(message);
         default:
@@ -365,4 +407,13 @@
             throw err;
         }
     }
+
+    /**
+     * This assertion marks code which was written before ricochet frames were implemented.
+     * Such code will go away when the ports catch up.
+     */
+    static boolean workaroundWithoutRicochetFrames() {
+        assert(!HAVE_RICOCHET_FRAMES) : "this code should not be executed if `-XX:+UseRicochetFrames is enabled";
+        return true;
+    }
 }
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java	Thu May 12 19:27:49 2011 -0700
@@ -63,8 +63,17 @@
     }
 
     static void checkSpreadArgument(Object av, int n) {
-        if (av == null ? n != 0 : ((Object[])av).length != n)
-            throw newIllegalArgumentException("Array is not of length "+n);
+        if (av == null) {
+            if (n == 0)  return;
+        } else if (av instanceof Object[]) {
+            int len = ((Object[])av).length;
+            if (len == n)  return;
+        } else {
+            int len = java.lang.reflect.Array.getLength(av);
+            if (len == n)  return;
+        }
+        // fall through to error:
+        throw newIllegalArgumentException("Array is not of length "+n);
     }
 
     // handy shared exception makers (they simplify the common case code)
@@ -80,6 +89,9 @@
     /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj) {
         return new IllegalArgumentException(message(message, obj));
     }
+    /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) {
+        return new IllegalArgumentException(message(message, obj, obj2));
+    }
     /*non-public*/ static Error uncaughtException(Exception ex) {
         Error err = new InternalError("uncaught exception");
         err.initCause(ex);
@@ -89,4 +101,8 @@
         if (obj != null)  message = message + ": " + obj;
         return message;
     }
+    private static String message(String message, Object obj, Object obj2) {
+        if (obj != null || obj2 != null)  message = message + ": " + obj + ", " + obj2;
+        return message;
+    }
 }
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java	Thu May 12 19:27:49 2011 -0700
@@ -1080,7 +1080,7 @@
             MethodType rawType = mh.type();
             if (rawType.parameterType(0) == caller)  return mh;
             MethodType narrowType = rawType.changeParameterType(0, caller);
-            MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, null);
+            MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, 0);
             return fixVarargs(narrowMH, mh);
         }
 
@@ -1377,18 +1377,7 @@
      */
     public static
     MethodHandle convertArguments(MethodHandle target, MethodType newType) {
-        MethodType oldType = target.type();
-        if (oldType.equals(newType))
-            return target;
-        MethodHandle res = null;
-        try {
-            res = MethodHandleImpl.convertArguments(target,
-                                                    newType, oldType, null);
-        } catch (IllegalArgumentException ex) {
-        }
-        if (res == null)
-            throw new WrongMethodTypeException("cannot convert to "+newType+": "+target);
-        return res;
+        return MethodHandleImpl.convertArguments(target, newType, 1);
     }
 
     /**
@@ -1431,7 +1420,7 @@
      */
     public static
     MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
-        return convertArguments(target, newType);  // FIXME!
+        return MethodHandleImpl.convertArguments(target, newType, 2);
     }
 
     /*
@@ -1526,23 +1515,32 @@
     MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
         MethodType oldType = target.type();
         checkReorder(reorder, newType, oldType);
-        return MethodHandleImpl.convertArguments(target,
+        return MethodHandleImpl.permuteArguments(target,
                                                  newType, oldType,
                                                  reorder);
     }
 
     private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
+        if (newType.returnType() != oldType.returnType())
+            throw newIllegalArgumentException("return types do not match",
+                    oldType, newType);
         if (reorder.length == oldType.parameterCount()) {
             int limit = newType.parameterCount();
             boolean bad = false;
-            for (int i : reorder) {
+            for (int j = 0; j < reorder.length; j++) {
+                int i = reorder[j];
                 if (i < 0 || i >= limit) {
                     bad = true; break;
                 }
+                Class<?> src = newType.parameterType(i);
+                Class<?> dst = oldType.parameterType(j);
+                if (src != dst)
+                    throw newIllegalArgumentException("parameter types do not match after reorder",
+                            oldType, newType);
             }
             if (!bad)  return;
         }
-        throw newIllegalArgumentException("bad reorder array");
+        throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder));
     }
 
     /**
@@ -1631,7 +1629,7 @@
             if (type == void.class)
                 throw newIllegalArgumentException("void type");
             Wrapper w = Wrapper.forPrimitiveType(type);
-            return identity(type).bindTo(w.convert(value, type));
+            return insertArguments(identity(type), 0, w.convert(value, type));
         } else {
             return identity(type).bindTo(type.cast(value));
         }
@@ -1866,7 +1864,8 @@
     MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
         MethodType targetType = target.type();
         MethodHandle adapter = target;
-        MethodType adapterType = targetType;
+        MethodType adapterType = null;
+        assert((adapterType = targetType) != null);
         int maxPos = targetType.parameterCount();
         if (pos + filters.length > maxPos)
             throw newIllegalArgumentException("too many filters");
@@ -1874,19 +1873,23 @@
         for (MethodHandle filter : filters) {
             curPos += 1;
             if (filter == null)  continue;  // ignore null elements of filters
-            MethodType filterType = filter.type();
-            if (filterType.parameterCount() != 1
-                || filterType.returnType() != targetType.parameterType(curPos))
-                throw newIllegalArgumentException("target and filter types do not match");
-            adapterType = adapterType.changeParameterType(curPos, filterType.parameterType(0));
-            adapter = MethodHandleImpl.filterArgument(adapter, curPos, filter);
+            adapter = filterArgument(adapter, curPos, filter);
+            assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null);
         }
-        MethodType midType = adapter.type();
-        if (midType != adapterType)
-            adapter = MethodHandleImpl.convertArguments(adapter, adapterType, midType, null);
+        assert(adapterType.equals(adapter.type()));
         return adapter;
     }
 
+    /*non-public*/ static
+    MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
+        MethodType targetType = target.type();
+        MethodType filterType = filter.type();
+        if (filterType.parameterCount() != 1
+            || filterType.returnType() != targetType.parameterType(pos))
+            throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
+        return MethodHandleImpl.filterArgument(target, pos, filter);
+    }
+
     /**
      * Adapts a target method handle {@code target} by post-processing
      * its return value with a unary filter function.
@@ -1922,14 +1925,26 @@
     MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
         MethodType targetType = target.type();
         MethodType filterType = filter.type();
-        if (filterType.parameterCount() != 1
-            || filterType.parameterType(0) != targetType.returnType())
-            throw newIllegalArgumentException("target and filter types do not match");
+        Class<?> rtype = targetType.returnType();
+        int filterValues = filterType.parameterCount();
+        if (filterValues == 0
+                ? (rtype != void.class)
+                : (rtype != filterType.parameterType(0)))
+            throw newIllegalArgumentException("target and filter types do not match", target, filter);
         // result = fold( lambda(retval, arg...) { filter(retval) },
         //                lambda(        arg...) { target(arg...) } )
+        MethodType newType = targetType.changeReturnType(filterType.returnType());
+        MethodHandle result = null;
+        if (AdapterMethodHandle.canCollectArguments(filterType, targetType, 0, false)) {
+            result = AdapterMethodHandle.makeCollectArguments(filter, target, 0, false);
+            if (result != null)  return result;
+        }
         // FIXME: Too many nodes here.
-        MethodHandle returner = dropArguments(filter, 1, targetType.parameterList());
-        return foldArguments(returner, target);
+        assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this class is deprecated
+        MethodHandle returner = dropArguments(filter, filterValues, targetType.parameterList());
+        result = foldArguments(returner, target);
+        assert(result.type().equals(newType));
+        return result;
     }
 
     /**
@@ -1981,16 +1996,23 @@
     MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
         MethodType targetType = target.type();
         MethodType combinerType = combiner.type();
+        int foldPos = 0;  // always at the head, at present
         int foldArgs = combinerType.parameterCount();
-        boolean ok = (targetType.parameterCount() >= 1 + foldArgs);
-        if (ok && !combinerType.parameterList().equals(targetType.parameterList().subList(1, foldArgs+1)))
+        int foldVals = combinerType.returnType() == void.class ? 0 : 1;
+        int afterInsertPos = foldPos + foldVals;
+        boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
+        if (ok && !(combinerType.parameterList()
+                    .equals(targetType.parameterList().subList(afterInsertPos,
+                                                               afterInsertPos + foldArgs))))
             ok = false;
-        if (ok && !combinerType.returnType().equals(targetType.parameterType(0)))
+        if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0)))
             ok = false;
         if (!ok)
             throw misMatchedTypes("target and combiner types", targetType, combinerType);
-        MethodType newType = targetType.dropParameterTypes(0, 1);
-        return MethodHandleImpl.foldArguments(target, newType, combiner);
+        MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos);
+        MethodHandle res = MethodHandleImpl.foldArguments(target, newType, foldPos, combiner);
+        if (res == null)  throw newIllegalArgumentException("cannot fold from "+newType+" to " +targetType);
+        return res;
     }
 
     /**
--- a/jdk/src/share/classes/java/lang/invoke/MethodType.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodType.java	Thu May 12 19:27:49 2011 -0700
@@ -25,6 +25,7 @@
 
 package java.lang.invoke;
 
+import sun.invoke.util.Wrapper;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -262,18 +263,18 @@
      * Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
      * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
      * All parameters and the return type will be {@code Object},
-     * except the final varargs parameter if any, which will be {@code Object[]}.
-     * @param objectArgCount number of parameters (excluding the varargs parameter if any)
-     * @param varargs whether there will be a varargs parameter, of type {@code Object[]}
-     * @return a totally generic method type, given only its count of parameters and varargs
-     * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
+     * except the final array parameter if any, which will be {@code Object[]}.
+     * @param objectArgCount number of parameters (excluding the final array parameter if any)
+     * @param finalArray whether there will be a trailing array parameter, of type {@code Object[]}
+     * @return a generally applicable method type, for all calls of the given fixed argument count and a collected array of further arguments
+     * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray})
      * @see #genericMethodType(int)
      */
     public static
-    MethodType genericMethodType(int objectArgCount, boolean varargs) {
+    MethodType genericMethodType(int objectArgCount, boolean finalArray) {
         MethodType mt;
         checkSlotCount(objectArgCount);
-        int ivarargs = (!varargs ? 0 : 1);
+        int ivarargs = (!finalArray ? 0 : 1);
         int ootIndex = objectArgCount*2 + ivarargs;
         if (ootIndex < objectOnlyTypes.length) {
             mt = objectOnlyTypes[ootIndex];
@@ -626,6 +627,30 @@
         return sb.toString();
     }
 
+
+    /*non-public*/
+    boolean isConvertibleTo(MethodType newType) {
+        if (!canConvert(returnType(), newType.returnType()))
+            return false;
+        int argc = parameterCount();
+        if (argc != newType.parameterCount())
+            return false;
+        for (int i = 0; i < argc; i++) {
+            if (!canConvert(newType.parameterType(i), parameterType(i)))
+                return false;
+        }
+        return true;
+    }
+    private static boolean canConvert(Class<?> src, Class<?> dst) {
+        if (src == dst || dst == void.class)  return true;
+        if (src.isPrimitive() && dst.isPrimitive()) {
+        if (!Wrapper.forPrimitiveType(dst)
+                .isConvertibleFrom(Wrapper.forPrimitiveType(src)))
+            return false;
+        }
+        return true;
+    }
+
     /// Queries which have to do with the bytecode architecture
 
     /** Reports the number of JVM stack slots required to invoke a method
--- a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java	Thu May 12 19:27:49 2011 -0700
@@ -46,6 +46,7 @@
     final long argCounts;               // packed slot & value counts
     final long primCounts;              // packed prim & double counts
     final int vmslots;                  // total number of parameter slots
+    private Object vmlayout;            // vm-specific information for calls
     final MethodType erasedType;        // the canonical erasure
 
     /*lazy*/ MethodType primsAsBoxes;   // replace prims by wrappers
--- a/jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java	Thu May 12 19:27:49 2011 -0700
@@ -66,6 +66,10 @@
         this.entryPoint = ep[0];
     }
 
+    static {
+        assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this class is deprecated
+    }
+
     /** From targetType remove the last spreadCount arguments, and instead
      *  append a simple Object argument.
      */
--- a/jdk/src/share/classes/java/lang/invoke/ToGeneric.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/ToGeneric.java	Thu May 12 19:27:49 2011 -0700
@@ -96,7 +96,7 @@
             ToGeneric va2 = ToGeneric.of(primsAtEnd);
             this.adapter = va2.adapter;
             if (true) throw new UnsupportedOperationException("NYI: primitive parameters must follow references; entryType = "+entryType);
-            this.entryPoint = MethodHandleImpl.convertArguments(
+            this.entryPoint = MethodHandleImpl.permuteArguments(
                     va2.entryPoint, primsAtEnd, entryType, primsAtEndOrder);
             // example: for entryType of (int,Object,Object), the reordered
             // type is (Object,Object,int) and the order is {1,2,0},
@@ -128,7 +128,7 @@
                         assert(eptWithInts.parameterType(i) == int.class);
                         MethodType nextType = midType.changeParameterType(i, int.class);
                         rawEntryPoint = MethodHandleImpl.convertArguments(
-                                rawEntryPoint, nextType, midType, null);
+                                rawEntryPoint, nextType, midType, 0);
                         midType = nextType;
                     }
                 }
@@ -152,6 +152,10 @@
         this.invoker = makeRawArgumentFilter(invoker0, rawEntryTypeInit, entryType);
     }
 
+    static {
+        assert(MethodHandleNatives.workaroundWithoutRicochetFrames());  // this class is deprecated
+    }
+
     /** A generic argument list will be created by a call of type 'raw'.
      *  The values need to be reboxed for to match 'cooked'.
      *  Do this on the fly.
@@ -171,7 +175,7 @@
                             invoker.type().generic(), invoker, 0, MethodHandle.class);
                 if (filteredInvoker == null)  throw new UnsupportedOperationException("NYI");
             }
-            MethodHandle reboxer = ValueConversions.rebox(dst, false);
+            MethodHandle reboxer = ValueConversions.rebox(dst);
             filteredInvoker = FilterGeneric.makeArgumentFilter(1+i, reboxer, filteredInvoker);
             if (filteredInvoker == null)  throw new InternalError();
         }
@@ -199,13 +203,13 @@
             assert(!rret.isPrimitive());
             if (rret == Object.class && !mustCast)
                 return null;
-            return ValueConversions.cast(tret, false);
+            return ValueConversions.cast(tret);
         } else if (tret == rret) {
-            return ValueConversions.unbox(tret, false);
+            return ValueConversions.unbox(tret);
         } else {
             assert(rret.isPrimitive());
             assert(tret == double.class ? rret == long.class : rret == int.class);
-            return ValueConversions.unboxRaw(tret, false);
+            return ValueConversions.unboxRaw(tret);
         }
     }
 
@@ -311,7 +315,7 @@
     }
 
     static Adapter buildAdapterFromBytecodes(MethodType entryPointType) {
-        throw new UnsupportedOperationException("NYI");
+        throw new UnsupportedOperationException("NYI: "+entryPointType);
     }
 
     /**
--- a/jdk/src/share/classes/sun/invoke/util/ValueConversions.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/sun/invoke/util/ValueConversions.java	Thu May 12 19:27:49 2011 -0700
@@ -31,10 +31,15 @@
 import java.lang.invoke.MethodType;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.EnumMap;
 import java.util.List;
 
 public class ValueConversions {
+    private static final Class<?> THIS_CLASS = ValueConversions.class;
+    // Do not adjust this except for special platforms:
+    private static final int MAX_ARITY = Integer.getInteger(THIS_CLASS.getName()+".MAX_ARITY", 255);
+
     private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
 
     private static EnumMap<Wrapper, MethodHandle>[] newWrapperCaches(int n) {
@@ -42,88 +47,101 @@
         EnumMap<Wrapper, MethodHandle>[] caches
                 = (EnumMap<Wrapper, MethodHandle>[]) new EnumMap[n];  // unchecked warning expected here
         for (int i = 0; i < n; i++)
-            caches[i] = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
+            caches[i] = new EnumMap<>(Wrapper.class);
         return caches;
     }
 
     /// Converting references to values.
 
-    static int unboxInteger(Object x) {
-        if (x == null)  return 0;  // never NPE
-        return ((Integer) x).intValue();
+    // There are several levels of this unboxing conversions:
+    //   no conversions:  exactly Integer.valueOf, etc.
+    //   implicit conversions sanctioned by JLS 5.1.2, etc.
+    //   explicit conversions as allowed by explicitCastArguments
+
+    static int unboxInteger(Object x, boolean cast) {
+        if (x instanceof Integer)
+            return ((Integer) x).intValue();
+        return primitiveConversion(Wrapper.INT, x, cast).intValue();
     }
 
-    static byte unboxByte(Object x) {
-        if (x == null)  return 0;  // never NPE
-        return ((Byte) x).byteValue();
+    static byte unboxByte(Object x, boolean cast) {
+        if (x instanceof Byte)
+            return ((Byte) x).byteValue();
+        return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();
     }
 
-    static short unboxShort(Object x) {
-        if (x == null)  return 0;  // never NPE
-        return ((Short) x).shortValue();
+    static short unboxShort(Object x, boolean cast) {
+        if (x instanceof Short)
+            return ((Short) x).shortValue();
+        return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();
     }
 
-    static boolean unboxBoolean(Object x) {
-        if (x == null)  return false;  // never NPE
-        return ((Boolean) x).booleanValue();
+    static boolean unboxBoolean(Object x, boolean cast) {
+        if (x instanceof Boolean)
+            return ((Boolean) x).booleanValue();
+        return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;
     }
 
-    static char unboxCharacter(Object x) {
-        if (x == null)  return 0;  // never NPE
-        return ((Character) x).charValue();
+    static char unboxCharacter(Object x, boolean cast) {
+        if (x instanceof Character)
+            return ((Character) x).charValue();
+        return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();
     }
 
-    static long unboxLong(Object x) {
-        if (x == null)  return 0;  // never NPE
-        return ((Long) x).longValue();
+    static long unboxLong(Object x, boolean cast) {
+        if (x instanceof Long)
+            return ((Long) x).longValue();
+        return primitiveConversion(Wrapper.LONG, x, cast).longValue();
     }
 
-    static float unboxFloat(Object x) {
-        if (x == null)  return 0;  // never NPE
-        return ((Float) x).floatValue();
+    static float unboxFloat(Object x, boolean cast) {
+        if (x instanceof Float)
+            return ((Float) x).floatValue();
+        return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();
     }
 
-    static double unboxDouble(Object x) {
-        if (x == null)  return 0;  // never NPE
-        return ((Double) x).doubleValue();
+    static double unboxDouble(Object x, boolean cast) {
+        if (x instanceof Double)
+            return ((Double) x).doubleValue();
+        return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
     }
 
     /// Converting references to "raw" values.
     /// A raw primitive value is always an int or long.
 
-    static int unboxByteRaw(Object x) {
-        return unboxByte(x);
+    static int unboxByteRaw(Object x, boolean cast) {
+        return unboxByte(x, cast);
     }
 
-    static int unboxShortRaw(Object x) {
-        return unboxShort(x);
+    static int unboxShortRaw(Object x, boolean cast) {
+        return unboxShort(x, cast);
     }
 
-    static int unboxBooleanRaw(Object x) {
-        return unboxBoolean(x) ? 1 : 0;
+    static int unboxBooleanRaw(Object x, boolean cast) {
+        return unboxBoolean(x, cast) ? 1 : 0;
     }
 
-    static int unboxCharacterRaw(Object x) {
-        return unboxCharacter(x);
+    static int unboxCharacterRaw(Object x, boolean cast) {
+        return unboxCharacter(x, cast);
     }
 
-    static int unboxFloatRaw(Object x) {
-        return Float.floatToIntBits(unboxFloat(x));
+    static int unboxFloatRaw(Object x, boolean cast) {
+        return Float.floatToIntBits(unboxFloat(x, cast));
     }
 
-    static long unboxDoubleRaw(Object x) {
-        return Double.doubleToRawLongBits(unboxDouble(x));
+    static long unboxDoubleRaw(Object x, boolean cast) {
+        return Double.doubleToRawLongBits(unboxDouble(x, cast));
     }
 
     private static MethodType unboxType(Wrapper wrap, boolean raw) {
-        return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), wrap.wrapperType());
+        return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), Object.class, boolean.class);
     }
 
     private static final EnumMap<Wrapper, MethodHandle>[]
             UNBOX_CONVERSIONS = newWrapperCaches(4);
 
-    private static MethodHandle unbox(Wrapper wrap, boolean exact, boolean raw) {
-        EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
+    private static MethodHandle unbox(Wrapper wrap, boolean raw, boolean cast) {
+        EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)+(raw?2:0)];
         MethodHandle mh = cache.get(wrap);
         if (mh != null) {
             return mh;
@@ -136,7 +154,7 @@
                 mh = raw ? ALWAYS_ZERO : IGNORE; break;
             case INT: case LONG:
                 // these guys don't need separate raw channels
-                if (raw)  mh = unbox(wrap, exact, false);
+                if (raw)  mh = unbox(wrap, false, cast);
                 break;
         }
         if (mh != null) {
@@ -146,37 +164,62 @@
         // look up the method
         String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
         MethodType type = unboxType(wrap, raw);
-        if (!exact) {
-            try {
-                // actually, type is wrong; the Java method takes Object
-                mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
-            } catch (ReflectiveOperationException ex) {
-                mh = null;
-            }
-        } else {
-            mh = unbox(wrap, !exact, raw).asType(type);
+        try {
+            mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+        } catch (ReflectiveOperationException ex) {
+            mh = null;
         }
         if (mh != null) {
+            mh = MethodHandles.insertArguments(mh, 1, cast);
             cache.put(wrap, mh);
             return mh;
         }
-        throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + (raw ? " (raw)" : ""));
+        throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
+                + (cast ? " (cast)" : "") + (raw ? " (raw)" : ""));
+    }
+
+    public static MethodHandle unboxCast(Wrapper type) {
+        return unbox(type, false, true);
     }
 
-    public static MethodHandle unbox(Wrapper type, boolean exact) {
-        return unbox(type, exact, false);
+    public static MethodHandle unboxRaw(Wrapper type) {
+        return unbox(type, true, false);
+    }
+
+    public static MethodHandle unbox(Class<?> type) {
+        return unbox(Wrapper.forPrimitiveType(type), false, false);
+    }
+
+    public static MethodHandle unboxCast(Class<?> type) {
+        return unbox(Wrapper.forPrimitiveType(type), false, true);
     }
 
-    public static MethodHandle unboxRaw(Wrapper type, boolean exact) {
-        return unbox(type, exact, true);
+    public static MethodHandle unboxRaw(Class<?> type) {
+        return unbox(Wrapper.forPrimitiveType(type), true, false);
     }
 
-    public static MethodHandle unbox(Class<?> type, boolean exact) {
-        return unbox(Wrapper.forPrimitiveType(type), exact, false);
-    }
-
-    public static MethodHandle unboxRaw(Class<?> type, boolean exact) {
-        return unbox(Wrapper.forPrimitiveType(type), exact, true);
+    /// Primitive conversions
+    public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
+        // Maybe merge this code with Wrapper.convert/cast.
+        Number res = null;
+        if (x == null) {
+            if (!cast)  return null;
+            x = wrap.zero();
+        }
+        if (x instanceof Number) {
+            res = (Number) x;
+        } else if (x instanceof Boolean) {
+            res = ((boolean)x ? 1 : 0);
+        } else if (x instanceof Character) {
+            res = (int)(char)x;
+        } else {
+            // this will fail with the required ClassCastException:
+            res = (Number) x;
+        }
+        if (!cast && !wrap.isConvertibleFrom(Wrapper.forWrapperType(x.getClass())))
+            // this will fail with the required ClassCastException:
+            res = (Number) wrap.wrapperType().cast(x);
+        return res;
     }
 
     /// Converting primitives to references
@@ -285,7 +328,7 @@
         MethodType type = boxType(wrap, raw);
         if (exact) {
             try {
-                mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
+                mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
             } catch (ReflectiveOperationException ex) {
                 mh = null;
             }
@@ -296,22 +339,31 @@
             cache.put(wrap, mh);
             return mh;
         }
-        throw new IllegalArgumentException("cannot find box adapter for " + wrap + (raw ? " (raw)" : ""));
+        throw new IllegalArgumentException("cannot find box adapter for "
+                + wrap + (exact ? " (exact)" : "") + (raw ? " (raw)" : ""));
     }
 
-    public static MethodHandle box(Class<?> type, boolean exact) {
+    public static MethodHandle box(Class<?> type) {
+        boolean exact = false;
+        // e.g., boxShort(short)Short if exact,
+        // e.g., boxShort(short)Object if !exact
         return box(Wrapper.forPrimitiveType(type), exact, false);
     }
 
-    public static MethodHandle boxRaw(Class<?> type, boolean exact) {
+    public static MethodHandle boxRaw(Class<?> type) {
+        boolean exact = false;
+        // e.g., boxShortRaw(int)Short if exact
+        // e.g., boxShortRaw(int)Object if !exact
         return box(Wrapper.forPrimitiveType(type), exact, true);
     }
 
-    public static MethodHandle box(Wrapper type, boolean exact) {
+    public static MethodHandle box(Wrapper type) {
+        boolean exact = false;
         return box(type, exact, false);
     }
 
-    public static MethodHandle boxRaw(Wrapper type, boolean exact) {
+    public static MethodHandle boxRaw(Wrapper type) {
+        boolean exact = false;
         return box(type, exact, true);
     }
 
@@ -319,16 +371,16 @@
 
     static int unboxRawInteger(Object x) {
         if (x instanceof Integer)
-            return unboxInteger(x);
+            return (int) x;
         else
-            return (int) unboxLong(x);
+            return (int) unboxLong(x, false);
     }
 
     static Integer reboxRawInteger(Object x) {
         if (x instanceof Integer)
             return (Integer) x;
         else
-            return (int) unboxLong(x);
+            return (int) unboxLong(x, false);
     }
 
     static Byte reboxRawByte(Object x) {
@@ -362,7 +414,7 @@
 
     static Double reboxRawDouble(Object x) {
         if (x instanceof Double)  return (Double) x;
-        return boxDoubleRaw(unboxLong(x));
+        return boxDoubleRaw(unboxLong(x, true));
     }
 
     private static MethodType reboxType(Wrapper wrap) {
@@ -371,7 +423,7 @@
     }
 
     private static final EnumMap<Wrapper, MethodHandle>[]
-            REBOX_CONVERSIONS = newWrapperCaches(2);
+            REBOX_CONVERSIONS = newWrapperCaches(1);
 
     /**
      * Because we normalize primitive types to reduce the number of signatures,
@@ -380,10 +432,10 @@
      * When the erased primitive value is then boxed into an Integer or Long,
      * the final boxed primitive is sometimes required.  This transformation
      * is called a "rebox".  It takes an Integer or Long and produces some
-     * other boxed value.
+     * other boxed value, typed (inexactly) as an Object
      */
-    public static MethodHandle rebox(Wrapper wrap, boolean exact) {
-        EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[exact?1:0];
+    public static MethodHandle rebox(Wrapper wrap) {
+        EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[0];
         MethodHandle mh = cache.get(wrap);
         if (mh != null) {
             return mh;
@@ -402,14 +454,11 @@
         // look up the method
         String name = "reboxRaw" + wrap.simpleName();
         MethodType type = reboxType(wrap);
-        if (exact) {
-            try {
-                mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
-            } catch (ReflectiveOperationException ex) {
-                mh = null;
-            }
-        } else {
-            mh = rebox(wrap, !exact).asType(IDENTITY.type());
+        try {
+            mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+            mh = mh.asType(IDENTITY.type());
+        } catch (ReflectiveOperationException ex) {
+            mh = null;
         }
         if (mh != null) {
             cache.put(wrap, mh);
@@ -418,8 +467,8 @@
         throw new IllegalArgumentException("cannot find rebox adapter for " + wrap);
     }
 
-    public static MethodHandle rebox(Class<?> type, boolean exact) {
-        return rebox(Wrapper.forPrimitiveType(type), exact);
+    public static MethodHandle rebox(Class<?> type) {
+        return rebox(Wrapper.forPrimitiveType(type));
     }
 
     /// Width-changing conversions between int and long.
@@ -486,9 +535,10 @@
             case VOID:
                 mh = EMPTY;
                 break;
+            case OBJECT:
             case INT: case LONG: case FLOAT: case DOUBLE:
                 try {
-                    mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
+                    mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.simpleName(), type);
                 } catch (ReflectiveOperationException ex) {
                     mh = null;
                 }
@@ -592,7 +642,7 @@
         return t.cast(x);
     }
 
-    private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY;
+    private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY, NEW_ARRAY;
     static {
         try {
             MethodType idType = MethodType.genericMethodType(1);
@@ -600,40 +650,56 @@
             MethodType alwaysZeroType = idType.changeReturnType(int.class);
             MethodType ignoreType = idType.changeReturnType(void.class);
             MethodType zeroObjectType = MethodType.genericMethodType(0);
-            IDENTITY = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", idType);
-            IDENTITY_I = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(int.class, int.class));
-            IDENTITY_J = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(long.class, long.class));
+            IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
+            IDENTITY_I = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(int.class, int.class));
+            IDENTITY_J = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(long.class, long.class));
             //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
-            CAST_REFERENCE = IMPL_LOOKUP.findStatic(ValueConversions.class, "castReference", castType);
-            ALWAYS_NULL = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysNull", idType);
-            ALWAYS_ZERO = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysZero", alwaysZeroType);
-            ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType);
-            IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType);
-            EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1));
-        } catch (Exception ex) {
+            CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType);
+            ALWAYS_NULL = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysNull", idType);
+            ALWAYS_ZERO = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysZero", alwaysZeroType);
+            ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType);
+            IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
+            EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
+            NEW_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "newArray", MethodType.methodType(Object[].class, int.class));
+        } catch (NoSuchMethodException | IllegalAccessException ex) {
             Error err = new InternalError("uncaught exception");
             err.initCause(ex);
             throw err;
         }
     }
 
-    private static final EnumMap<Wrapper, MethodHandle> WRAPPER_CASTS
-            = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
+    // Varargs methods need to be in a separately initialized class, to bootstrapping problems.
+    static class LazyStatics {
+        private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST;
+        static {
+            try {
+                //MAKE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeArray", MethodType.methodType(Object[].class, Object[].class));
+                COPY_AS_REFERENCE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsReferenceArray", MethodType.methodType(Object[].class, Class.class, Object[].class));
+                COPY_AS_PRIMITIVE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class));
+                MAKE_LIST = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeList", MethodType.methodType(List.class, Object[].class));
+            } catch (ReflectiveOperationException ex) {
+                Error err = new InternalError("uncaught exception");
+                err.initCause(ex);
+                throw err;
+            }
+        }
+    }
 
-    private static final EnumMap<Wrapper, MethodHandle> EXACT_WRAPPER_CASTS
-            = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
+    private static final EnumMap<Wrapper, MethodHandle>[] WRAPPER_CASTS
+            = newWrapperCaches(2);
 
     /** Return a method that casts its sole argument (an Object) to the given type
      *  and returns it as the given type (if exact is true), or as plain Object (if erase is true).
      */
-    public static MethodHandle cast(Class<?> type, boolean exact) {
+    public static MethodHandle cast(Class<?> type) {
+        boolean exact = false;
         if (type.isPrimitive())  throw new IllegalArgumentException("cannot cast primitive type "+type);
         MethodHandle mh = null;
         Wrapper wrap = null;
         EnumMap<Wrapper, MethodHandle> cache = null;
         if (Wrapper.isWrapperType(type)) {
             wrap = Wrapper.forWrapperType(type);
-            cache = (exact ? EXACT_WRAPPER_CASTS : WRAPPER_CASTS);
+            cache = WRAPPER_CASTS[exact?1:0];
             mh = cache.get(wrap);
             if (mh != null)  return mh;
         }
@@ -673,7 +739,7 @@
         if (wrap != Wrapper.VOID)
             type = type.appendParameterTypes(wrap.primitiveType());
         try {
-            mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type);
+            mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type);
         } catch (ReflectiveOperationException ex) {
             mh = null;
         }
@@ -692,6 +758,210 @@
         throw new IllegalArgumentException("cannot find identity for " + wrap);
     }
 
+    /// Float/non-float conversions.
+
+    static float doubleToFloat(double x) {
+        return (float) x;
+    }
+    static double floatToDouble(float x) {
+        return x;
+    }
+
+    // narrow double to integral type
+    static long doubleToLong(double x) {
+        return (long) x;
+    }
+    static int doubleToInt(double x) {
+        return (int) x;
+    }
+    static short doubleToShort(double x) {
+        return (short) x;
+    }
+    static char doubleToChar(double x) {
+        return (char) x;
+    }
+    static byte doubleToByte(double x) {
+        return (byte) x;
+    }
+    static boolean doubleToBoolean(double x) {
+        return toBoolean((byte) x);
+    }
+
+    // narrow float to integral type
+    static long floatToLong(float x) {
+        return (long) x;
+    }
+    static int floatToInt(float x) {
+        return (int) x;
+    }
+    static short floatToShort(float x) {
+        return (short) x;
+    }
+    static char floatToChar(float x) {
+        return (char) x;
+    }
+    static byte floatToByte(float x) {
+        return (byte) x;
+    }
+    static boolean floatToBoolean(float x) {
+        return toBoolean((byte) x);
+    }
+
+    // widen integral type to double
+    static double longToDouble(long x) {
+        return x;
+    }
+    static double intToDouble(int x) {
+        return x;
+    }
+    static double shortToDouble(short x) {
+        return x;
+    }
+    static double charToDouble(char x) {
+        return x;
+    }
+    static double byteToDouble(byte x) {
+        return x;
+    }
+    static double booleanToDouble(boolean x) {
+        return fromBoolean(x);
+    }
+
+    // widen integral type to float
+    static float longToFloat(long x) {
+        return x;
+    }
+    static float intToFloat(int x) {
+        return x;
+    }
+    static float shortToFloat(short x) {
+        return x;
+    }
+    static float charToFloat(char x) {
+        return x;
+    }
+    static float byteToFloat(byte x) {
+        return x;
+    }
+    static float booleanToFloat(boolean x) {
+        return fromBoolean(x);
+    }
+
+    static boolean toBoolean(byte x) {
+        // see javadoc for MethodHandles.explicitCastArguments
+        return ((x & 1) != 0);
+    }
+    static byte fromBoolean(boolean x) {
+        // see javadoc for MethodHandles.explicitCastArguments
+        return (x ? (byte)1 : (byte)0);
+    }
+
+    private static final EnumMap<Wrapper, MethodHandle>[]
+            CONVERT_FLOAT_FUNCTIONS = newWrapperCaches(4);
+
+    static MethodHandle convertFloatFunction(Wrapper wrap, boolean toFloat, boolean doubleSize) {
+        EnumMap<Wrapper, MethodHandle> cache = CONVERT_FLOAT_FUNCTIONS[(toFloat?1:0)+(doubleSize?2:0)];
+        MethodHandle mh = cache.get(wrap);
+        if (mh != null) {
+            return mh;
+        }
+        // slow path
+        Wrapper fwrap = (doubleSize ? Wrapper.DOUBLE : Wrapper.FLOAT);
+        Class<?> fix = wrap.primitiveType();
+        Class<?> flt = (doubleSize ? double.class : float.class);
+        Class<?> src = toFloat ? fix : flt;
+        Class<?> dst = toFloat ? flt : fix;
+        if (src == dst)  return identity(wrap);
+        MethodType type = MethodType.methodType(dst, src);
+        switch (wrap) {
+            case VOID:
+                mh = toFloat ? zeroConstantFunction(fwrap) : MethodHandles.dropArguments(EMPTY, 0, flt);
+                break;
+            case OBJECT:
+                mh = toFloat ? unbox(flt) : box(flt);
+                break;
+            default:
+                try {
+                    mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type);
+                } catch (ReflectiveOperationException ex) {
+                    mh = null;
+                }
+                break;
+        }
+        if (mh != null) {
+            assert(mh.type() == type) : mh;
+            cache.put(wrap, mh);
+            return mh;
+        }
+
+        throw new IllegalArgumentException("cannot find float conversion constant for " +
+                                           src.getSimpleName()+" -> "+dst.getSimpleName());
+    }
+
+    public static MethodHandle convertFromFloat(Class<?> fixType) {
+        Wrapper wrap = Wrapper.forPrimitiveType(fixType);
+        return convertFloatFunction(wrap, false, false);
+    }
+    public static MethodHandle convertFromDouble(Class<?> fixType) {
+        Wrapper wrap = Wrapper.forPrimitiveType(fixType);
+        return convertFloatFunction(wrap, false, true);
+    }
+    public static MethodHandle convertToFloat(Class<?> fixType) {
+        Wrapper wrap = Wrapper.forPrimitiveType(fixType);
+        return convertFloatFunction(wrap, true, false);
+    }
+    public static MethodHandle convertToDouble(Class<?> fixType) {
+        Wrapper wrap = Wrapper.forPrimitiveType(fixType);
+        return convertFloatFunction(wrap, true, true);
+    }
+
+    private static String capitalize(String x) {
+        return Character.toUpperCase(x.charAt(0))+x.substring(1);
+    }
+
+    /// Collection of multiple arguments.
+
+    public static Object convertArrayElements(Class<?> arrayType, Object array) {
+        Class<?> src = array.getClass().getComponentType();
+        Class<?> dst = arrayType.getComponentType();
+        if (src == null || dst == null)  throw new IllegalArgumentException("not array type");
+        Wrapper sw = (src.isPrimitive() ? Wrapper.forPrimitiveType(src) : null);
+        Wrapper dw = (dst.isPrimitive() ? Wrapper.forPrimitiveType(dst) : null);
+        int length;
+        if (sw == null) {
+            Object[] a = (Object[]) array;
+            length = a.length;
+            if (dw == null)
+                return Arrays.copyOf(a, length, arrayType.asSubclass(Object[].class));
+            Object res = dw.makeArray(length);
+            dw.copyArrayUnboxing(a, 0, res, 0, length);
+            return res;
+        }
+        length = java.lang.reflect.Array.getLength(array);
+        Object[] res;
+        if (dw == null) {
+            res = Arrays.copyOf(NO_ARGS_ARRAY, length, arrayType.asSubclass(Object[].class));
+        } else {
+            res = new Object[length];
+        }
+        sw.copyArrayBoxing(array, 0, res, 0, length);
+        if (dw == null)  return res;
+        Object a = dw.makeArray(length);
+        dw.copyArrayUnboxing(res, 0, a, 0, length);
+        return a;
+    }
+
+    private static MethodHandle findCollector(String name, int nargs, Class<?> rtype, Class<?>... ptypes) {
+        MethodType type = MethodType.genericMethodType(nargs)
+                .changeReturnType(rtype)
+                .insertParameterTypes(0, ptypes);
+        try {
+            return IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+        } catch (ReflectiveOperationException ex) {
+            return null;
+        }
+    }
+
     private static final Object[] NO_ARGS_ARRAY = {};
     private static Object[] makeArray(Object... args) { return args; }
     private static Object[] array() { return NO_ARGS_ARRAY; }
@@ -723,35 +993,175 @@
                                   Object a4, Object a5, Object a6, Object a7,
                                   Object a8, Object a9)
                 { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
-    static MethodHandle[] makeArrays() {
-        ArrayList<MethodHandle> arrays = new ArrayList<MethodHandle>();
-        MethodHandles.Lookup lookup = IMPL_LOOKUP;
+    private static MethodHandle[] makeArrays() {
+        ArrayList<MethodHandle> mhs = new ArrayList<>();
         for (;;) {
-            int nargs = arrays.size();
-            MethodType type = MethodType.genericMethodType(nargs).changeReturnType(Object[].class);
-            String name = "array";
-            MethodHandle array = null;
-            try {
-                array = lookup.findStatic(ValueConversions.class, name, type);
-            } catch (ReflectiveOperationException ex) {
-            }
-            if (array == null)  break;
-            arrays.add(array);
+            MethodHandle mh = findCollector("array", mhs.size(), Object[].class);
+            if (mh == null)  break;
+            mhs.add(mh);
         }
-        assert(arrays.size() == 11);  // current number of methods
-        return arrays.toArray(new MethodHandle[0]);
+        assert(mhs.size() == 11);  // current number of methods
+        return mhs.toArray(new MethodHandle[MAX_ARITY+1]);
+    }
+    private static final MethodHandle[] ARRAYS = makeArrays();
+
+    // mh-fill versions of the above:
+    private static Object[] newArray(int len) { return new Object[len]; }
+    private static void fillWithArguments(Object[] a, int pos, Object... args) {
+        System.arraycopy(args, 0, a, pos, args.length);
     }
-    static final MethodHandle[] ARRAYS = makeArrays();
+    // using Integer pos instead of int pos to avoid bootstrapping problems
+    private static Object[] fillArray(Object[] a, Integer pos, Object a0)
+                { fillWithArguments(a, pos, a0); return a; }
+    private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1)
+                { fillWithArguments(a, pos, a0, a1); return a; }
+    private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2)
+                { fillWithArguments(a, pos, a0, a1, a2); return a; }
+    private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3)
+                { fillWithArguments(a, pos, a0, a1, a2, a3); return a; }
+    private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+                                  Object a4)
+                { fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; }
+    private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+                                  Object a4, Object a5)
+                { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; }
+    private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+                                  Object a4, Object a5, Object a6)
+                { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; }
+    private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+                                  Object a4, Object a5, Object a6, Object a7)
+                { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; }
+    private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+                                  Object a4, Object a5, Object a6, Object a7,
+                                  Object a8)
+                { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; }
+    private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+                                  Object a4, Object a5, Object a6, Object a7,
+                                  Object a8, Object a9)
+                { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; }
+    private static MethodHandle[] makeFillArrays() {
+        ArrayList<MethodHandle> mhs = new ArrayList<>();
+        mhs.add(null);  // there is no empty fill; at least a0 is required
+        for (;;) {
+            MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Object[].class, Integer.class);
+            if (mh == null)  break;
+            mhs.add(mh);
+        }
+        assert(mhs.size() == 11);  // current number of methods
+        return mhs.toArray(new MethodHandle[0]);
+    }
+    private static final MethodHandle[] FILL_ARRAYS = makeFillArrays();
+
+    private static Object[] copyAsReferenceArray(Class<? extends Object[]> arrayType, Object... a) {
+        return Arrays.copyOf(a, a.length, arrayType);
+    }
+    private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
+        Object a = w.makeArray(boxes.length);
+        w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length);
+        return a;
+    }
 
     /** Return a method handle that takes the indicated number of Object
      *  arguments and returns an Object array of them, as if for varargs.
      */
     public static MethodHandle varargsArray(int nargs) {
-        if (nargs < ARRAYS.length)
-            return ARRAYS[nargs];
-        // else need to spin bytecode or do something else fancy
-        throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs);
+        MethodHandle mh = ARRAYS[nargs];
+        if (mh != null)  return mh;
+        mh = findCollector("array", nargs, Object[].class);
+        if (mh != null)  return ARRAYS[nargs] = mh;
+        MethodHandle producer = filler(0);  // identity function produces result
+        return ARRAYS[nargs] = buildVarargsArray(producer, nargs);
+    }
+
+    private static MethodHandle buildVarargsArray(MethodHandle producer, int nargs) {
+        // Build up the result mh as a sequence of fills like this:
+        //   producer(fill(fill(fill(newArray(23),0,x1..x10),10,x11..x20),20,x21..x23))
+        // The various fill(_,10*I,___*[J]) are reusable.
+        MethodHandle filler = filler(nargs);
+        MethodHandle mh = producer;
+        mh = MethodHandles.dropArguments(mh, 1, filler.type().parameterList());
+        mh = MethodHandles.foldArguments(mh, filler);
+        mh = MethodHandles.foldArguments(mh, buildNewArray(nargs));
+        return mh;
+    }
+
+    private static MethodHandle buildNewArray(int nargs) {
+        return MethodHandles.insertArguments(NEW_ARRAY, 0, (int) nargs);
+    }
+
+    private static final MethodHandle[] FILLERS = new MethodHandle[MAX_ARITY+1];
+    // filler(N).invoke(a, arg0..arg[N-1]) fills a[0]..a[N-1]
+    private static MethodHandle filler(int nargs) {
+        MethodHandle filler = FILLERS[nargs];
+        if (filler != null)  return filler;
+        return FILLERS[nargs] = buildFiller(nargs);
     }
+    private static MethodHandle buildFiller(int nargs) {
+        if (nargs == 0)
+            return MethodHandles.identity(Object[].class);
+        final int CHUNK = (FILL_ARRAYS.length - 1);
+        int rightLen = nargs % CHUNK;
+        int leftLen = nargs - rightLen;
+        if (rightLen == 0) {
+            leftLen = nargs - (rightLen = CHUNK);
+            if (FILLERS[leftLen] == null) {
+                // build some precursors from left to right
+                for (int j = 0; j < leftLen; j += CHUNK)  filler(j);
+            }
+        }
+        MethodHandle leftFill = filler(leftLen);  // recursive fill
+        MethodHandle rightFill = FILL_ARRAYS[rightLen];
+        rightFill = MethodHandles.insertArguments(rightFill, 1, (int) leftLen);  // [leftLen..nargs-1]
+
+        // Combine the two fills: right(left(newArray(nargs), x1..x20), x21..x23)
+        MethodHandle mh = filler(0);  // identity function produces result
+        mh = MethodHandles.dropArguments(mh, 1, rightFill.type().parameterList());
+        mh = MethodHandles.foldArguments(mh, rightFill);
+        if (leftLen > 0) {
+            mh = MethodHandles.dropArguments(mh, 1, leftFill.type().parameterList());
+            mh = MethodHandles.foldArguments(mh, leftFill);
+        }
+        return mh;
+    }
+
+    // Type-polymorphic version of varargs maker.
+    private static final ClassValue<MethodHandle[]> TYPED_COLLECTORS
+        = new ClassValue<MethodHandle[]>() {
+            protected MethodHandle[] computeValue(Class<?> type) {
+                return new MethodHandle[256];
+            }
+    };
+
+    /** Return a method handle that takes the indicated number of
+     *  typed arguments and returns an array of them.
+     *  The type argument is the array type.
+     */
+    public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
+        Class<?> elemType = arrayType.getComponentType();
+        if (elemType == null)  throw new IllegalArgumentException("not an array: "+arrayType);
+        // FIXME: Need more special casing and caching here.
+        if (elemType == Object.class)
+            return varargsArray(nargs);
+        // other cases:  primitive arrays, subtypes of Object[]
+        MethodHandle cache[] = TYPED_COLLECTORS.get(elemType);
+        MethodHandle mh = nargs < cache.length ? cache[nargs] : null;
+        if (mh != null)  return mh;
+        MethodHandle producer = buildArrayProducer(arrayType);
+        mh = buildVarargsArray(producer, nargs);
+        mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
+        cache[nargs] = mh;
+        return mh;
+    }
+
+    private static MethodHandle buildArrayProducer(Class<?> arrayType) {
+        Class<?> elemType = arrayType.getComponentType();
+        if (elemType.isPrimitive())
+            return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType));
+        else
+            return LazyStatics.COPY_AS_REFERENCE_ARRAY.bindTo(arrayType);
+    }
+
+    // List version of varargs maker.
 
     private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
     private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
@@ -784,34 +1194,29 @@
                                      Object a4, Object a5, Object a6, Object a7,
                                      Object a8, Object a9)
                 { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
-    static MethodHandle[] makeLists() {
-        ArrayList<MethodHandle> arrays = new ArrayList<MethodHandle>();
-        MethodHandles.Lookup lookup = IMPL_LOOKUP;
+    private static MethodHandle[] makeLists() {
+        ArrayList<MethodHandle> mhs = new ArrayList<>();
         for (;;) {
-            int nargs = arrays.size();
-            MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class);
-            String name = "list";
-            MethodHandle array = null;
-            try {
-                array = lookup.findStatic(ValueConversions.class, name, type);
-            } catch (ReflectiveOperationException ex) {
-            }
-            if (array == null)  break;
-            arrays.add(array);
+            MethodHandle mh = findCollector("list", mhs.size(), List.class);
+            if (mh == null)  break;
+            mhs.add(mh);
         }
-        assert(arrays.size() == 11);  // current number of methods
-        return arrays.toArray(new MethodHandle[0]);
+        assert(mhs.size() == 11);  // current number of methods
+        return mhs.toArray(new MethodHandle[MAX_ARITY+1]);
     }
-    static final MethodHandle[] LISTS = makeLists();
+    private static final MethodHandle[] LISTS = makeLists();
 
     /** Return a method handle that takes the indicated number of Object
-     *  arguments and returns List.
+     *  arguments and returns a List.
      */
     public static MethodHandle varargsList(int nargs) {
-        if (nargs < LISTS.length)
-            return LISTS[nargs];
-        // else need to spin bytecode or do something else fancy
-        throw new UnsupportedOperationException("NYI");
+        MethodHandle mh = LISTS[nargs];
+        if (mh != null)  return mh;
+        mh = findCollector("list", nargs, List.class);
+        if (mh != null)  return LISTS[nargs] = mh;
+        return LISTS[nargs] = buildVarargsList(nargs);
+    }
+    private static MethodHandle buildVarargsList(int nargs) {
+        return MethodHandles.filterReturnValue(varargsArray(nargs), LazyStatics.MAKE_LIST);
     }
 }
-
--- a/jdk/src/share/classes/sun/invoke/util/VerifyType.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/sun/invoke/util/VerifyType.java	Thu May 12 19:27:49 2011 -0700
@@ -54,9 +54,15 @@
         if (dst == void.class)     return true;  // drop any return value
         if (isNullType(src))       return !dst.isPrimitive();
         if (!src.isPrimitive())    return dst.isAssignableFrom(src);
+        if (!dst.isPrimitive())    return false;
         // Verifier allows an int to carry byte, short, char, or even boolean:
-        if (dst == int.class)      return Wrapper.forPrimitiveType(src).isSubwordOrInt();
-        return false;
+        Wrapper sw = Wrapper.forPrimitiveType(src);
+        if (dst == int.class)      return sw.isSubwordOrInt();
+        Wrapper dw = Wrapper.forPrimitiveType(dst);
+        if (!sw.isSubwordOrInt())  return false;
+        if (!dw.isSubwordOrInt())  return false;
+        if (!dw.isSigned() && sw.isSigned())  return false;
+        return dw.bitWidth() > sw.bitWidth();
     }
 
     /**
@@ -155,6 +161,7 @@
                     return -1;   // truncation may be required
                 if (!dw.isSigned() && sw.isSigned())
                     return -1;   // sign elimination may be required
+                return 1;
             }
             if (src == float.class || dst == float.class) {
                 if (src == double.class || dst == double.class)
--- a/jdk/src/share/classes/sun/invoke/util/Wrapper.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/src/share/classes/sun/invoke/util/Wrapper.java	Thu May 12 19:27:49 2011 -0700
@@ -26,37 +26,47 @@
 package sun.invoke.util;
 
 public enum Wrapper {
-    BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)),
+    BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, new boolean[0], Format.unsigned(1)),
     // These must be in the order defined for widening primitive conversions in JLS 5.1.2
-    BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)),
-    SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)),
-    CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)),
-    INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)),
-    LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)),
-    FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)),
-    DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)),
-    //NULL(Null.class, null.class, 'N', null, Format.other(1)),
-    OBJECT(Object.class, Object.class, 'L', null, Format.other(1)),
+    BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed(8)),
+    SHORT(Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed(16)),
+    CHAR(Character.class, char.class, 'C', (Character)(char)0, new char[0], Format.unsigned(16)),
+    INT(Integer.class, int.class, 'I', (Integer)(int)0, new int[0], Format.signed(32)),
+    LONG(Long.class, long.class, 'J', (Long)(long)0, new long[0], Format.signed(64)),
+    FLOAT(Float.class, float.class, 'F', (Float)(float)0, new float[0], Format.floating(32)),
+    DOUBLE(Double.class, double.class, 'D', (Double)(double)0, new double[0], Format.floating(64)),
+    //NULL(Null.class, null.class, 'N', null, null, Format.other(1)),
+    OBJECT(Object.class, Object.class, 'L', null, new Object[0], Format.other(1)),
     // VOID must be the last type, since it is "assignable" from any other type:
-    VOID(Void.class, void.class, 'V', null, Format.other(0)),
+    VOID(Void.class, void.class, 'V', null, null, Format.other(0)),
     ;
 
     private final Class<?> wrapperType;
     private final Class<?> primitiveType;
     private final char     basicTypeChar;
     private final Object   zero;
+    private final Object   emptyArray;
     private final int      format;
     private final String   simpleName;
 
-    private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, int format) {
+    private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, Object emptyArray, int format) {
         this.wrapperType = wtype;
         this.primitiveType = ptype;
         this.basicTypeChar = tchar;
         this.zero = zero;
+        this.emptyArray = emptyArray;
         this.format = format;
         this.simpleName = wtype.getSimpleName();
     }
 
+    /** For debugging, give the details of this wrapper. */
+    public String detailString() {
+        return simpleName+
+                java.util.Arrays.asList(wrapperType, primitiveType,
+                basicTypeChar, zero,
+                "0x"+Integer.toHexString(format));
+    }
+
     private static abstract class Format {
         static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12;
         static final int
@@ -114,16 +124,18 @@
     public boolean isUnsigned()    { return format >= Format.BOOLEAN && format < Format.FLOAT; }
     /** Is the wrapped type either float or double? */
     public boolean isFloating()    { return format >= Format.FLOAT; }
+    /** Is the wrapped type either void or a reference? */
+    public boolean isOther()       { return (format & ~Format.SLOT_MASK) == 0; }
 
-    /** Does the JVM verifier allow a variable of this wrapper's
+    /** Does the JLS 5.1.2 allow a variable of this wrapper's
      *  primitive type to be assigned from a value of the given wrapper's primitive type?
      *  Cases:
      *  <ul>
      *  <li>unboxing followed by widening primitive conversion
-     *  <li>any type converted to {@code void}
+     *  <li>any type converted to {@code void} (i.e., dropping a method call's value)
      *  <li>boxing conversion followed by widening reference conversion to {@code Object}
-     *  <li>conversion of {@code boolean} to any type
      *  </ul>
+     *  These are the cases allowed by MethodHandle.asType and convertArguments.
      */
     public boolean isConvertibleFrom(Wrapper source) {
         if (this == source)  return true;
@@ -131,13 +143,75 @@
             // At best, this is a narrowing conversion.
             return false;
         }
-        if ((this.format ^ source.format) == (Format.SHORT ^ Format.CHAR)) {
-            assert (this == SHORT && source == CHAR) || (this == CHAR && source == SHORT);
+        // All conversions are allowed in the enum order between floats and signed ints.
+        // First detect non-signed non-float types (boolean, char, Object, void).
+        boolean floatOrSigned = (((this.format & source.format) & Format.SIGNED) != 0);
+        if (!floatOrSigned) {
+            if (this.isOther())  return true;
+            // can convert char to int or wider, but nothing else
+            if (source.format == Format.CHAR)  return true;
+            // no other conversions are classified as widening
             return false;
         }
+        // All signed and float conversions in the enum order are widening.
+        assert(this.isFloating() || this.isSigned());
+        assert(source.isFloating() || source.isSigned());
         return true;
     }
 
+    static { assert(checkConvertibleFrom()); }
+    private static boolean checkConvertibleFrom() {
+        // Check the matrix for correct classification of widening conversions.
+        for (Wrapper w : values()) {
+            assert(w.isConvertibleFrom(w));
+            assert(VOID.isConvertibleFrom(w));
+            if (w != VOID) {
+                assert(OBJECT.isConvertibleFrom(w));
+                assert(!w.isConvertibleFrom(VOID));
+            }
+            // check relations with unsigned integral types:
+            if (w != CHAR) {
+                assert(!CHAR.isConvertibleFrom(w));
+                if (!w.isConvertibleFrom(INT))
+                    assert(!w.isConvertibleFrom(CHAR));
+            }
+            if (w != BOOLEAN) {
+                assert(!BOOLEAN.isConvertibleFrom(w));
+                if (w != VOID && w != OBJECT)
+                    assert(!w.isConvertibleFrom(BOOLEAN));
+            }
+            // check relations with signed integral types:
+            if (w.isSigned()) {
+                for (Wrapper x : values()) {
+                    if (w == x)  continue;
+                    if (x.isFloating())
+                        assert(!w.isConvertibleFrom(x));
+                    else if (x.isSigned()) {
+                        if (w.compareTo(x) < 0)
+                            assert(!w.isConvertibleFrom(x));
+                        else
+                            assert(w.isConvertibleFrom(x));
+                    }
+                }
+            }
+            // check relations with floating types:
+            if (w.isFloating()) {
+                for (Wrapper x : values()) {
+                    if (w == x)  continue;
+                    if (x.isSigned())
+                        assert(w.isConvertibleFrom(x));
+                    else if (x.isFloating()) {
+                        if (w.compareTo(x) < 0)
+                            assert(!w.isConvertibleFrom(x));
+                        else
+                            assert(w.isConvertibleFrom(x));
+                    }
+                }
+            }
+        }
+        return true;  // i.e., assert(true)
+    }
+
     /** Produce a zero value for the given wrapper type.
      *  This will be a numeric zero for a number or character,
      *  false for a boolean, and null for a reference or void.
@@ -549,7 +623,7 @@
     }
 
     private static boolean boolValue(long bits) {
-        //bits &= 1;  // simple 31-bit zero extension
+        bits &= 1;  // simple 31-bit zero extension
         return (bits != 0);
     }
 
@@ -559,4 +633,31 @@
     private static RuntimeException newIllegalArgumentException(String message) {
         return new IllegalArgumentException(message);
     }
+
+    // primitive array support
+    public Object makeArray(int len) {
+        return java.lang.reflect.Array.newInstance(primitiveType, len);
+    }
+    public Class<?> arrayType() {
+        return emptyArray.getClass();
+    }
+    public void copyArrayUnboxing(Object[] values, int vpos, Object a, int apos, int length) {
+        if (a.getClass() != arrayType())
+            arrayType().cast(a);  // throw NPE or CCE if bad type
+        for (int i = 0; i < length; i++) {
+            Object value = values[i+vpos];
+            value = convert(value, primitiveType);
+            java.lang.reflect.Array.set(a, i+apos, value);
+        }
+    }
+    public void copyArrayBoxing(Object a, int apos, Object[] values, int vpos, int length) {
+        if (a.getClass() != arrayType())
+            arrayType().cast(a);  // throw NPE or CCE if bad type
+        for (int i = 0; i < length; i++) {
+            Object value = java.lang.reflect.Array.get(a, i+apos);
+            //Already done: value = convert(value, primitiveType);
+            assert(value.getClass() == wrapperType);
+            values[i+vpos] = value;
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/6998541/Test6998541.java	Thu May 12 19:27:49 2011 -0700
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2011, 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.
+ *
+ * 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.
+ *
+ */
+
+/**
+ * @test
+ * @bug 6998541
+ * @summary JSR 292 implement missing return-type conversion for OP_RETYPE_RAW
+ *
+ * @run main/othervm -Xbatch
+ *    -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2
+ *       -DTest6998541.N=100000 -DTest6998541.KIND=cast Test6998541
+ * @run main/othervm -Xbatch
+ *    -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2
+ *       -DTest6998541.N=100000 -DTest6998541.KIND=normal Test6998541
+ */
+
+import java.util.*;
+
+import java.lang.invoke.*;
+import static java.lang.invoke.MethodHandles.*;
+
+public class Test6998541 {
+    private static final Class  CLASS = Test6998541.class;
+    private static final String NAME  = "identity";
+    private static final int    N     = Math.max(2, Integer.getInteger(CLASS.getSimpleName()+".N", 10000));
+    private static final String KIND  = System.getProperty(CLASS.getSimpleName()+".KIND", "cast");
+    private static final int    BITS  = 0x00000201;
+
+    private static final boolean DO_CASTS = !KIND.equals("normal");
+
+    public static void main(String[] args) throws Throwable {
+        System.out.println("KIND="+KIND+" DO_CASTS="+DO_CASTS+" N="+N);
+        doboolean();
+        dobyte();
+        dochar();
+        doshort();
+        doint();
+        dolong();
+        dofloat();
+        dodouble();
+        dovoid();
+    }
+
+    private static void doboolean() throws Throwable {
+        for (int i = 0; i < N; i++) {
+            boolean2prim(false);
+            boolean2prim(true);
+        }
+        boolean2prim_invalid(true);
+    }
+    private static void dobyte() throws Throwable {
+        byte x = Byte.MIN_VALUE;
+        for (int i = 0; i < N; i++, x++)
+            byte2prim(x);
+        byte2prim_invalid(x);
+    }
+    private static void dochar() throws Throwable {
+        char x = Character.MIN_VALUE;
+        for (int i = 0; i < N; i++, x++)
+            char2prim(x);
+        char2prim_invalid(x);
+    }
+    private static void doshort() throws Throwable {
+        short x = Short.MIN_VALUE;
+        for (int i = 0; i < N; i++, x++)
+            short2prim(x);
+        short2prim_invalid(x);
+    }
+    private static void doint() throws Throwable {
+        int x = Integer.MIN_VALUE;
+        int D = Integer.MAX_VALUE / (N / 2) | BITS;
+        for (int i = 0; i < N; i++, x += D) {
+            int2prim(x);
+        }
+        int2prim_invalid(x);
+    }
+    private static void dolong() throws Throwable {
+        long x = Long.MIN_VALUE;
+        long D = Long.MAX_VALUE / ((long) (N / 2)) | BITS;
+        for (int i = 0; i < N; i++, x += D)
+            long2prim(x);
+        long2prim_invalid(x);
+    }
+    private static void dofloat() throws Throwable {
+        float x = Float.MIN_VALUE;
+        float D = Float.MAX_VALUE / ((float) (N / 2));
+        for (int i = 0; i < N; i++, x += D)
+            float2prim(x);
+        float2prim_invalid(x);
+    }
+    private static void dodouble() throws Throwable {
+        double x = Double.MIN_VALUE;
+        double D = Double.MAX_VALUE / ((double) (N / 2));
+        for (int i = 0; i < N; i++, x += D)
+            double2prim(x);
+        double2prim_invalid(x);
+    }
+    private static void dovoid() throws Throwable {
+        for (int i = 0; i < N; i++) {
+            void2prim(i);
+        }
+        void2prim_invalid(0);
+        // do the other direction here also:
+        for (int i = 0; i < N; i++) {
+            prim2void(i);
+        }
+        prim2void_invalid(0);
+    }
+
+    private static void assertEquals(Object o, Object o2) {
+        if (!o.equals(o2))
+            throw new AssertionError("expected: " + o + ", found: " + o2);
+    }
+    private static void fail() {
+        throw new AssertionError();
+    }
+
+    private final static MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+    private static MethodHandle mh(Class ret, Class... args) {
+        try {
+            MethodType mt  = MethodType.methodType(ret, args);
+            Class lookupRet = (args.length == 0 ? void.class : args[0]);
+            MethodHandle mh = lookup.findStatic(CLASS, NAME, mt.changeReturnType(lookupRet));
+            if (DO_CASTS)
+                return MethodHandles.explicitCastArguments(mh, mt);
+            if (canDoAsType(mh.type(), mt))
+                return mh.asType(mt);
+            try {
+                mh.asType(mt);
+                throw new AssertionError("asType should not succeed: "+mh+" => "+mt);
+            } catch (WrongMethodTypeException ex) {
+                // this was a required WMTE
+                return mh.asType(mt.generic()).asType(mt);
+            }
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+    private static final Class<?>[] NUMERIC_TYPE_WIDENING_ORDER = {
+        byte.class, short.class, int.class, long.class, float.class, double.class
+    };
+    private static boolean canDoAsType(Class<?> src, Class<?> dst) {
+        if (src == dst)  return true;
+        if (dst == void.class)  return true;
+        if (!src.isPrimitive() || !dst.isPrimitive())  return true;
+        // primitive conversion works for asType only when it's widening
+        if (src == boolean.class || dst == boolean.class)  return false;
+        if (dst == char.class)  return false;
+        if (src == char.class)  src = int.class;  // can widen char to int
+        for (Class<?> ntype : NUMERIC_TYPE_WIDENING_ORDER) {
+            if (src == ntype)  return true;
+            if (dst == ntype)  return false;
+        }
+        throw new AssertionError("should not reach here: "+src+", "+dst);
+    }
+    private static boolean canDoAsType(MethodType mt0, MethodType mt1) {
+        Class<?> rt0 = mt0.returnType();
+        Class<?> rt1 = mt1.returnType();
+        if (!canDoAsType(rt0, rt1))  return false;
+        int argc = mt0.parameterCount();
+        if (argc != mt1.parameterCount())  return false;
+        for (int i = 0; i < argc; i++) {
+            if (!canDoAsType(mt1.parameterType(i), mt0.parameterType(i)))
+                return false;
+        }
+        return true;
+    }
+
+    private static MethodHandle mh_z(Class ret) { return mh(ret, boolean.class); }
+
+    private final static MethodHandle mh_zz = mh_z(boolean.class);
+    private final static MethodHandle mh_bz = mh_z(byte.class   );
+    private final static MethodHandle mh_cz = mh_z(char.class   );
+    private final static MethodHandle mh_sz = mh_z(short.class  );
+    private final static MethodHandle mh_iz = mh_z(int.class    );
+    private final static MethodHandle mh_jz = mh_z(long.class   );
+    private final static MethodHandle mh_fz = mh_z(float.class  );
+    private final static MethodHandle mh_dz = mh_z(double.class );
+
+    private static void boolean2prim(boolean x) throws Throwable {
+        int i = x ? 1 : 0;
+        assertEquals(          x, (boolean) mh_zz.invokeExact(x));  // boolean -> boolean
+        if (!DO_CASTS)  return;
+        assertEquals((byte)    i, (byte)    mh_bz.invokeExact(x));  // boolean -> byte
+        assertEquals((char)    i, (char)    mh_cz.invokeExact(x));  // boolean -> char
+        assertEquals((short)   i, (short)   mh_sz.invokeExact(x));  // boolean -> short
+        assertEquals((int)     i, (int)     mh_iz.invokeExact(x));  // boolean -> int
+        assertEquals((long)    i, (long)    mh_jz.invokeExact(x));  // boolean -> long
+        assertEquals((float)   i, (float)   mh_fz.invokeExact(x));  // boolean -> float
+        assertEquals((double)  i, (double)  mh_dz.invokeExact(x));  // boolean -> double
+    }
+    private static void boolean2prim_invalid(boolean x) throws Throwable {
+        if (DO_CASTS)  return;
+        try { byte    y = (byte)    mh_bz.invokeExact(x); fail(); } catch (ClassCastException _) {}  // boolean -> byte
+        try { char    y = (char)    mh_cz.invokeExact(x); fail(); } catch (ClassCastException _) {}  // boolean -> char
+        try { short   y = (short)   mh_sz.invokeExact(x); fail(); } catch (ClassCastException _) {}  // boolean -> short
+        try { int     y = (int)     mh_iz.invokeExact(x); fail(); } catch (ClassCastException _) {}  // boolean -> int
+        try { long    y = (long)    mh_jz.invokeExact(x); fail(); } catch (ClassCastException _) {}  // boolean -> long
+        try { float   y = (float)   mh_fz.invokeExact(x); fail(); } catch (ClassCastException _) {}  // boolean -> float
+        try { double  y = (double)  mh_dz.invokeExact(x); fail(); } catch (ClassCastException _) {}  // boolean -> double
+    }
+
+    private static MethodHandle mh_b(Class ret) { return mh(ret, byte.class); }
+
+    private final static MethodHandle mh_zb = mh_b(boolean.class);
+    private final static MethodHandle mh_bb = mh_b(byte.class   );
+    private final static MethodHandle mh_cb = mh_b(char.class   );
+    private final static MethodHandle mh_sb = mh_b(short.class  );
+    private final static MethodHandle mh_ib = mh_b(int.class    );
+    private final static MethodHandle mh_jb = mh_b(long.class   );
+    private final static MethodHandle mh_fb = mh_b(float.class  );
+    private final static MethodHandle mh_db = mh_b(double.class );
+
+    private static void byte2prim(byte x) throws Throwable {
+        assertEquals((byte)    x, (byte)    mh_bb.invokeExact(x));  // byte -> byte
+        assertEquals((short)   x, (short)   mh_sb.invokeExact(x));  // byte -> short
+        assertEquals((int)     x, (int)     mh_ib.invokeExact(x));  // byte -> int
+        assertEquals((long)    x, (long)    mh_jb.invokeExact(x));  // byte -> long
+        assertEquals((float)   x, (float)   mh_fb.invokeExact(x));  // byte -> float
+        assertEquals((double)  x, (double)  mh_db.invokeExact(x));  // byte -> double
+        if (!DO_CASTS)  return;
+        boolean z = ((x & 1) != 0);
+        assertEquals((char)    x, (char)    mh_cb.invokeExact(x));  // byte -> char
+        assertEquals((boolean) z, (boolean) mh_zb.invokeExact(x));  // byte -> boolean
+    }
+    private static void byte2prim_invalid(byte x) throws Throwable {
+        if (DO_CASTS)  return;
+        try { char    y = (char)    mh_cb.invokeExact(x); fail(); } catch (ClassCastException _) {}  // byte -> char
+        try { boolean y = (boolean) mh_zb.invokeExact(x); fail(); } catch (ClassCastException _) {}  // byte -> boolean
+    }
+
+    private static MethodHandle mh_c(Class ret) { return mh(ret, char.class); }
+
+    private final static MethodHandle mh_zc = mh_c(boolean.class);
+    private final static MethodHandle mh_bc = mh_c(byte.class   );
+    private final static MethodHandle mh_cc = mh_c(char.class   );
+    private final static MethodHandle mh_sc = mh_c(short.class  );
+    private final static MethodHandle mh_ic = mh_c(int.class    );
+    private final static MethodHandle mh_jc = mh_c(long.class   );
+    private final static MethodHandle mh_fc = mh_c(float.class  );
+    private final static MethodHandle mh_dc = mh_c(double.class );
+
+    private static void char2prim(char x) throws Throwable {
+        assertEquals((char)    x, (char)    mh_cc.invokeExact(x));  // char -> char
+        assertEquals((int)     x, (int)     mh_ic.invokeExact(x));  // char -> int
+        assertEquals((long)    x, (long)    mh_jc.invokeExact(x));  // char -> long
+        assertEquals((float)   x, (float)   mh_fc.invokeExact(x));  // char -> float
+        assertEquals((double)  x, (double)  mh_dc.invokeExact(x));  // char -> double
+        if (!DO_CASTS)  return;
+        boolean z = ((x & 1) != 0);
+        assertEquals((boolean) z, (boolean) mh_zc.invokeExact(x));  // char -> boolean
+        assertEquals((byte)    x, (byte)    mh_bc.invokeExact(x));  // char -> byte
+        assertEquals((short)   x, (short)   mh_sc.invokeExact(x));  // char -> short
+    }
+    private static void char2prim_invalid(char x) throws Throwable {
+        if (DO_CASTS)  return;
+        try { boolean y = (boolean) mh_zc.invokeExact(x); fail(); } catch (ClassCastException _) {}  // char -> boolean
+        try { byte    y = (byte)    mh_bc.invokeExact(x); fail(); } catch (ClassCastException _) {}  // char -> byte
+        try { short   y = (short)   mh_sc.invokeExact(x); fail(); } catch (ClassCastException _) {}  // char -> short
+    }
+
+    private static MethodHandle mh_s(Class ret) { return mh(ret, short.class); }
+
+    private final static MethodHandle mh_zs = mh_s(boolean.class);
+    private final static MethodHandle mh_bs = mh_s(byte.class   );
+    private final static MethodHandle mh_cs = mh_s(char.class   );
+    private final static MethodHandle mh_ss = mh_s(short.class  );
+    private final static MethodHandle mh_is = mh_s(int.class    );
+    private final static MethodHandle mh_js = mh_s(long.class   );
+    private final static MethodHandle mh_fs = mh_s(float.class  );
+    private final static MethodHandle mh_ds = mh_s(double.class );
+
+    private static void short2prim(short x) throws Throwable {
+        assertEquals((short)   x, (short)   mh_ss.invokeExact(x));  // short -> short
+        assertEquals((int)     x, (int)     mh_is.invokeExact(x));  // short -> int
+        assertEquals((long)    x, (long)    mh_js.invokeExact(x));  // short -> long
+        assertEquals((float)   x, (float)   mh_fs.invokeExact(x));  // short -> float
+        assertEquals((double)  x, (double)  mh_ds.invokeExact(x));  // short -> double
+        if (!DO_CASTS)  return;
+        boolean z = ((x & 1) != 0);
+        assertEquals((boolean) z, (boolean) mh_zs.invokeExact(x));  // short -> boolean
+        assertEquals((byte)    x, (byte)    mh_bs.invokeExact(x));  // short -> byte
+        assertEquals((char)    x, (char)    mh_cs.invokeExact(x));  // short -> char
+    }
+    private static void short2prim_invalid(short x) throws Throwable {
+        if (DO_CASTS)  return;
+        try { boolean y = (boolean) mh_zs.invokeExact(x); fail(); } catch (ClassCastException _) {}  // short -> boolean
+        try { byte    y = (byte)    mh_bs.invokeExact(x); fail(); } catch (ClassCastException _) {}  // short -> byte
+        try { char    y = (char)    mh_cs.invokeExact(x); fail(); } catch (ClassCastException _) {}  // short -> char
+    }
+
+    private static MethodHandle mh_i(Class ret) { return mh(ret, int.class); }
+
+    private final static MethodHandle mh_zi = mh_i(boolean.class);
+    private final static MethodHandle mh_bi = mh_i(byte.class   );
+    private final static MethodHandle mh_ci = mh_i(char.class   );
+    private final static MethodHandle mh_si = mh_i(short.class  );
+    private final static MethodHandle mh_ii = mh_i(int.class    );
+    private final static MethodHandle mh_ji = mh_i(long.class   );
+    private final static MethodHandle mh_fi = mh_i(float.class  );
+    private final static MethodHandle mh_di = mh_i(double.class );
+
+    private static void int2prim(int x) throws Throwable {
+        assertEquals((int)     x, (int)     mh_ii.invokeExact(x));  // int -> int
+        assertEquals((long)    x, (long)    mh_ji.invokeExact(x));  // int -> long
+        assertEquals((float)   x, (float)   mh_fi.invokeExact(x));  // int -> float
+        assertEquals((double)  x, (double)  mh_di.invokeExact(x));  // int -> double
+        if (!DO_CASTS)  return;
+        boolean z = ((x & 1) != 0);
+        assertEquals((boolean) z, (boolean) mh_zi.invokeExact(x));  // int -> boolean
+        assertEquals((byte)    x, (byte)    mh_bi.invokeExact(x));  // int -> byte
+        assertEquals((char)    x, (char)    mh_ci.invokeExact(x));  // int -> char
+        assertEquals((short)   x, (short)   mh_si.invokeExact(x));  // int -> short
+    }
+    private static void int2prim_invalid(int x) throws Throwable {
+        if (DO_CASTS)  return;
+        try { boolean y = (boolean) mh_zi.invokeExact(x); fail(); } catch (ClassCastException _) {}  // int -> boolean
+        try { byte    y = (byte)    mh_bi.invokeExact(x); fail(); } catch (ClassCastException _) {}  // int -> byte
+        try { char    y = (char)    mh_ci.invokeExact(x); fail(); } catch (ClassCastException _) {}  // int -> char
+        try { short   y = (short)   mh_si.invokeExact(x); fail(); } catch (ClassCastException _) {}  // int -> short
+    }
+
+    private static MethodHandle mh_j(Class ret) { return mh(ret, long.class); }
+
+    private final static MethodHandle mh_zj = mh_j(boolean.class);
+    private final static MethodHandle mh_bj = mh_j(byte.class   );
+    private final static MethodHandle mh_cj = mh_j(char.class   );
+    private final static MethodHandle mh_sj = mh_j(short.class  );
+    private final static MethodHandle mh_ij = mh_j(int.class    );
+    private final static MethodHandle mh_jj = mh_j(long.class   );
+    private final static MethodHandle mh_fj = mh_j(float.class  );
+    private final static MethodHandle mh_dj = mh_j(double.class );
+
+    private static void long2prim(long x) throws Throwable {
+        assertEquals((long)   x, (long)    mh_jj.invokeExact(x));  // long -> long
+        assertEquals((float)  x, (float)   mh_fj.invokeExact(x));  // long -> float
+        assertEquals((double) x, (double)  mh_dj.invokeExact(x));  // long -> double
+        if (!DO_CASTS)  return;
+        boolean z = ((x & 1) != 0);
+        assertEquals((boolean)z, (boolean) mh_zj.invokeExact(x));  // long -> boolean
+        assertEquals((byte)   x, (byte)    mh_bj.invokeExact(x));  // long -> byte
+        assertEquals((char)   x, (char)    mh_cj.invokeExact(x));  // long -> char
+        assertEquals((short)  x, (short)   mh_sj.invokeExact(x));  // long -> short
+        assertEquals((int)    x, (int)     mh_ij.invokeExact(x));  // long -> int
+    }
+    private static void long2prim_invalid(long x) throws Throwable {
+        if (DO_CASTS)  return;
+        try { boolean y = (boolean) mh_zj.invokeExact(x); fail(); } catch (ClassCastException _) {}  // long -> boolean
+        try { byte    y = (byte)    mh_bj.invokeExact(x); fail(); } catch (ClassCastException _) {}  // long -> byte
+        try { char    y = (char)    mh_cj.invokeExact(x); fail(); } catch (ClassCastException _) {}  // long -> char
+        try { short   y = (short)   mh_sj.invokeExact(x); fail(); } catch (ClassCastException _) {}  // long -> short
+        try { int     y = (int)     mh_ij.invokeExact(x); fail(); } catch (ClassCastException _) {}  // long -> int
+    }
+
+    private static MethodHandle mh_f(Class ret) { return mh(ret, float.class); }
+
+    private final static MethodHandle mh_zf = mh_f(boolean.class);
+    private final static MethodHandle mh_bf = mh_f(byte.class   );
+    private final static MethodHandle mh_cf = mh_f(char.class   );
+    private final static MethodHandle mh_sf = mh_f(short.class  );
+    private final static MethodHandle mh_if = mh_f(int.class    );
+    private final static MethodHandle mh_jf = mh_f(long.class   );
+    private final static MethodHandle mh_ff = mh_f(float.class  );
+    private final static MethodHandle mh_df = mh_f(double.class );
+
+    private static void float2prim(float x) throws Throwable {
+        assertEquals((float)   x, (float)   mh_ff.invokeExact(x));  // float -> float
+        assertEquals((double)  x, (double)  mh_df.invokeExact(x));  // float -> double
+        if (!DO_CASTS)  return;
+        boolean z = (((byte) x & 1) != 0);
+        assertEquals((boolean) z, (boolean) mh_zf.invokeExact(x));  // float -> boolean
+        assertEquals((byte)    x, (byte)    mh_bf.invokeExact(x));  // float -> byte
+        assertEquals((char)    x, (char)    mh_cf.invokeExact(x));  // float -> char
+        assertEquals((short)   x, (short)   mh_sf.invokeExact(x));  // float -> short
+        assertEquals((int)     x, (int)     mh_if.invokeExact(x));  // float -> int
+        assertEquals((long)    x, (long)    mh_jf.invokeExact(x));  // float -> long
+    }
+    private static void float2prim_invalid(float x) throws Throwable {
+        if (DO_CASTS)  return;
+        try { boolean y = (boolean) mh_zf.invokeExact(x); fail(); } catch (ClassCastException _) {}  // float -> boolean
+        try { byte    y = (byte)    mh_bf.invokeExact(x); fail(); } catch (ClassCastException _) {}  // float -> byte
+        try { char    y = (char)    mh_cf.invokeExact(x); fail(); } catch (ClassCastException _) {}  // float -> char
+        try { short   y = (short)   mh_sf.invokeExact(x); fail(); } catch (ClassCastException _) {}  // float -> short
+        try { int     y = (int)     mh_if.invokeExact(x); fail(); } catch (ClassCastException _) {}  // float -> int
+        try { long    y = (long)    mh_jf.invokeExact(x); fail(); } catch (ClassCastException _) {}  // float -> long
+    }
+
+    private static MethodHandle mh_d(Class ret) { return mh(ret, double.class); }
+
+    private final static MethodHandle mh_zd = mh_d(boolean.class);
+    private final static MethodHandle mh_bd = mh_d(byte.class   );
+    private final static MethodHandle mh_cd = mh_d(char.class   );
+    private final static MethodHandle mh_sd = mh_d(short.class  );
+    private final static MethodHandle mh_id = mh_d(int.class    );
+    private final static MethodHandle mh_jd = mh_d(long.class   );
+    private final static MethodHandle mh_fd = mh_d(float.class  );
+    private final static MethodHandle mh_dd = mh_d(double.class );
+
+    private static void double2prim(double x) throws Throwable {
+        assertEquals((double) x, (double)  mh_dd.invokeExact(x));  // double -> double
+        if (!DO_CASTS)  return;
+        boolean z = (((byte) x & 1) != 0);
+        assertEquals((boolean) z, (boolean) mh_zd.invokeExact(x));  // double -> boolean
+        assertEquals((byte)    x, (byte)    mh_bd.invokeExact(x));  // double -> byte
+        assertEquals((char)    x, (char)    mh_cd.invokeExact(x));  // double -> char
+        assertEquals((short)   x, (short)   mh_sd.invokeExact(x));  // double -> short
+        assertEquals((int)     x, (int)     mh_id.invokeExact(x));  // double -> int
+        assertEquals((long)    x, (long)    mh_jd.invokeExact(x));  // double -> long
+        assertEquals((float)   x, (float)   mh_fd.invokeExact(x));  // double -> float
+    }
+    private static void double2prim_invalid(double x) throws Throwable {
+        if (DO_CASTS)  return;
+        try { boolean y = (boolean) mh_zd.invokeExact(x); fail(); } catch (ClassCastException _) {}  // double -> boolean
+        try { byte    y = (byte)    mh_bd.invokeExact(x); fail(); } catch (ClassCastException _) {}  // double -> byte
+        try { char    y = (char)    mh_cd.invokeExact(x); fail(); } catch (ClassCastException _) {}  // double -> char
+        try { short   y = (short)   mh_sd.invokeExact(x); fail(); } catch (ClassCastException _) {}  // double -> short
+        try { int     y = (int)     mh_id.invokeExact(x); fail(); } catch (ClassCastException _) {}  // double -> int
+        try { long    y = (long)    mh_jd.invokeExact(x); fail(); } catch (ClassCastException _) {}  // double -> long
+        try { float   y = (float)   mh_fd.invokeExact(x); fail(); } catch (ClassCastException _) {}  // double -> float
+    }
+
+    private final static MethodHandle mh_zv = mh(boolean.class);
+    private final static MethodHandle mh_bv = mh(byte.class   );
+    private final static MethodHandle mh_cv = mh(char.class   );
+    private final static MethodHandle mh_sv = mh(short.class  );
+    private final static MethodHandle mh_iv = mh(int.class    );
+    private final static MethodHandle mh_jv = mh(long.class   );
+    private final static MethodHandle mh_fv = mh(float.class  );
+    private final static MethodHandle mh_dv = mh(double.class );
+
+    private static void void2prim(int i) throws Throwable {
+        if (!DO_CASTS)  return;
+        assertEquals(        false, (boolean) mh_zv.invokeExact());  // void -> boolean
+        assertEquals((byte)  0,     (byte)    mh_bv.invokeExact());  // void -> byte
+        assertEquals((char)  0,     (char)    mh_cv.invokeExact());  // void -> char
+        assertEquals((short) 0,     (short)   mh_sv.invokeExact());  // void -> short
+        assertEquals(        0,     (int)     mh_iv.invokeExact());  // void -> int
+        assertEquals(        0L,    (long)    mh_jv.invokeExact());  // void -> long
+        assertEquals(        0.0f,  (float)   mh_fv.invokeExact());  // void -> float
+        assertEquals(        0.0d,  (double)  mh_dv.invokeExact());  // void -> double
+    }
+
+    private static void void2prim_invalid(double x) throws Throwable {
+        if (DO_CASTS)  return;
+        try { assertEquals(        false, (boolean) mh_zv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> boolean
+        try { assertEquals((byte)  0,     (byte)    mh_bv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> byte
+        try { assertEquals((char)  0,     (char)    mh_cv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> char
+        try { assertEquals((short) 0,     (short)   mh_sv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> short
+        try { assertEquals(        0,     (int)     mh_iv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> int
+        try { assertEquals(        0L,    (long)    mh_jv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> long
+        try { assertEquals(        0.0f,  (float)   mh_fv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> float
+        try { assertEquals(        0.0d,  (double)  mh_dv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> double
+    }
+
+    private static MethodHandle mh_v(Class arg) { return mh(void.class, arg); }
+
+    private final static MethodHandle mh_vz = mh_v(boolean.class);
+    private final static MethodHandle mh_vb = mh_v(byte.class   );
+    private final static MethodHandle mh_vc = mh_v(char.class   );
+    private final static MethodHandle mh_vs = mh_v(short.class  );
+    private final static MethodHandle mh_vi = mh_v(int.class    );
+    private final static MethodHandle mh_vj = mh_v(long.class   );
+    private final static MethodHandle mh_vf = mh_v(float.class  );
+    private final static MethodHandle mh_vd = mh_v(double.class );
+
+    private static void prim2void(int x) throws Throwable {
+        boolean z = ((x & 1) != 0);
+        mh_vz.invokeExact(         z);  // boolean -> void
+        mh_vb.invokeExact((byte)   x);  // byte    -> void
+        mh_vc.invokeExact((char)   x);  // char    -> void
+        mh_vs.invokeExact((short)  x);  // short   -> void
+        mh_vi.invokeExact((int)    x);  // int     -> void
+        mh_vj.invokeExact((long)   x);  // long    -> void
+        mh_vf.invokeExact((float)  x);  // float   -> void
+        mh_vd.invokeExact((double) x);  // double  -> void
+    }
+
+    private static void prim2void_invalid(int x) throws Throwable {
+        // no cases
+    }
+
+    private static boolean identity(boolean v) { return v; }
+    private static byte    identity(byte    v) { return v; }
+    private static char    identity(char    v) { return v; }
+    private static short   identity(short   v) { return v; }
+    private static int     identity(int     v) { return v; }
+    private static long    identity(long    v) { return v; }
+    private static float   identity(float   v) { return v; }
+    private static double  identity(double  v) { return v; }
+    private static void    identity() {}
+}
--- a/jdk/test/java/lang/invoke/InvokeGenericTest.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/test/java/lang/invoke/InvokeGenericTest.java	Thu May 12 19:27:49 2011 -0700
@@ -53,6 +53,10 @@
         if (vstr != null)  verbosity = Integer.parseInt(vstr);
     }
 
+    public static void main(String... av) throws Throwable {
+        new InvokeGenericTest().testFirst();
+    }
+
     @Test
     public void testFirst() throws Throwable {
         verbosity += 9; try {
@@ -103,7 +107,7 @@
     void startTest(String name) {
         if (testName != null)  printCounts();
         if (verbosity >= 1)
-            System.out.println(name);
+            System.out.println("["+name+"]");
         posTests = negTests = 0;
         testName = name;
     }
@@ -356,6 +360,18 @@
     }
 
     @Test
+    public void testSimplePrims() throws Throwable {
+        startTest("testSimplePrims");
+        countTest();
+        int[] args = { 1, 2 };
+        MethodHandle mh = callable(Object.class, Object.class);
+        Object res; List resl;
+        res = resl = (List) mh.invoke(args[0], args[1]);
+        //System.out.println(res);
+        assertEquals(Arrays.toString(args), res.toString());
+    }
+
+    @Test
     public void testAlternateName() throws Throwable {
         startTest("testAlternateName");
         countTest();
@@ -415,9 +431,9 @@
         } catch (WrongMethodTypeException ex) {
             return;
         } catch (Exception ex) {
-            throw new RuntimeException("wrong exception calling "+target+target.type()+" on "+Arrays.asList(args)+" : "+ex);
+            throw new RuntimeException("wrong exception calling "+target+" on "+Arrays.asList(args), ex);
         }
-        throw new RuntimeException("bad success calling "+target+target.type()+" on "+Arrays.asList(args));
+        throw new RuntimeException("bad success calling "+target+" on "+Arrays.asList(args));
     }
 
     /** Make a list of all combinations of the given types, with the given arities.
--- a/jdk/test/java/lang/invoke/MethodHandlesTest.java	Thu May 12 19:27:33 2011 -0700
+++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java	Thu May 12 19:27:49 2011 -0700
@@ -105,24 +105,6 @@
     public MethodHandlesTest() {
     }
 
-    @Before
-    public void checkImplementedPlatform() {
-        boolean platformOK = false;
-        Properties properties = System.getProperties();
-        String vers = properties.getProperty("java.vm.version");
-        String name = properties.getProperty("java.vm.name");
-        String arch = properties.getProperty("os.arch");
-        if ((arch.equals("amd64") || arch.equals("i386") || arch.equals("x86") ||
-             arch.equals("sparc") || arch.equals("sparcv9")) &&
-            (name.contains("Client") || name.contains("Server"))
-            ) {
-            platformOK = true;
-        } else {
-            System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch));
-        }
-        assumeTrue(platformOK);
-    }
-
     String testName;
     static int allPosTests, allNegTests;
     int posTests, negTests;
@@ -331,6 +313,9 @@
     static MethodHandle varargsArray(int arity) {
         return ValueConversions.varargsArray(arity);
     }
+    static MethodHandle varargsArray(Class<?> arrayType, int arity) {
+        return ValueConversions.varargsArray(arrayType, arity);
+    }
     /** Variation of varargsList, but with the given rtype. */
     static MethodHandle varargsList(int arity, Class<?> rtype) {
         MethodHandle list = varargsList(arity);
@@ -865,7 +850,7 @@
                     Class<?> type = (Class<?>) t[1];
                     Object value;
                     Field field;
-                    try {
+                        try {
                         field = HasFields.class.getDeclaredField(name);
                     } catch (Exception ex) {
                         throw new InternalError("no field HasFields."+name);
@@ -1144,16 +1129,9 @@
                 : MethodHandles.arrayElementSetter(arrayType);
         assertSame(mh.type(), expType);
         if (elemType != int.class && elemType != boolean.class) {
-            MethodType gtype;
-            if (true) { // FIXME: remove this path (and remove <void> below in the mh.invokes)
-                gtype = mh.type().changeParameterType(0, Object.class);
-                if (testSetter)
-                    gtype = gtype.changeParameterType(2, Object.class);
-                else
-                    gtype = gtype.changeReturnType(Object.class);
-            } else
-                // FIXME: This simpler path hits a bug in convertArguments => ToGeneric
-                gtype = mh.type().generic().changeParameterType(1, int.class);
+            // FIXME: change Integer.class and (Integer) below to int.class and (int) below.
+            MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class);
+            if (testSetter)  gtype = gtype.changeReturnType(void.class);
             mh = MethodHandles.convertArguments(mh, gtype);
         }
         Object sawValue, expValue;
@@ -1169,7 +1147,7 @@
                 else if (elemType == boolean.class)
                     mh.invokeExact((boolean[]) array, i, (boolean)random);
                 else
-                    mh.invokeExact(array, i, random);
+                    mh.invokeExact(array, (Integer)i, random);
                 assertEquals(model, array2list(array));
             } else {
                 Array.set(array, i, random);
@@ -1189,7 +1167,7 @@
                 else if (elemType == boolean.class)
                     sawValue = (boolean) mh.invokeExact((boolean[]) array, i);
                 else
-                    sawValue = mh.invokeExact(array, i);
+                    sawValue = mh.invokeExact(array, (Integer)i);
                 assertEquals(sawValue, expValue);
                 assertEquals(model, array2list(array));
             }
@@ -1341,21 +1319,15 @@
             int numcases = 1;
             for (int outargs = 0; outargs <= max; outargs++) {
                 if (outargs - inargs >= MAX_ARG_INCREASE)  continue;
-                int[] reorder = new int[outargs];
                 int casStep = dilution + 1;
                 // Avoid some common factors:
                 while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) ||
                        (casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0))
                     casStep++;
-                for (int cas = 0; cas < numcases; cas += casStep) {
-                    for (int i = 0, c = cas; i < outargs; i++) {
-                        reorder[i] = c % inargs;
-                        c /= inargs;
-                    }
-                    testPermuteArguments(args, types, reorder);
-                }
+                testPermuteArguments(args, types, outargs, numcases, casStep);
                 numcases *= inargs;
                 if (dilution > 10 && outargs >= 4) {
+                    int[] reorder = new int[outargs];
                     // Do some special patterns, which we probably missed.
                     // Replication of a single argument or argument pair.
                     for (int i = 0; i < inargs; i++) {
@@ -1383,6 +1355,19 @@
         }
     }
 
+    public void testPermuteArguments(Object[] args, Class<?>[] types,
+                                     int outargs, int numcases, int casStep) throws Throwable {
+        int inargs = args.length;
+        int[] reorder = new int[outargs];
+        for (int cas = 0; cas < numcases; cas += casStep) {
+            for (int i = 0, c = cas; i < outargs; i++) {
+                reorder[i] = c % inargs;
+                c /= inargs;
+            }
+            testPermuteArguments(args, types, reorder);
+        }
+    }
+
     static int[] reverse(int[] reorder) {
         reorder = reorder.clone();
         for (int i = 0, imax = reorder.length / 2; i < imax; i++) {
@@ -1433,6 +1418,12 @@
         MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
         Object result = newTarget.invokeWithArguments(args);
         Object expected = Arrays.asList(permArgs);
+        if (!expected.equals(result)) {
+            System.out.println("*** failed permuteArguments "+Arrays.toString(reorder)+" types="+Arrays.asList(types));
+            System.out.println("in args:   "+Arrays.asList(args));
+            System.out.println("out args:  "+expected);
+            System.out.println("bad args:  "+result);
+        }
         assertEquals(expected, result);
     }
 
@@ -1456,26 +1447,27 @@
     }
     public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
         countTest();
-        MethodHandle target = varargsArray(nargs);
-        MethodHandle target2 = changeArgTypes(target, argType);
+        Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
+        MethodHandle target2 = varargsArray(arrayType, nargs);
+        MethodHandle target = target2.asType(target2.type().generic());
         if (verbosity >= 3)
             System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
         Object[] args = randomArgs(target2.type().parameterArray());
         // make sure the target does what we think it does:
-        if (pos == 0 && nargs < 5) {
-            Object[] check = (Object[]) target.invokeWithArguments(args);
+        if (pos == 0 && nargs < 5 && !argType.isPrimitive()) {
+            Object[] check = (Object[]) (Object) target.invokeWithArguments(args);
             assertArrayEquals(args, check);
             switch (nargs) {
                 case 0:
-                    check = (Object[]) target.invokeExact();
+                    check = (Object[]) (Object) target.invokeExact();
                     assertArrayEquals(args, check);
                     break;
                 case 1:
-                    check = (Object[]) target.invokeExact(args[0]);
+                    check = (Object[]) (Object) target.invokeExact(args[0]);
                     assertArrayEquals(args, check);
                     break;
                 case 2:
-                    check = (Object[]) target.invokeExact(args[0], args[1]);
+                    check = (Object[]) (Object) target.invokeExact(args[0], args[1]);
                     assertArrayEquals(args, check);
                     break;
             }
@@ -1483,23 +1475,50 @@
         List<Class<?>> newParams = new ArrayList<Class<?>>(target2.type().parameterList());
         {   // modify newParams in place
             List<Class<?>> spreadParams = newParams.subList(pos, nargs);
-            spreadParams.clear(); spreadParams.add(Object[].class);
+            spreadParams.clear(); spreadParams.add(arrayType);
         }
-        MethodType newType = MethodType.methodType(Object.class, newParams);
-        MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType);
-        Object[] returnValue;
+        MethodType newType = MethodType.methodType(arrayType, newParams);
+        MethodHandle result = target2.asSpreader(arrayType, nargs-pos);
+        assert(result.type() == newType) : Arrays.asList(result, newType);
+        result = result.asType(newType.generic());
+        Object returnValue;
         if (pos == 0) {
-            // In the following line, the first cast implies
-            // normal Object return value for the MH call (Object[])->Object,
-            // while the second cast dynamically converts to an Object array.
-            // Such a double cast is typical of MH.invokeExact.
-            returnValue = (Object[]) (Object) result.invokeExact(args);
+            Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
+            returnValue = result.invokeExact(args2);
         } else {
             Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
-            args1[pos] = Arrays.copyOfRange(args, pos, args.length);
-            returnValue = (Object[]) result.invokeWithArguments(args1);
+            args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
+            returnValue = result.invokeWithArguments(args1);
         }
-        assertArrayEquals(args, returnValue);
+        String argstr = Arrays.toString(args);
+        if (!argType.isPrimitive()) {
+            Object[] rv = (Object[]) returnValue;
+            String rvs = Arrays.toString(rv);
+            if (!Arrays.equals(args, rv)) {
+                System.out.println("method:   "+result);
+                System.out.println("expected: "+argstr);
+                System.out.println("returned: "+rvs);
+                assertArrayEquals(args, rv);
+            }
+        } else if (argType == int.class) {
+            String rvs = Arrays.toString((int[]) returnValue);
+            if (!argstr.equals(rvs)) {
+                System.out.println("method:   "+result);
+                System.out.println("expected: "+argstr);
+                System.out.println("returned: "+rvs);
+                assertEquals(argstr, rvs);
+            }
+        } else if (argType == long.class) {
+            String rvs = Arrays.toString((long[]) returnValue);
+            if (!argstr.equals(rvs)) {
+                System.out.println("method:   "+result);
+                System.out.println("expected: "+argstr);
+                System.out.println("returned: "+rvs);
+                assertEquals(argstr, rvs);
+            }
+        } else {
+            // cannot test...
+        }
     }
 
     @Test
@@ -2130,15 +2149,12 @@
             Object z = surprise.invokeExact(x);
             System.out.println("Failed to throw; got z="+z);
             assertTrue(false);
-        } catch (Exception ex) {
+        } catch (ClassCastException ex) {
             if (verbosity > 2)
                 System.out.println("caught "+ex);
             if (verbosity > 3)
                 ex.printStackTrace();
-            assertTrue(ex instanceof ClassCastException
-                    // FIXME: accept only one of the two for any given unit test
-                    || ex instanceof WrongMethodTypeException
-                    );
+            assertTrue(true);  // all is well
         }
     }
 
@@ -2328,6 +2344,34 @@
         // else need to spin bytecode or do something else fancy
         throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs);
     }
+    public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
+        Class<?> elemType = arrayType.getComponentType();
+        MethodType vaType = MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType));
+        MethodHandle mh = varargsArray(nargs);
+        if (arrayType != Object[].class)
+            mh = MethodHandles.filterReturnValue(mh, CHANGE_ARRAY_TYPE.bindTo(arrayType));
+        return mh.asType(vaType);
+    }
+    static Object changeArrayType(Class<?> arrayType, Object[] a) {
+        Class<?> elemType = arrayType.getComponentType();
+        if (!elemType.isPrimitive())
+            return Arrays.copyOf(a, a.length, arrayType.asSubclass(Object[].class));
+        Object b = java.lang.reflect.Array.newInstance(elemType, a.length);
+        for (int i = 0; i < a.length; i++)
+            java.lang.reflect.Array.set(b, i, a[i]);
+        return b;
+    }
+    private static final MethodHandle CHANGE_ARRAY_TYPE;
+    static {
+        try {
+            CHANGE_ARRAY_TYPE = IMPL_LOOKUP.findStatic(ValueConversions.class, "changeArrayType",
+                                                       MethodType.methodType(Object.class, Class.class, Object[].class));
+        } catch (NoSuchMethodException | IllegalAccessException ex) {
+            Error err = new InternalError("uncaught exception");
+            err.initCause(ex);
+            throw err;
+        }
+    }
 
     private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
     private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/RicochetTest.java	Thu May 12 19:27:49 2011 -0700
@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) 2011, 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.
+ */
+
+/* @test
+ * @summary unit tests for recursive method handles
+ * @run junit/othervm -DRicochetTest.MAX_ARITY=255 test.java.lang.invoke.RicochetTest
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.invoke.*;
+import java.util.*;
+import org.junit.*;
+import static java.lang.invoke.MethodType.*;
+import static java.lang.invoke.MethodHandles.*;
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+
+/**
+ *
+ * @author jrose
+ */
+public class RicochetTest {
+    private static final Class CLASS = RicochetTest.class;
+    private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40);
+
+    public static void main(String... av) throws Throwable {
+        RicochetTest test = new RicochetTest();
+        if (av.length > 0)  test.testOnly = Arrays.asList(av).toString();
+        if (REPEAT == 1 || test.testOnly != null) {
+            test.testAll();
+            if (test.testOnlyTests == null)  throw new RuntimeException("no matching test: "+test.testOnly);
+        } else if (REPEAT == 0) {
+            org.junit.runner.JUnitCore.runClasses(RicochetTest.class);
+        } else {
+            verbose(1, "REPEAT="+REPEAT);
+            for (int i = 0; i < REPEAT; i++) {
+                test.testRepetition = (i+1);
+                verbose(0, "[#"+test.testRepetition+"]");
+                test.testAll();
+            }
+        }
+    }
+    int testRepetition;
+
+    public void testAll() throws Throwable {
+        testNull();
+        testBoxInteger();
+        testFilterReturnValue();
+        testFilterObject();
+        testBoxLong();
+        testFilterInteger();
+        testIntSpreads();
+        testByteSpreads();
+        testLongSpreads();
+        testIntCollects();
+        testReturns();
+    }
+
+    @Test
+    public void testNull() throws Throwable {
+        if (testRepetition > (1+REPEAT/100))  return;  // trivial test
+        if (!startTest("testNull"))  return;
+        assertEquals(opI(37), opI.invokeWithArguments(37));
+        assertEqualFunction(opI, opI);
+    }
+
+    @Test
+    public void testBoxInteger() throws Throwable {
+        if (!startTest("testBoxInteger"))  return;
+        assertEqualFunction(opI, opI.asType(opL_I.type()).asType(opI.type()));
+    }
+
+    @Test
+    public void testFilterReturnValue() throws Throwable {
+        if (!startTest("testFilterReturnValue"))  return;
+        int[] ints = { 12, 23, 34, 45, 56, 67, 78, 89 };
+        Object res = list8ints.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
+        assertEquals(Arrays.toString(ints), res.toString());
+        MethodHandle idreturn = filterReturnValue(list8ints, identity(Object.class));
+        res = idreturn.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
+        assertEquals(Arrays.toString(ints), res.toString());
+        MethodHandle add0 = addL.bindTo(0);
+        assertEqualFunction(filterReturnValue(opL2, add0), opL2);
+    }
+
+    @Test
+    public void testFilterObject() throws Throwable {
+        if (!startTest("testFilterObject"))  return;
+        MethodHandle add0 = addL.bindTo(0);
+        assertEqualFunction(sequence(opL2, add0), opL2);
+        int bump13 = -13;  // value near 20 works as long as test values are near [-80..80]
+        MethodHandle add13   = addL.bindTo(bump13);
+        MethodHandle add13_0 = addL.bindTo(opI2(bump13, 0));
+        MethodHandle add13_1 = addL.bindTo(opI2(0, bump13));
+        assertEqualFunction(sequence(opL2, add13_0),
+                            filterArguments(opL2, 0, add13));
+        assertEqualFunction(sequence(opL2, add13_1),
+                            filterArguments(opL2, 1, add13));
+        System.out.println("[testFilterObject done]");
+    }
+
+    @Test
+    public void testBoxLong() throws Throwable {
+        if (!startTest("testBoxLong"))  return;
+        assertEqualFunction(opJ, opJ.asType(opL_J.type()).asType(opJ.type()));
+    }
+
+    @Test
+    public void testFilterInteger() throws Throwable {
+        if (!startTest("testFilterInteger"))  return;
+        assertEqualFunction(opI, sequence(convI_L, opL_I));
+    }
+
+    @Test
+    public void testIntSpreads() throws Throwable {
+        if (!startTest("testIntSpreads"))  return;
+        MethodHandle id = identity(int[].class);
+        final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
+        for (int nargs = 0; nargs <= MAX; nargs++) {
+            if (nargs > 30 && nargs < MAX-20)  nargs += 10;
+            int[] args = new int[nargs];
+            for (int j = 0; j < args.length; j++)  args[j] = (int)(j + 11);
+            //System.out.println("testIntSpreads "+Arrays.toString(args));
+            int[] args1 = (int[]) id.invokeExact(args);
+            assertArrayEquals(args, args1);
+            MethodHandle coll = id.asCollector(int[].class, nargs);
+            int[] args2 = args;
+            switch (nargs) {
+                case 0:  args2 = (int[]) coll.invokeExact(); break;
+                case 1:  args2 = (int[]) coll.invokeExact(args[0]); break;
+                case 2:  args2 = (int[]) coll.invokeExact(args[0], args[1]); break;
+                case 3:  args2 = (int[]) coll.invokeExact(args[0], args[1], args[2]); break;
+                case 4:  args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
+                case 5:  args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
+            }
+            assertArrayEquals(args, args2);
+            MethodHandle mh = coll.asSpreader(int[].class, nargs);
+            int[] args3 = (int[]) mh.invokeExact(args);
+            assertArrayEquals(args, args3);
+        }
+    }
+
+    @Test
+    public void testByteSpreads() throws Throwable {
+        if (!startTest("testByteSpreads"))  return;
+        MethodHandle id = identity(byte[].class);
+        final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
+        for (int nargs = 0; nargs <= MAX; nargs++) {
+            if (nargs > 30 && nargs < MAX-20)  nargs += 10;
+            byte[] args = new byte[nargs];
+            for (int j = 0; j < args.length; j++)  args[j] = (byte)(j + 11);
+            //System.out.println("testByteSpreads "+Arrays.toString(args));
+            byte[] args1 = (byte[]) id.invokeExact(args);
+            assertArrayEquals(args, args1);
+            MethodHandle coll = id.asCollector(byte[].class, nargs);
+            byte[] args2 = args;
+            switch (nargs) {
+                case 0:  args2 = (byte[]) coll.invokeExact(); break;
+                case 1:  args2 = (byte[]) coll.invokeExact(args[0]); break;
+                case 2:  args2 = (byte[]) coll.invokeExact(args[0], args[1]); break;
+                case 3:  args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2]); break;
+                case 4:  args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
+                case 5:  args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
+            }
+            assertArrayEquals(args, args2);
+            MethodHandle mh = coll.asSpreader(byte[].class, nargs);
+            byte[] args3 = (byte[]) mh.invokeExact(args);
+            assertArrayEquals(args, args3);
+        }
+    }
+
+    @Test
+    public void testLongSpreads() throws Throwable {
+        if (!startTest("testLongSpreads"))  return;
+        MethodHandle id = identity(long[].class);
+        final int MAX = (MAX_ARITY - 2) / 2;  // 253/2+1 would cause parameter overflow with 'this' added
+        for (int nargs = 0; nargs <= MAX; nargs++) {
+            if (nargs > 30 && nargs < MAX-20)  nargs += 10;
+            long[] args = new long[nargs];
+            for (int j = 0; j < args.length; j++)  args[j] = (long)(j + 11);
+            //System.out.println("testLongSpreads "+Arrays.toString(args));
+            long[] args1 = (long[]) id.invokeExact(args);
+            assertArrayEquals(args, args1);
+            MethodHandle coll = id.asCollector(long[].class, nargs);
+            long[] args2 = args;
+            switch (nargs) {
+                case 0:  args2 = (long[]) coll.invokeExact(); break;
+                case 1:  args2 = (long[]) coll.invokeExact(args[0]); break;
+                case 2:  args2 = (long[]) coll.invokeExact(args[0], args[1]); break;
+                case 3:  args2 = (long[]) coll.invokeExact(args[0], args[1], args[2]); break;
+                case 4:  args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
+                case 5:  args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
+            }
+            assertArrayEquals(args, args2);
+            MethodHandle mh = coll.asSpreader(long[].class, nargs);
+            long[] args3 = (long[]) mh.invokeExact(args);
+            assertArrayEquals(args, args3);
+        }
+    }
+
+    @Test
+    public void testIntCollects() throws Throwable {
+        if (!startTest("testIntCollects"))  return;
+        for (MethodHandle lister : INT_LISTERS) {
+            int outputs = lister.type().parameterCount();
+            for (int collects = 0; collects <= Math.min(outputs, INT_COLLECTORS.length-1); collects++) {
+                int inputs = outputs - 1 + collects;
+                if (inputs < 0)  continue;
+                for (int pos = 0; pos + collects <= inputs; pos++) {
+                    MethodHandle collector = INT_COLLECTORS[collects];
+                    int[] args = new int[inputs];
+                    int ap = 0, arg = 31;
+                    for (int i = 0; i < pos; i++)
+                        args[ap++] = arg++ + 0;
+                    for (int i = 0; i < collects; i++)
+                        args[ap++] = arg++ + 10;
+                    while (ap < args.length)
+                        args[ap++] = arg++ + 20;
+                    // calculate piecemeal:
+                    //System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
+                    int[] collargs = Arrays.copyOfRange(args, pos, pos+collects);
+                    int coll = (int) collector.asSpreader(int[].class, collargs.length).invokeExact(collargs);
+                    int[] listargs = Arrays.copyOfRange(args, 0, outputs);
+                    System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1));
+                    listargs[pos] = coll;
+                    //System.out.println("  coll="+coll+" listargs="+Arrays.toString(listargs));
+                    Object expect = lister.asSpreader(int[].class, listargs.length).invokeExact(listargs);
+                    //System.out.println("  expect="+expect);
+
+                    // now use the combined MH, and test the output:
+                    MethodHandle mh = collectArguments(lister, pos, INT_COLLECTORS[collects]);
+                    if (mh == null)  continue;  // no infix collection, yet
+                    assert(mh.type().parameterCount() == inputs);
+                    Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args);
+                    assertEquals(expect, observe);
+                }
+            }
+        }
+    }
+
+    private static MethodHandle collectArguments(MethodHandle lister, int pos, MethodHandle collector) {
+        int collects = collector.type().parameterCount();
+        int outputs = lister.type().parameterCount();
+        if (pos == outputs - 1)
+            return MethodHandles.filterArguments(lister, pos,
+                        collector.asSpreader(int[].class, collects))
+                            .asCollector(int[].class, collects);
+        //return MethodHandles.collectArguments(lister, pos, collector); //no such animal
+        return null;
+    }
+
+    private static final Class<?>[] RETURN_TYPES = {
+        Object.class, String.class, Integer.class,
+        int.class, long.class,
+        boolean.class, byte.class, char.class, short.class,
+        float.class, double.class,
+        void.class,
+    };
+
+    @Test
+    public void testReturns() throws Throwable {
+        if (!startTest("testReturns"))  return;
+        // fault injection:
+        int faultCount = 0;  // total of 1296 tests
+        faultCount = Integer.getInteger("testReturns.faultCount", 0);
+        for (Class<?> ret : RETURN_TYPES) {
+            // make a complicated identity function and pass something through it
+            System.out.println(ret.getSimpleName());
+            Class<?> vret = (ret == void.class) ? Void.class : ret;
+            MethodHandle id = // (vret)->ret
+                identity(vret).asType(methodType(ret, vret));
+            final int LENGTH = 4;
+            int[] index = {0};
+            Object vals = java.lang.reflect.Array.newInstance(vret, LENGTH);
+            MethodHandle indexGetter =  //()->int
+                insertArguments(arrayElementGetter(index.getClass()), 0, index, 0);
+            MethodHandle valSelector =  // (int)->vret
+                arrayElementGetter(vals.getClass()).bindTo(vals);
+            MethodHandle valGetter =  // ()->vret
+                foldArguments(valSelector, indexGetter);
+            if (ret != void.class) {
+                for (int i = 0; i < LENGTH; i++) {
+                    Object val = (i + 50);
+                    if (ret == boolean.class)  val = (i % 3 == 0);
+                    if (ret == String.class)   val = "#"+i;
+                    if (ret == char.class)     val = (char)('a'+i);
+                    if (ret == byte.class)     val = (byte)~i;
+                    if (ret == short.class)    val = (short)(1<<i);
+                    java.lang.reflect.Array.set(vals, i, val);
+                }
+            }
+            for (int i = 0; i < LENGTH; i++) {
+                Object val = java.lang.reflect.Array.get(vals, i);
+                System.out.println(i+" => "+val);
+                index[0] = i;
+                if (--faultCount == 0)  index[0] ^= 1;
+                Object x = valGetter.invokeWithArguments();
+                assertEquals(val, x);
+                // make a return-filter call:  x = id(valGetter())
+                if (--faultCount == 0)  index[0] ^= 1;
+                x = filterReturnValue(valGetter, id).invokeWithArguments();
+                assertEquals(val, x);
+                // make a filter call:  x = id(*,valGetter(),*)
+                for (int len = 1; len <= 4; len++) {
+                    for (int pos = 0; pos < len; pos++) {
+                        MethodHandle proj = id;  // lambda(..., vret x,...){x}
+                        for (int j = 0; j < len; j++) {
+                            if (j == pos)  continue;
+                            proj = dropArguments(proj, j, Object.class);
+                        }
+                        assert(proj.type().parameterCount() == len);
+                        // proj: (Object*, pos: vret, Object*)->ret
+                        assertEquals(vret, proj.type().parameterType(pos));
+                        MethodHandle vgFilter = dropArguments(valGetter, 0, Object.class);
+                        if (--faultCount == 0)  index[0] ^= 1;
+                        x = filterArguments(proj, pos, vgFilter).invokeWithArguments(new Object[len]);
+                        assertEquals(val, x);
+                    }
+                }
+                // make a fold call:
+                for (int len = 0; len <= 4; len++) {
+                    for (int fold = 0; fold <= len; fold++) {
+                        MethodHandle proj = id;  // lambda(ret x, ...){x}
+                        if (ret == void.class)  proj = constant(Object.class, null);
+                        int arg0 = (ret == void.class ? 0 : 1);
+                        for (int j = 0; j < len; j++) {
+                            proj = dropArguments(proj, arg0, Object.class);
+                        }
+                        assert(proj.type().parameterCount() == arg0 + len);
+                        // proj: (Object*, pos: vret, Object*)->ret
+                        if (arg0 != 0)  assertEquals(vret, proj.type().parameterType(0));
+                        MethodHandle vgFilter = valGetter.asType(methodType(ret));
+                        for (int j = 0; j < fold; j++) {
+                            vgFilter = dropArguments(vgFilter, j, Object.class);
+                        }
+                        x = foldArguments(proj, vgFilter).invokeWithArguments(new Object[len]);
+                        if (--faultCount == 0)  index[0] ^= 1;
+                        assertEquals(val, x);
+                    }
+                }
+            }
+        }
+        //System.out.println("faultCount="+faultCount);
+    }
+
+    private static MethodHandle sequence(MethodHandle mh1, MethodHandle... mhs) {
+        MethodHandle res = mh1;
+        for (MethodHandle mh2 : mhs)
+            res = filterReturnValue(res, mh2);
+        return res;
+    }
+    private static void assertEqualFunction(MethodHandle x, MethodHandle y) throws Throwable {
+        assertEquals(x.type(), y.type()); //??
+        MethodType t = x.type();
+        if (t.parameterCount() == 0) {
+            assertEqualFunctionAt(null, x, y);
+            return;
+        }
+        Class<?> ptype = t.parameterType(0);
+        if (ptype == long.class || ptype == Long.class) {
+            for (long i = -10; i <= 10; i++) {
+                assertEqualFunctionAt(i, x, y);
+            }
+        } else {
+            for (int i = -10; i <= 10; i++) {
+                assertEqualFunctionAt(i, x, y);
+            }
+        }
+    }
+    private static void assertEqualFunctionAt(Object v, MethodHandle x, MethodHandle y) throws Throwable {
+        Object[] args = new Object[x.type().parameterCount()];
+        Arrays.fill(args, v);
+        Object xval = invokeWithCatch(x, args);
+        Object yval = invokeWithCatch(y, args);
+        String msg = "ok";
+        if (!Objects.equals(xval, yval)) {
+            msg = ("applying "+x+" & "+y+" to "+v);
+        }
+        assertEquals(msg, xval, yval);
+    }
+    private static Object invokeWithCatch(MethodHandle mh, Object... args) throws Throwable {
+        try {
+            return mh.invokeWithArguments(args);
+        } catch (Throwable ex) {
+            System.out.println("threw: "+mh+Arrays.asList(args));
+            ex.printStackTrace();
+            return ex;
+        }
+    }
+
+    private static final Lookup LOOKUP = lookup();
+    private static MethodHandle findStatic(String name,
+                                           Class<?> rtype,
+                                           Class<?>... ptypes) {
+        try {
+            return LOOKUP.findStatic(LOOKUP.lookupClass(), name, methodType(rtype, ptypes));
+        } catch (ReflectiveOperationException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+    private static MethodHandle findStatic(String name,
+                                           Class<?> rtype,
+                                           List<?> ptypes) {
+        return findStatic(name, rtype, ptypes.toArray(new Class<?>[ptypes.size()]));
+    }
+    static int getProperty(String name, int dflt) {
+        String qual = LOOKUP.lookupClass().getName();
+        String prop = System.getProperty(qual+"."+name);
+        if (prop == null)  prop = System.getProperty(name);
+        if (prop == null)  return dflt;
+        return Integer.parseInt(prop);
+    }
+
+    private static int opI(int... xs) {
+        stress();
+        int base = 100;
+        int z = 0;
+        for (int x : xs) {
+            z = (z * base) + (x % base);
+        }
+        verbose("opI", xs.length, xs, z);
+        return z;
+    }
+    private static int opI2(int x, int y) { return opI(x, y); }  // x*100 + y%100
+    private static int opI3(int x, int y, int z) { return opI(x, y, z); }
+    private static int opI4(int w, int x, int y, int z) { return opI(w, x, y, z); }
+    private static int opI(int x) { return opI2(x, 37); }
+    private static Object opI_L(int x) { return (Object) opI(x); }
+    private static long opJ3(long x, long y, long z) { return (long) opI3((int)x, (int)y, (int)z); }
+    private static long opJ2(long x, long y) { return (long) opI2((int)x, (int)y); }
+    private static long opJ(long x) { return (long) opI((int)x); }
+    private static Object opL2(Object x, Object y) { return (Object) opI2((int)x, (int)y); }
+    private static Object opL(Object x) { return (Object) opI((int)x); }
+    private static int opL2_I(Object x, Object y) { return (int) opI2((int)x, (int)y); }
+    private static int opL_I(Object x) { return (int) opI((int)x); }
+    private static long opL_J(Object x) { return (long) opI((int)x); }
+    private static final MethodHandle opI, opI2, opI3, opI4, opI_L, opJ, opJ2, opJ3, opL2, opL, opL2_I, opL_I, opL_J;
+    static {
+        opI4 = findStatic("opI4", int.class, int.class, int.class, int.class, int.class);
+        opI3 = findStatic("opI3", int.class, int.class, int.class, int.class);
+        opI2 = findStatic("opI2", int.class, int.class, int.class);
+        opI = findStatic("opI", int.class, int.class);
+        opI_L = findStatic("opI_L", Object.class, int.class);
+        opJ = findStatic("opJ", long.class, long.class);
+        opJ2 = findStatic("opJ2", long.class, long.class, long.class);
+        opJ3 = findStatic("opJ3", long.class, long.class, long.class, long.class);
+        opL2 = findStatic("opL2", Object.class, Object.class, Object.class);
+        opL = findStatic("opL", Object.class, Object.class);
+        opL2_I = findStatic("opL2_I", int.class, Object.class, Object.class);
+        opL_I = findStatic("opL_I", int.class, Object.class);
+        opL_J = findStatic("opL_J", long.class, Object.class);
+    }
+    private static final MethodHandle[] INT_COLLECTORS = {
+        constant(int.class, 42), opI, opI2, opI3, opI4
+    };
+    private static final MethodHandle[] LONG_COLLECTORS = {
+        constant(long.class, 42), opJ, opJ2, opJ3
+    };
+
+    private static int addI(int x, int y) { stress(); return x+y; }
+    private static Object addL(Object x, Object y) { return addI((int)x, (int)y); }
+    private static final MethodHandle addI, addL;
+    static {
+        addI = findStatic("addI", int.class, int.class, int.class);
+        addL = findStatic("addL", Object.class, Object.class, Object.class);
+    }
+
+    private static Object list8ints(int a, int b, int c, int d, int e, int f, int g, int h) {
+        return Arrays.asList(a, b, c, d, e, f, g, h);
+    }
+    private static Object list8longs(long a, long b, long c, long d, long e, long f, long g, long h) {
+        return Arrays.asList(a, b, c, d, e, f, g, h);
+    }
+    private static final MethodHandle list8ints = findStatic("list8ints", Object.class,
+                                                             Collections.nCopies(8, int.class));
+    private static final MethodHandle list8longs = findStatic("list8longs", Object.class,
+                                                              Collections.nCopies(8, long.class));
+    private static final MethodHandle[] INT_LISTERS, LONG_LISTERS;
+    static {
+        int listerCount = list8ints.type().parameterCount() + 1;
+        INT_LISTERS  = new MethodHandle[listerCount];
+        LONG_LISTERS = new MethodHandle[listerCount];
+        MethodHandle lister = list8ints;
+        MethodHandle llister = list8longs;
+        for (int i = listerCount - 1; ; i--) {
+            INT_LISTERS[i] = lister;
+            LONG_LISTERS[i] = llister;
+            if (i == 0)  break;
+            lister  = insertArguments(lister,  i-1, (int)0);
+            llister = insertArguments(llister, i-1, (long)0);
+        }
+    }
+
+    private static Object  convI_L(int     x) { stress(); return (Object)  x; }
+    private static int     convL_I(Object  x) { stress(); return (int)     x; }
+    private static Object  convJ_L(long    x) { stress(); return (Object)  x; }
+    private static long    convL_J(Object  x) { stress(); return (long)    x; }
+    private static int     convJ_I(long    x) { stress(); return (int)     x; }
+    private static long    convI_J(int     x) { stress(); return (long)    x; }
+    private static final MethodHandle convI_L, convL_I, convJ_L, convL_J, convJ_I, convI_J;
+    static {
+        convI_L = findStatic("convI_L", Object.class, int.class);
+        convL_I = findStatic("convL_I", int.class, Object.class);
+        convJ_L = findStatic("convJ_L", Object.class, long.class);
+        convL_J = findStatic("convL_J", long.class, Object.class);
+        convJ_I = findStatic("convJ_I", int.class, long.class);
+        convI_J = findStatic("convI_J", long.class, int.class);
+    }
+
+    // stress modes:
+    private static final int REPEAT = getProperty("REPEAT", 0);
+    private static final int STRESS = getProperty("STRESS", 0);
+    private static /*v*/ int STRESS_COUNT;
+    private static final Object[] SINK = new Object[4];
+    private static void stress() {
+        if (STRESS <= 0) return;
+        int count = STRESS + (STRESS_COUNT++ & 0x1);  // non-constant value
+        for (int i = 0; i < count; i++) {
+            SINK[i % SINK.length] = new Object[STRESS + i % (SINK.length + 1)];
+        }
+    }
+
+    // verbosity:
+    private static final int VERBOSITY = getProperty("VERBOSITY", 0) + (REPEAT == 0 ? 0 : -1);
+    private static void verbose(Object a, Object b, Object c, Object d) {
+        if (VERBOSITY <= 0)  return;
+        verbose(1, a, b, c, d);
+    }
+    private static void verbose(Object a, Object b, Object c) {
+        if (VERBOSITY <= 0)  return;
+        verbose(1, a, b, c);
+    }
+    private static void verbose(int level, Object a, Object... bcd) {
+        if (level > VERBOSITY)  return;
+        String m = a.toString();
+        if (bcd != null && bcd.length > 0) {
+            List<Object> l = new ArrayList<>(bcd.length);
+            for (Object x : bcd) {
+                if (x instanceof Object[])  x = Arrays.asList((Object[])x);
+                if (x instanceof int[])     x = Arrays.toString((int[])x);
+                if (x instanceof long[])    x = Arrays.toString((long[])x);
+                l.add(x);
+            }
+            m = m+Arrays.asList(bcd);
+        }
+        System.out.println(m);
+    }
+    String testOnly;
+    String testOnlyTests;
+    private boolean startTest(String name) {
+        if (testOnly != null && !testOnly.contains(name))
+            return false;
+        verbose(0, "["+name+"]");
+        testOnlyTests = (testOnlyTests == null) ? name : testOnlyTests+" "+name;
+        return true;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/invoke/util/ValueConversionsTest.java	Thu May 12 19:27:49 2011 -0700
@@ -0,0 +1,448 @@
+/*
+ * Copyright (c) 2009, 2011, 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.
+ *
+ * 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 test.sun.invoke.util;
+
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.Wrapper;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/* @test
+ * @summary unit tests for value-type conversion utilities
+ * @ignore This test requires a special compilation environment to access sun.inovke.util.  Run by hand.
+ * @run junit/othervm test.sun.invoke.util.ValueConversionsTest
+ * @run junit/othervm
+ *          -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.START_ARITY=250
+ *              test.sun.invoke.util.ValueConversionsTest
+ */
+
+// This might take a while and burn lots of metadata:
+// @run junit/othervm -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.EXHAUSTIVE=true test.sun.invoke.util.ValueConversionsTest
+
+/**
+ *
+ * @author jrose
+ */
+public class ValueConversionsTest {
+    private static final Class CLASS = ValueConversionsTest.class;
+    private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40);
+    private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0);
+    private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE");
+
+    @Test
+    public void testUnbox() throws Throwable {
+        testUnbox(false);
+    }
+
+    @Test
+    public void testUnboxCast() throws Throwable {
+        testUnbox(true);
+    }
+
+    private void testUnbox(boolean doCast) throws Throwable {
+        //System.out.println("unbox");
+        for (Wrapper dst : Wrapper.values()) {
+            //System.out.println(dst);
+            for (Wrapper src : Wrapper.values()) {
+                testUnbox(doCast, dst, src);
+            }
+        }
+    }
+
+    private void testUnbox(boolean doCast, Wrapper dst, Wrapper src) throws Throwable {
+        boolean expectThrow = !doCast && !dst.isConvertibleFrom(src);
+        if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT)  return;  // must have prims
+        if (dst == Wrapper.OBJECT)
+            expectThrow = false;  // everything (even VOID==null here) converts to OBJECT
+        try {
+            for (int n = -5; n < 10; n++) {
+                Object box = src.wrap(n);
+                switch (src) {
+                    case VOID:   assertEquals(box, null); break;
+                    case OBJECT: box = box.toString(); break;
+                    case SHORT:  assertEquals(box.getClass(), Short.class); break;
+                    default:     assertEquals(box.getClass(), src.wrapperType()); break;
+                }
+                MethodHandle unboxer;
+                if (doCast)
+                    unboxer = ValueConversions.unboxCast(dst.primitiveType());
+                else
+                    unboxer = ValueConversions.unbox(dst.primitiveType());
+                Object expResult = (box == null) ? dst.zero() : dst.wrap(box);
+                Object result = null;
+                switch (dst) {
+                    case INT:     result = (int)     unboxer.invokeExact(box); break;
+                    case LONG:    result = (long)    unboxer.invokeExact(box); break;
+                    case FLOAT:   result = (float)   unboxer.invokeExact(box); break;
+                    case DOUBLE:  result = (double)  unboxer.invokeExact(box); break;
+                    case CHAR:    result = (char)    unboxer.invokeExact(box); break;
+                    case BYTE:    result = (byte)    unboxer.invokeExact(box); break;
+                    case SHORT:   result = (short)   unboxer.invokeExact(box); break;
+                    case OBJECT:  result = (Object)  unboxer.invokeExact(box); break;
+                    case BOOLEAN: result = (boolean) unboxer.invokeExact(box); break;
+                    case VOID:    result = null;     unboxer.invokeExact(box); break;
+                }
+                if (expectThrow) {
+                    expResult = "(need an exception)";
+                }
+                assertEquals("(doCast,expectThrow,dst,src,n,box)="+Arrays.asList(doCast,expectThrow,dst,src,n,box),
+                             expResult, result);
+            }
+        } catch (RuntimeException ex) {
+            if (expectThrow)  return;
+            System.out.println("Unexpected throw for (doCast,expectThrow,dst,src)="+Arrays.asList(doCast,expectThrow,dst,src));
+            throw ex;
+        }
+    }
+
+    @Test
+    public void testUnboxRaw() throws Throwable {
+        //System.out.println("unboxRaw");
+        for (Wrapper w : Wrapper.values()) {
+            if (w == Wrapper.OBJECT)  continue;  // skip this; no raw form
+            //System.out.println(w);
+            for (int n = -5; n < 10; n++) {
+                Object box = w.wrap(n);
+                long expResult = w.unwrapRaw(box);
+                Object box2 = w.wrapRaw(expResult);
+                assertEquals(box, box2);
+                MethodHandle unboxer = ValueConversions.unboxRaw(w.primitiveType());
+                long result = -1;
+                switch (w) {
+                    case INT:     result = (int)  unboxer.invokeExact(box); break;
+                    case LONG:    result = (long) unboxer.invokeExact(box); break;
+                    case FLOAT:   result = (int)  unboxer.invokeExact(box); break;
+                    case DOUBLE:  result = (long) unboxer.invokeExact(box); break;
+                    case CHAR:    result = (int)  unboxer.invokeExact(box); break;
+                    case BYTE:    result = (int)  unboxer.invokeExact(box); break;
+                    case SHORT:   result = (int)  unboxer.invokeExact(box); break;
+                    case BOOLEAN: result = (int)  unboxer.invokeExact(box); break;
+                    case VOID:    result = (int)  unboxer.invokeExact(box); break;
+                }
+                assertEquals("(w,n,box)="+Arrays.asList(w,n,box),
+                             expResult, result);
+            }
+        }
+    }
+
+    @Test
+    public void testBox() throws Throwable {
+        //System.out.println("box");
+        for (Wrapper w : Wrapper.values()) {
+            if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
+            //System.out.println(w);
+            for (int n = -5; n < 10; n++) {
+                Object box = w.wrap(n);
+                MethodHandle boxer = ValueConversions.box(w.primitiveType());
+                Object expResult = box;
+                Object result = null;
+                switch (w) {
+                    case INT:     result = boxer.invokeExact((int)n); break;
+                    case LONG:    result = boxer.invokeExact((long)n); break;
+                    case FLOAT:   result = boxer.invokeExact((float)n); break;
+                    case DOUBLE:  result = boxer.invokeExact((double)n); break;
+                    case CHAR:    result = boxer.invokeExact((char)n); break;
+                    case BYTE:    result = boxer.invokeExact((byte)n); break;
+                    case SHORT:   result = boxer.invokeExact((short)n); break;
+                    case OBJECT:  result = boxer.invokeExact((Object)n); break;
+                    case BOOLEAN: result = boxer.invokeExact((n & 1) != 0); break;
+                }
+                assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
+                             expResult, result);
+            }
+        }
+    }
+
+    @Test
+    public void testBoxRaw() throws Throwable {
+        //System.out.println("boxRaw");
+        for (Wrapper w : Wrapper.values()) {
+            if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
+            if (w == Wrapper.OBJECT)  continue;  // skip this; no raw form
+            //System.out.println(w);
+            for (int n = -5; n < 10; n++) {
+                Object box = w.wrap(n);
+                long   raw = w.unwrapRaw(box);
+                Object expResult = box;
+                MethodHandle boxer = ValueConversions.boxRaw(w.primitiveType());
+                Object result = null;
+                switch (w) {
+                case INT:     result = boxer.invokeExact((int)raw); break;
+                case LONG:    result = boxer.invokeExact(raw); break;
+                case FLOAT:   result = boxer.invokeExact((int)raw); break;
+                case DOUBLE:  result = boxer.invokeExact(raw); break;
+                case CHAR:    result = boxer.invokeExact((int)raw); break;
+                case BYTE:    result = boxer.invokeExact((int)raw); break;
+                case SHORT:   result = boxer.invokeExact((int)raw); break;
+                case BOOLEAN: result = boxer.invokeExact((int)raw); break;
+                }
+                assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
+                             expResult, result);
+            }
+        }
+    }
+
+    @Test
+    public void testReboxRaw() throws Throwable {
+        //System.out.println("reboxRaw");
+        for (Wrapper w : Wrapper.values()) {
+            Wrapper pw = Wrapper.forPrimitiveType(w.rawPrimitiveType());
+            if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
+            if (w == Wrapper.OBJECT)  continue;  // skip this; no raw form
+            //System.out.println(w);
+            for (int n = -5; n < 10; n++) {
+                Object box = w.wrap(n);
+                Object raw = pw.wrap(w.unwrapRaw(box));
+                Object expResult = box;
+                MethodHandle boxer = ValueConversions.rebox(w.primitiveType());
+                Object result = null;
+                switch (w) {
+                case INT:     result = boxer.invokeExact(raw); break;
+                case LONG:    result = boxer.invokeExact(raw); break;
+                case FLOAT:   result = boxer.invokeExact(raw); break;
+                case DOUBLE:  result = boxer.invokeExact(raw); break;
+                case CHAR:    result = boxer.invokeExact(raw); break;
+                case BYTE:    result = boxer.invokeExact(raw); break;
+                case SHORT:   result = boxer.invokeExact(raw); break;
+                case BOOLEAN: result = boxer.invokeExact(raw); break;
+                }
+                assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
+                             expResult, result);
+            }
+        }
+    }
+
+    @Test
+    public void testCast() throws Throwable {
+        //System.out.println("cast");
+        Class<?>[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class };
+        Object[] objects = { new Object(), Boolean.FALSE,      "hello",      (Long)12L,    (Integer)6    };
+        for (Class<?> dst : types) {
+            MethodHandle caster = ValueConversions.cast(dst);
+            assertEquals(caster.type(), ValueConversions.identity().type());
+            for (Object obj : objects) {
+                Class<?> src = obj.getClass();
+                boolean canCast;
+                if (dst.isInterface()) {
+                    canCast = true;
+                } else {
+                    canCast = dst.isAssignableFrom(src);
+                    assertEquals(canCast, dst.isInstance(obj));
+                }
+                //System.out.println("obj="+obj+" <: dst="+dst);
+                try {
+                    Object result = caster.invokeExact(obj);
+                    if (canCast)
+                        assertEquals(obj, result);
+                    else
+                        assertEquals("cast should not have succeeded", dst, obj);
+                } catch (ClassCastException ex) {
+                    if (canCast)
+                        throw ex;
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testIdentity() throws Throwable {
+        //System.out.println("identity");
+        MethodHandle id = ValueConversions.identity();
+        Object expResult = "foo";
+        Object result = id.invokeExact(expResult);
+        // compiler bug:  ValueConversions.identity().invokeExact("bar");
+        assertEquals(expResult, result);
+    }
+
+    @Test
+    public void testVarargsArray() throws Throwable {
+        //System.out.println("varargsArray");
+        final int MIN = START_ARITY;
+        final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
+        for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) {
+            MethodHandle target = ValueConversions.varargsArray(nargs);
+            Object[] args = new Object[nargs];
+            for (int i = 0; i < nargs; i++)
+                args[i] = "#"+i;
+            Object res = target.invokeWithArguments(args);
+            assertArrayEquals(args, (Object[])res);
+        }
+    }
+
+    @Test
+    public void testVarargsReferenceArray() throws Throwable {
+        //System.out.println("varargsReferenceArray");
+        testTypedVarargsArray(Object[].class);
+        testTypedVarargsArray(String[].class);
+        testTypedVarargsArray(Number[].class);
+    }
+
+    @Test
+    public void testVarargsPrimitiveArray() throws Throwable {
+        //System.out.println("varargsPrimitiveArray");
+        testTypedVarargsArray(int[].class);
+        testTypedVarargsArray(long[].class);
+        testTypedVarargsArray(byte[].class);
+        testTypedVarargsArray(boolean[].class);
+        testTypedVarargsArray(short[].class);
+        testTypedVarargsArray(char[].class);
+        testTypedVarargsArray(float[].class);
+        testTypedVarargsArray(double[].class);
+    }
+
+    private static int nextArgCount(int nargs, int density, int MAX) {
+        if (EXHAUSTIVE)  return nargs + 1;
+        if (nargs >= MAX)  return Integer.MAX_VALUE;
+        int BOT = 20, TOP = MAX-5;
+        if (density < 10) { BOT = 10; MAX = TOP-2; }
+        if (nargs <= BOT || nargs >= TOP) {
+            ++nargs;
+        } else {
+            int bump = Math.max(1, 100 / density);
+            nargs += bump;
+            if (nargs > TOP)  nargs = TOP;
+        }
+        return nargs;
+    }
+
+    private void testTypedVarargsArray(Class<?> arrayType) throws Throwable {
+        System.out.println(arrayType.getSimpleName());
+        Class<?> elemType = arrayType.getComponentType();
+        int MIN = START_ARITY;
+        int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
+        int density = 3;
+        if (elemType == int.class || elemType == long.class)  density = 7;
+        if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; }
+        for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) {
+            Object[] args = makeTestArray(elemType, nargs);
+            MethodHandle varargsArray = ValueConversions.varargsArray(arrayType, nargs);
+            MethodType vaType = varargsArray.type();
+            assertEquals(arrayType, vaType.returnType());
+            if (nargs != 0) {
+                assertEquals(elemType, vaType.parameterType(0));
+                assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1));
+            }
+            assertEquals(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)),
+                         vaType);
+            Object res = varargsArray.invokeWithArguments(args);
+            String resString = toArrayString(res);
+            assertEquals(Arrays.toString(args), resString);
+
+            MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs);
+            MethodType stype = spreader.type();
+            assert(stype == MethodType.methodType(arrayType, arrayType));
+            if (nargs <= 5) {
+                // invoke target as a spreader also:
+                Object res2 = spreader.invokeWithArguments((Object)res);
+                String res2String = toArrayString(res2);
+                assertEquals(Arrays.toString(args), res2String);
+                // invoke the spreader on a generic Object[] array; check for error
+                try {
+                    Object res3 = spreader.invokeWithArguments((Object)args);
+                    String res3String = toArrayString(res3);
+                    assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class));
+                    assertEquals(Arrays.toString(args), res3String);
+                } catch (ClassCastException ex) {
+                    assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class));
+                }
+            }
+            if (nargs == 0) {
+                // invoke spreader on null arglist
+                Object res3 = spreader.invokeWithArguments((Object)null);
+                String res3String = toArrayString(res3);
+                assertEquals(Arrays.toString(args), res3String);
+            }
+        }
+    }
+
+    private static Object[] makeTestArray(Class<?> elemType, int len) {
+        Wrapper elem = null;
+        if (elemType.isPrimitive())
+            elem = Wrapper.forPrimitiveType(elemType);
+        else if (Wrapper.isWrapperType(elemType))
+            elem = Wrapper.forWrapperType(elemType);
+        Object[] args = new Object[len];
+        for (int i = 0; i < len; i++) {
+            Object arg = i * 100;
+            if (elem == null) {
+                if (elemType == String.class)
+                    arg = "#"+arg;
+                arg = elemType.cast(arg);  // just to make sure
+            } else {
+                switch (elem) {
+                    case BOOLEAN: arg = (i % 3 == 0);           break;
+                    case CHAR:    arg = 'a' + i;                break;
+                    case LONG:    arg = (long)i * 1000_000_000; break;
+                    case FLOAT:   arg = (float)i / 100;         break;
+                    case DOUBLE:  arg = (double)i / 1000_000;   break;
+                }
+                arg = elem.cast(arg, elemType);
+            }
+            args[i] = arg;
+        }
+        //System.out.println(elemType.getName()+Arrays.toString(args));
+        return args;
+    }
+
+    private static String toArrayString(Object a) {
+        if (a == null)  return "null";
+        Class<?> elemType = a.getClass().getComponentType();
+        if (elemType == null)  return a.toString();
+        if (elemType.isPrimitive()) {
+            switch (Wrapper.forPrimitiveType(elemType)) {
+                case INT:      return Arrays.toString((int[])a);
+                case BYTE:     return Arrays.toString((byte[])a);
+                case BOOLEAN:  return Arrays.toString((boolean[])a);
+                case SHORT:    return Arrays.toString((short[])a);
+                case CHAR:     return Arrays.toString((char[])a);
+                case FLOAT:    return Arrays.toString((float[])a);
+                case LONG:     return Arrays.toString((long[])a);
+                case DOUBLE:   return Arrays.toString((double[])a);
+            }
+        }
+        return Arrays.toString((Object[])a);
+    }
+
+    @Test
+    public void testVarargsList() throws Throwable {
+        //System.out.println("varargsList");
+        final int MIN = START_ARITY;
+        final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
+        for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 7, MAX)) {
+            MethodHandle target = ValueConversions.varargsList(nargs);
+            Object[] args = new Object[nargs];
+            for (int i = 0; i < nargs; i++)
+                args[i] = "#"+i;
+            Object res = target.invokeWithArguments(args);
+            assertEquals(Arrays.asList(args), res);
+        }
+    }
+}