changeset 9987:d33c58176d0c

8050877: Improve code for pairwise argument conversions and value boxing/unboxing Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com
author vlivanov
date Wed, 10 Sep 2014 18:34:03 +0400
parents 4e6337ca7989
children db52173c10e4
files src/share/classes/java/lang/invoke/MethodHandle.java src/share/classes/java/lang/invoke/MethodHandleImpl.java src/share/classes/java/lang/invoke/MethodHandles.java src/share/classes/sun/invoke/util/ValueConversions.java test/sun/invoke/util/ValueConversionsTest.java
diffstat 5 files changed, 280 insertions(+), 244 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 18:34:03 2014 +0400
@@ -771,7 +771,7 @@
     /*non-public*/ MethodHandle asTypeUncached(MethodType newType) {
         if (!type.isConvertibleTo(newType))
             throw new WrongMethodTypeException("cannot convert "+this+" to "+newType);
-        return asTypeCache = MethodHandleImpl.makePairwiseConvert(this, newType, 1);
+        return asTypeCache = MethodHandleImpl.makePairwiseConvert(this, newType, true);
     }
 
     /**
@@ -984,7 +984,7 @@
         int collectArgPos = type().parameterCount()-1;
         MethodHandle target = this;
         if (arrayType != type().parameterType(collectArgPos))
-            target = MethodHandleImpl.makePairwiseConvert(this, type().changeParameterType(collectArgPos, arrayType), 1);
+            target = MethodHandleImpl.makePairwiseConvert(this, type().changeParameterType(collectArgPos, arrayType), true);
         MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength);
         return MethodHandles.collectArguments(target, collectArgPos, collector);
     }
--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 18:34:03 2014 +0400
@@ -179,45 +179,49 @@
      * integral widening or narrowing, and floating point widening or narrowing.
      * @param srcType required call type
      * @param target original method handle
-     * @param level which strength of conversion is allowed
+     * @param strict if true, only asType conversions are allowed; if false, explicitCastArguments conversions allowed
+     * @param monobox if true, unboxing conversions are assumed to be exactly typed (Integer to int only, not long or double)
      * @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(MethodHandle target, MethodType srcType, int level) {
-        assert(level >= 0 && level <= 2);
+    static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType,
+                                            boolean strict, boolean monobox) {
         MethodType dstType = target.type();
         assert(dstType.parameterCount() == target.type().parameterCount());
         if (srcType == dstType)
             return target;
+        return makePairwiseConvertIndirect(target, srcType, strict, monobox);
+    }
 
+    private static int countNonNull(Object[] array) {
+        int count = 0;
+        for (Object x : array) {
+            if (x != null)  ++count;
+        }
+        return count;
+    }
+
+    static MethodHandle makePairwiseConvertIndirect(MethodHandle target, MethodType srcType,
+                                                    boolean strict, boolean monobox) {
         // Calculate extra arguments (temporaries) required in the names array.
-        // FIXME: Use an ArrayList<Name>.  Some arguments require more than one conversion step.
+        Object[] convSpecs = computeValueConversions(srcType, target.type(), strict, monobox);
         final int INARG_COUNT = srcType.parameterCount();
-        int conversions = 0;
-        boolean[] needConv = new boolean[1+INARG_COUNT];
-        for (int i = 0; i <= INARG_COUNT; i++) {
-            Class<?> src = (i == INARG_COUNT) ? dstType.returnType() : srcType.parameterType(i);
-            Class<?> dst = (i == INARG_COUNT) ? srcType.returnType() : dstType.parameterType(i);
-            if (!VerifyType.isNullConversion(src, dst, false) ||
-                level <= 1 && dst.isInterface() && !dst.isAssignableFrom(src)) {
-                needConv[i] = true;
-                conversions++;
-            }
-        }
-        boolean retConv = needConv[INARG_COUNT];
-        if (retConv && srcType.returnType() == void.class) {
+        int convCount = countNonNull(convSpecs);
+        boolean retConv = (convSpecs[INARG_COUNT] != null);
+        boolean retVoid = srcType.returnType() == void.class;
+        if (retConv && retVoid) {
+            convCount -= 1;
             retConv = false;
-            conversions--;
         }
 
         final int IN_MH         = 0;
         final int INARG_BASE    = 1;
         final int INARG_LIMIT   = INARG_BASE + INARG_COUNT;
-        final int NAME_LIMIT    = INARG_LIMIT + conversions + 1;
+        final int NAME_LIMIT    = INARG_LIMIT + convCount + 1;
         final int RETURN_CONV   = (!retConv ? -1         : NAME_LIMIT - 1);
         final int OUT_CALL      = (!retConv ? NAME_LIMIT : RETURN_CONV) - 1;
-        final int RESULT        = (srcType.returnType() == void.class ? -1 : NAME_LIMIT - 1);
+        final int RESULT        = (retVoid ? -1 : NAME_LIMIT - 1);
 
         // Now build a LambdaForm.
         MethodType lambdaType = srcType.basicType().invokerType();
@@ -229,59 +233,21 @@
 
         int nameCursor = INARG_LIMIT;
         for (int i = 0; i < INARG_COUNT; i++) {
-            Class<?> src = srcType.parameterType(i);
-            Class<?> dst = dstType.parameterType(i);
-
-            if (!needConv[i]) {
+            Object convSpec = convSpecs[i];
+            if (convSpec == null) {
                 // do nothing: difference is trivial
                 outArgs[OUTARG_BASE + i] = names[INARG_BASE + i];
                 continue;
             }
 
-            // Tricky case analysis follows.
-            MethodHandle fn = null;
-            if (src.isPrimitive()) {
-                if (dst.isPrimitive()) {
-                    fn = ValueConversions.convertPrimitive(src, dst);
-                } else {
-                    Wrapper w = Wrapper.forPrimitiveType(src);
-                    MethodHandle boxMethod = ValueConversions.box(w);
-                    if (dst == w.wrapperType())
-                        fn = boxMethod;
-                    else
-                        fn = boxMethod.asType(MethodType.methodType(dst, src));
-                }
+            Name conv;
+            if (convSpec instanceof Class) {
+                Class<?> convClass = (Class<?>) convSpec;
+                conv = new Name(Lazy.MH_castReference, convClass, names[INARG_BASE + i]);
             } else {
-                if (dst.isPrimitive()) {
-                    // Caller has boxed a primitive.  Unbox it for the target.
-                    Wrapper w = Wrapper.forPrimitiveType(dst);
-                    if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType(), false)) {
-                        fn = ValueConversions.unbox(dst);
-                    } else if (src == Object.class || !Wrapper.isWrapperType(src)) {
-                        // Examples:  Object->int, Number->int, Comparable->int; Byte->int, Character->int
-                        // must include additional conversions
-                        // src must be examined at runtime, to detect Byte, Character, etc.
-                        MethodHandle unboxMethod = (level == 1
-                                                    ? ValueConversions.unbox(dst)
-                                                    : ValueConversions.unboxCast(dst));
-                        fn = unboxMethod;
-                    } else {
-                        // Example: Byte->int
-                        // Do this by reformulating the problem to Byte->byte.
-                        Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType();
-                        MethodHandle unbox = ValueConversions.unbox(srcPrim);
-                        // Compose the two conversions.  FIXME:  should make two Names for this job
-                        fn = unbox.asType(MethodType.methodType(dst, src));
-                    }
-                } 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.
-                    fn = ValueConversions.cast(dst, Lazy.MH_castReference);
-                }
+                MethodHandle fn = (MethodHandle) convSpec;
+                conv = new Name(fn, names[INARG_BASE + i]);
             }
-            Name conv = new Name(fn, names[INARG_BASE + i]);
             assert(names[nameCursor] == null);
             names[nameCursor++] = conv;
             assert(outArgs[OUTARG_BASE + i] == null);
@@ -292,25 +258,25 @@
         assert(nameCursor == OUT_CALL);
         names[OUT_CALL] = new Name(target, outArgs);
 
-        if (RETURN_CONV < 0) {
+        Object convSpec = convSpecs[INARG_COUNT];
+        if (!retConv) {
             assert(OUT_CALL == names.length-1);
         } else {
-            Class<?> needReturn = srcType.returnType();
-            Class<?> haveReturn = dstType.returnType();
-            MethodHandle fn;
-            Object[] arg = { names[OUT_CALL] };
-            if (haveReturn == void.class) {
-                // synthesize a zero value for the given void
-                Object zero = Wrapper.forBasicType(needReturn).zero();
-                fn = MethodHandles.constant(needReturn, zero);
-                arg = new Object[0];  // don't pass names[OUT_CALL] to conversion
+            Name conv;
+            if (convSpec == void.class) {
+                conv = new Name(LambdaForm.constantZero(BasicType.basicType(srcType.returnType())));
+            } else if (convSpec instanceof Class) {
+                Class<?> convClass = (Class<?>) convSpec;
+                conv = new Name(Lazy.MH_castReference, convClass, names[OUT_CALL]);
             } else {
-                MethodHandle identity = MethodHandles.identity(needReturn);
-                MethodType needConversion = identity.type().changeParameterType(0, haveReturn);
-                fn = makePairwiseConvert(identity, needConversion, level);
+                MethodHandle fn = (MethodHandle) convSpec;
+                if (fn.type().parameterCount() == 0)
+                    conv = new Name(fn);  // don't pass retval to void conversion
+                else
+                    conv = new Name(fn, names[OUT_CALL]);
             }
             assert(names[RETURN_CONV] == null);
-            names[RETURN_CONV] = new Name(fn, arg);
+            names[RETURN_CONV] = conv;
             assert(RETURN_CONV == names.length-1);
         }
 
@@ -345,6 +311,81 @@
         return SimpleMethodHandle.make(MethodType.methodType(refType, refType), form);
     }
 
+    static Object[] computeValueConversions(MethodType srcType, MethodType dstType,
+                                            boolean strict, boolean monobox) {
+        final int INARG_COUNT = srcType.parameterCount();
+        Object[] convSpecs = new Object[INARG_COUNT+1];
+        for (int i = 0; i <= INARG_COUNT; i++) {
+            boolean isRet = (i == INARG_COUNT);
+            Class<?> src = isRet ? dstType.returnType() : srcType.parameterType(i);
+            Class<?> dst = isRet ? srcType.returnType() : dstType.parameterType(i);
+            if (!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict)) {
+                convSpecs[i] = valueConversion(src, dst, strict, monobox);
+            }
+        }
+        return convSpecs;
+    }
+    static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType,
+                                            boolean strict) {
+        return makePairwiseConvert(target, srcType, strict, /*monobox=*/ false);
+    }
+
+    /**
+     * Find a conversion function from the given source to the given destination.
+     * This conversion function will be used as a LF NamedFunction.
+     * Return a Class object if a simple cast is needed.
+     * Return void.class if void is involved.
+     */
+    static Object valueConversion(Class<?> src, Class<?> dst, boolean strict, boolean monobox) {
+        assert(!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict));  // caller responsibility
+        if (dst == void.class)
+            return dst;
+        MethodHandle fn;
+        if (src.isPrimitive()) {
+            if (src == void.class) {
+                return void.class;  // caller must recognize this specially
+            } else if (dst.isPrimitive()) {
+                // Examples: int->byte, byte->int, boolean->int (!strict)
+                fn = ValueConversions.convertPrimitive(src, dst);
+            } else {
+                // Examples: int->Integer, boolean->Object, float->Number
+                Wrapper wsrc = Wrapper.forPrimitiveType(src);
+                fn = ValueConversions.boxExact(wsrc);
+                assert(fn.type().parameterType(0) == wsrc.primitiveType());
+                assert(fn.type().returnType() == wsrc.wrapperType());
+                if (!VerifyType.isNullConversion(wsrc.wrapperType(), dst, strict)) {
+                    // Corner case, such as int->Long, which will probably fail.
+                    MethodType mt = MethodType.methodType(dst, src);
+                    if (strict)
+                        fn = fn.asType(mt);
+                    else
+                        fn = MethodHandleImpl.makePairwiseConvert(fn, mt, /*strict=*/ false);
+                }
+            }
+        } else if (dst.isPrimitive()) {
+            Wrapper wdst = Wrapper.forPrimitiveType(dst);
+            if (monobox || src == wdst.wrapperType()) {
+                // Use a strongly-typed unboxer, if possible.
+                fn = ValueConversions.unboxExact(wdst, strict);
+            } else {
+                // Examples:  Object->int, Number->int, Comparable->int, Byte->int
+                // must include additional conversions
+                // src must be examined at runtime, to detect Byte, Character, etc.
+                fn = (strict
+                        ? ValueConversions.unboxWiden(wdst)
+                        : ValueConversions.unboxCast(wdst));
+            }
+        } 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.
+            return dst;
+        }
+        assert(fn.type().parameterCount() <= 1) : "pc"+Arrays.asList(src.getSimpleName(), dst.getSimpleName(), fn);
+        return fn;
+    }
+
     static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
         MethodType type = target.type();
         int last = type.parameterCount() - 1;
@@ -720,10 +761,16 @@
         MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType);
         // Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore().
         MethodHandle unboxResult;
-        if (type.returnType().isPrimitive()) {
-            unboxResult = ValueConversions.unbox(type.returnType());
+        Class<?> rtype = type.returnType();
+        if (rtype.isPrimitive()) {
+            if (rtype == void.class) {
+                unboxResult = ValueConversions.ignore();
+            } else {
+                Wrapper w = Wrapper.forPrimitiveType(type.returnType());
+                unboxResult = ValueConversions.unboxExact(w);
+            }
         } else {
-            unboxResult = ValueConversions.identity();
+            unboxResult = MethodHandles.identity(Object.class);
         }
 
         BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL();
@@ -773,7 +820,7 @@
             mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity));
             return mh;
         }
-        return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, 2);
+        return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, false, true);
     }
 
     static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
--- a/src/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 10 18:34:03 2014 +0400
@@ -2027,7 +2027,7 @@
         if (!target.type().isCastableTo(newType)) {
             throw new WrongMethodTypeException("cannot explicitly cast "+target+" to "+newType);
         }
-        return MethodHandleImpl.makePairwiseConvert(target, newType, 2);
+        return MethodHandleImpl.makePairwiseConvert(target, newType, false);
     }
 
     /**
--- a/src/share/classes/sun/invoke/util/ValueConversions.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/sun/invoke/util/ValueConversions.java	Wed Sep 10 18:34:03 2014 +0400
@@ -35,12 +35,28 @@
     private static final Class<?> THIS_CLASS = ValueConversions.class;
     private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
 
-    private static EnumMap<Wrapper, MethodHandle>[] newWrapperCaches(int n) {
-        @SuppressWarnings("unchecked")  // generic array creation
-        EnumMap<Wrapper, MethodHandle>[] caches
-                = (EnumMap<Wrapper, MethodHandle>[]) new EnumMap<?,?>[n];
+    /** Thread-safe canonicalized mapping from Wrapper to MethodHandle
+     * with unsynchronized reads and synchronized writes.
+     * It's safe to publish MethodHandles by data race because they are immutable. */
+    private static class WrapperCache {
+        /** EnumMap uses preconstructed array internally, which is constant during it's lifetime. */
+        private final EnumMap<Wrapper, MethodHandle> map = new EnumMap<>(Wrapper.class);
+
+        public MethodHandle get(Wrapper w) {
+            return map.get(w);
+        }
+        public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) {
+            // Simulate CAS to avoid racy duplication
+            MethodHandle prev = map.putIfAbsent(w, mh);
+            if (prev != null)  return prev;
+            return mh;
+        }
+    }
+
+    private static WrapperCache[] newWrapperCaches(int n) {
+        WrapperCache[] caches = new WrapperCache[n];
         for (int i = 0; i < n; i++)
-            caches[i] = new EnumMap<>(Wrapper.class);
+            caches[i] = new WrapperCache();
         return caches;
     }
 
@@ -51,63 +67,92 @@
     //   implicit conversions sanctioned by JLS 5.1.2, etc.
     //   explicit conversions as allowed by explicitCastArguments
 
+    static int unboxInteger(Integer x) {
+        return x;
+    }
     static int unboxInteger(Object x, boolean cast) {
         if (x instanceof Integer)
-            return ((Integer) x).intValue();
+            return (Integer) x;
         return primitiveConversion(Wrapper.INT, x, cast).intValue();
     }
 
+    static byte unboxByte(Byte x) {
+        return x;
+    }
     static byte unboxByte(Object x, boolean cast) {
         if (x instanceof Byte)
-            return ((Byte) x).byteValue();
+            return (Byte) x;
         return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();
     }
 
+    static short unboxShort(Short x) {
+        return x;
+    }
     static short unboxShort(Object x, boolean cast) {
         if (x instanceof Short)
-            return ((Short) x).shortValue();
+            return (Short) x;
         return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();
     }
 
+    static boolean unboxBoolean(Boolean x) {
+        return x;
+    }
     static boolean unboxBoolean(Object x, boolean cast) {
         if (x instanceof Boolean)
-            return ((Boolean) x).booleanValue();
+            return (Boolean) x;
         return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;
     }
 
+    static char unboxCharacter(Character x) {
+        return x;
+    }
     static char unboxCharacter(Object x, boolean cast) {
         if (x instanceof Character)
-            return ((Character) x).charValue();
+            return (Character) x;
         return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();
     }
 
+    static long unboxLong(Long x) {
+        return x;
+    }
     static long unboxLong(Object x, boolean cast) {
         if (x instanceof Long)
-            return ((Long) x).longValue();
+            return (Long) x;
         return primitiveConversion(Wrapper.LONG, x, cast).longValue();
     }
 
+    static float unboxFloat(Float x) {
+        return x;
+    }
     static float unboxFloat(Object x, boolean cast) {
         if (x instanceof Float)
-            return ((Float) x).floatValue();
+            return (Float) x;
         return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();
     }
 
+    static double unboxDouble(Double x) {
+        return x;
+    }
     static double unboxDouble(Object x, boolean cast) {
         if (x instanceof Double)
-            return ((Double) x).doubleValue();
+            return (Double) x;
         return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
     }
 
-    private static MethodType unboxType(Wrapper wrap) {
+    private static MethodType unboxType(Wrapper wrap, int kind) {
+        if (kind == 0)
+            return MethodType.methodType(wrap.primitiveType(), wrap.wrapperType());
         return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);
     }
 
-    private static final EnumMap<Wrapper, MethodHandle>[]
-            UNBOX_CONVERSIONS = newWrapperCaches(2);
+    private static final WrapperCache[] UNBOX_CONVERSIONS = newWrapperCaches(4);
 
-    private static MethodHandle unbox(Wrapper wrap, boolean cast) {
-        EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)];
+    private static MethodHandle unbox(Wrapper wrap, int kind) {
+        // kind 0 -> strongly typed with NPE
+        // kind 1 -> strongly typed but zero for null,
+        // kind 2 -> asType rules: accept multiple box types but only widening conversions with NPE
+        // kind 3 -> explicitCastArguments rules: allow narrowing conversions, zero for null
+        WrapperCache cache = UNBOX_CONVERSIONS[kind];
         MethodHandle mh = cache.get(wrap);
         if (mh != null) {
             return mh;
@@ -115,41 +160,59 @@
         // slow path
         switch (wrap) {
             case OBJECT:
-                mh = IDENTITY; break;
             case VOID:
-                mh = IGNORE; break;
-        }
-        if (mh != null) {
-            cache.put(wrap, mh);
-            return mh;
+                throw new IllegalArgumentException("unbox "+wrap);
         }
         // look up the method
         String name = "unbox" + wrap.wrapperSimpleName();
-        MethodType type = unboxType(wrap);
+        MethodType type = unboxType(wrap, kind);
         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;
+            if (kind > 0) {
+                boolean cast = (kind != 2);
+                mh = MethodHandles.insertArguments(mh, 1, cast);
+            }
+            if (kind == 1) {  // casting but exact (null -> zero)
+                mh = mh.asType(unboxType(wrap, 0));
+            }
+            return cache.put(wrap, mh);
         }
         throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
-                + (cast ? " (cast)" : ""));
+                + (kind <= 1 ? " (exact)" : kind == 3 ? " (cast)" : ""));
     }
 
-    public static MethodHandle unboxCast(Wrapper type) {
-        return unbox(type, true);
+    /** Return an exact unboxer for the given primitive type. */
+    public static MethodHandle unboxExact(Wrapper type) {
+        return unbox(type, 0);
     }
 
-    public static MethodHandle unbox(Class<?> type) {
-        return unbox(Wrapper.forPrimitiveType(type), false);
+    /** Return an exact unboxer for the given primitive type, with optional null-to-zero conversion.
+     *  The boolean says whether to throw an NPE on a null value (false means unbox a zero).
+     *  The type of the unboxer is of a form like (Integer)int.
+     */
+    public static MethodHandle unboxExact(Wrapper type, boolean throwNPE) {
+        return unbox(type, throwNPE ? 0 : 1);
     }
 
-    public static MethodHandle unboxCast(Class<?> type) {
-        return unbox(Wrapper.forPrimitiveType(type), true);
+    /** Return a widening unboxer for the given primitive type.
+     *  Widen narrower primitive boxes to the given type.
+     *  Do not narrow any primitive values or convert null to zero.
+     *  The type of the unboxer is of a form like (Object)int.
+     */
+    public static MethodHandle unboxWiden(Wrapper type) {
+        return unbox(type, 2);
+    }
+
+    /** Return a casting unboxer for the given primitive type.
+     *  Widen or narrow primitive values to the given type, or convert null to zero, as needed.
+     *  The type of the unboxer is of a form like (Object)int.
+     */
+    public static MethodHandle unboxCast(Wrapper type) {
+        return unbox(type, 3);
     }
 
     static private final Integer ZERO_INT = 0, ONE_INT = 1;
@@ -246,57 +309,26 @@
         return MethodType.methodType(boxType, wrap.primitiveType());
     }
 
-    private static final EnumMap<Wrapper, MethodHandle>[]
-            BOX_CONVERSIONS = newWrapperCaches(2);
+    private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1);
 
-    private static MethodHandle box(Wrapper wrap, boolean exact) {
-        EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)];
+    public static MethodHandle boxExact(Wrapper wrap) {
+        WrapperCache cache = BOX_CONVERSIONS[0];
         MethodHandle mh = cache.get(wrap);
         if (mh != null) {
             return mh;
         }
-        // slow path
-        switch (wrap) {
-            case OBJECT:
-                mh = IDENTITY; break;
-            case VOID:
-                mh = ZERO_OBJECT;
-                break;
-        }
-        if (mh != null) {
-            cache.put(wrap, mh);
-            return mh;
-        }
         // look up the method
         String name = "box" + wrap.wrapperSimpleName();
         MethodType type = boxType(wrap);
-        if (exact) {
-            try {
-                mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
-            } catch (ReflectiveOperationException ex) {
-                mh = null;
-            }
-        } else {
-            mh = box(wrap, !exact).asType(type.erase());
+        try {
+            mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+        } catch (ReflectiveOperationException ex) {
+            mh = null;
         }
         if (mh != null) {
-            cache.put(wrap, mh);
-            return mh;
+            return cache.put(wrap, mh);
         }
-        throw new IllegalArgumentException("cannot find box adapter for "
-                + wrap + (exact ? " (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);
-    }
-
-    public static MethodHandle box(Wrapper type) {
-        boolean exact = false;
-        return box(type, exact);
+        throw new IllegalArgumentException("cannot find box adapter for " + wrap);
     }
 
     /// Constant functions
@@ -328,11 +360,10 @@
         return 0;
     }
 
-    private static final EnumMap<Wrapper, MethodHandle>[]
-            CONSTANT_FUNCTIONS = newWrapperCaches(2);
+    private static final WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2);
 
     public static MethodHandle zeroConstantFunction(Wrapper wrap) {
-        EnumMap<Wrapper, MethodHandle> cache = CONSTANT_FUNCTIONS[0];
+        WrapperCache cache = CONSTANT_FUNCTIONS[0];
         MethodHandle mh = cache.get(wrap);
         if (mh != null) {
             return mh;
@@ -353,15 +384,13 @@
                 break;
         }
         if (mh != null) {
-            cache.put(wrap, mh);
-            return mh;
+            return cache.put(wrap, mh);
         }
 
         // use zeroInt and cast the result
         if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {
             mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);
-            cache.put(wrap, mh);
-            return mh;
+            return cache.put(wrap, mh);
         }
         throw new IllegalArgumentException("cannot find zero constant for " + wrap);
     }
@@ -423,15 +452,13 @@
         return x;
     }
 
-    private static final MethodHandle IDENTITY, CAST_REFERENCE, ZERO_OBJECT, IGNORE, EMPTY;
+    private static final MethodHandle IDENTITY, CAST_REFERENCE, IGNORE, EMPTY;
     static {
         try {
             MethodType idType = MethodType.genericMethodType(1);
             MethodType ignoreType = idType.changeReturnType(void.class);
-            MethodType zeroObjectType = MethodType.genericMethodType(0);
             IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
             CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
-            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));
         } catch (NoSuchMethodException | IllegalAccessException ex) {
@@ -439,30 +466,8 @@
         }
     }
 
-    private static final EnumMap<Wrapper, MethodHandle>[] WRAPPER_CASTS
-            = newWrapperCaches(1);
-
-    /** Return a method that casts its sole argument (an Object) to the given type
-     *  and returns it as the given type.
-     */
-    public static MethodHandle cast(Class<?> type) {
-        return cast(type, CAST_REFERENCE);
-    }
-    public static MethodHandle cast(Class<?> type, MethodHandle castReference) {
-        if (type.isPrimitive())  throw new IllegalArgumentException("cannot cast primitive type "+type);
-        MethodHandle mh;
-        Wrapper wrap = null;
-        EnumMap<Wrapper, MethodHandle> cache = null;
-        if (Wrapper.isWrapperType(type)) {
-            wrap = Wrapper.forWrapperType(type);
-            cache = WRAPPER_CASTS[0];
-            mh = cache.get(wrap);
-            if (mh != null)  return mh;
-        }
-        mh = MethodHandles.insertArguments(castReference, 0, type);
-        if (cache != null)
-            cache.put(wrap, mh);
-        return mh;
+    public static MethodHandle ignore() {
+        return IGNORE;
     }
 
     public static MethodHandle identity() {
@@ -477,7 +482,7 @@
     }
 
     public static MethodHandle identity(Wrapper wrap) {
-        EnumMap<Wrapper, MethodHandle> cache = CONSTANT_FUNCTIONS[1];
+        WrapperCache cache = CONSTANT_FUNCTIONS[1];
         MethodHandle mh = cache.get(wrap);
         if (mh != null) {
             return mh;
@@ -495,17 +500,16 @@
             mh = EMPTY;  // #(){} : #()void
         }
         if (mh != null) {
-            cache.put(wrap, mh);
-            return mh;
-        }
-
-        if (mh != null) {
-            cache.put(wrap, mh);
-            return mh;
+            return cache.put(wrap, mh);
         }
         throw new IllegalArgumentException("cannot find identity for " + wrap);
     }
 
+    /** Return a method that casts its second argument (an Object) to the given type (a Class). */
+    public static MethodHandle cast() {
+        return CAST_REFERENCE;
+    }
+
     /// Primitive conversions.
     // These are supported directly by the JVM, usually by a single instruction.
     // In the case of narrowing to a subword, there may be a pair of instructions.
@@ -712,11 +716,10 @@
         return (x ? (byte)1 : (byte)0);
     }
 
-    private static final EnumMap<Wrapper, MethodHandle>[]
-            CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
+    private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
 
     public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {
-        EnumMap<Wrapper, MethodHandle> cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
+        WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
         MethodHandle mh = cache.get(wdst);
         if (mh != null) {
             return mh;
@@ -724,17 +727,9 @@
         // slow path
         Class<?> src = wsrc.primitiveType();
         Class<?> dst = wdst.primitiveType();
-        MethodType type = src == void.class ? MethodType.methodType(dst) : MethodType.methodType(dst, src);
+        MethodType type = MethodType.methodType(dst, src);
         if (wsrc == wdst) {
-            mh = identity(src);
-        } else if (wsrc == Wrapper.VOID) {
-            mh = zeroConstantFunction(wdst);
-        } else if (wdst == Wrapper.VOID) {
-            mh = MethodHandles.dropArguments(EMPTY, 0, src);  // Defer back to MethodHandles.
-        } else if (wsrc == Wrapper.OBJECT) {
-            mh = unboxCast(dst);
-        } else if (wdst == Wrapper.OBJECT) {
-            mh = box(src);
+            mh = MethodHandles.identity(src);
         } else {
             assert(src.isPrimitive() && dst.isPrimitive());
             try {
@@ -745,8 +740,7 @@
         }
         if (mh != null) {
             assert(mh.type() == type) : mh;
-            cache.put(wdst, mh);
-            return mh;
+            return cache.put(wdst, mh);
         }
 
         throw new IllegalArgumentException("cannot find primitive conversion function for " +
--- a/test/sun/invoke/util/ValueConversionsTest.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/test/sun/invoke/util/ValueConversionsTest.java	Wed Sep 10 18:34:03 2014 +0400
@@ -25,7 +25,7 @@
 
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.Wrapper;
-
+import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.MethodHandle;
 import java.io.Serializable;
@@ -65,6 +65,7 @@
     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.VOID   || src == Wrapper.VOID  )  return;  // must have values
         if (dst == Wrapper.OBJECT)
             expectThrow = false;  // everything (even VOID==null here) converts to OBJECT
         try {
@@ -78,9 +79,9 @@
                 }
                 MethodHandle unboxer;
                 if (doCast)
-                    unboxer = ValueConversions.unboxCast(dst.primitiveType());
+                    unboxer = ValueConversions.unboxCast(dst);
                 else
-                    unboxer = ValueConversions.unbox(dst.primitiveType());
+                    unboxer = ValueConversions.unboxWiden(dst);
                 Object expResult = (box == null) ? dst.zero() : dst.wrap(box);
                 Object result = null;
                 switch (dst) {
@@ -91,9 +92,7 @@
                     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)";
@@ -111,22 +110,22 @@
     @Test
     public void testBox() throws Throwable {
         for (Wrapper w : Wrapper.values()) {
-            if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
+            if (w == Wrapper.VOID)    continue;  // skip this; no unboxed form
+            if (w == Wrapper.OBJECT)  continue;  // skip this; already unboxed
             for (int n = -5; n < 10; n++) {
                 Object box = w.wrap(n);
-                MethodHandle boxer = ValueConversions.box(w.primitiveType());
+                MethodHandle boxer = ValueConversions.boxExact(w);
                 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;
+                    case INT:     result = (Integer) boxer.invokeExact(/*int*/n); break;
+                    case LONG:    result = (Long)    boxer.invokeExact((long)n); break;
+                    case FLOAT:   result = (Float)   boxer.invokeExact((float)n); break;
+                    case DOUBLE:  result = (Double)  boxer.invokeExact((double)n); break;
+                    case CHAR:    result = (Character) boxer.invokeExact((char)n); break;
+                    case BYTE:    result = (Byte)    boxer.invokeExact((byte)n); break;
+                    case SHORT:   result = (Short)   boxer.invokeExact((short)n); break;
+                    case BOOLEAN: result = (Boolean) boxer.invokeExact((n & 1) != 0); break;
                 }
                 assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
                              expResult, result);
@@ -139,8 +138,8 @@
         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());
+            MethodHandle caster = ValueConversions.cast().bindTo(dst);
+            assertEquals(caster.type(), MethodHandles.identity(Object.class).type());
             for (Object obj : objects) {
                 Class<?> src = obj.getClass();
                 boolean canCast = dst.isAssignableFrom(src);
@@ -183,14 +182,12 @@
         }
     }
     static void testConvert(Wrapper src, Wrapper dst, long tval) throws Throwable {
+        if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT)  return;  // must have prims
+        if (dst == Wrapper.VOID   || src == Wrapper.VOID  )  return;  // must have values
         boolean testSingleCase = (tval != 0);
         final long tvalInit = tval;
         MethodHandle conv = ValueConversions.convertPrimitive(src, dst);
-        MethodType convType;
-        if (src == Wrapper.VOID)
-            convType = MethodType.methodType(dst.primitiveType() /* , void */);
-        else
-            convType = MethodType.methodType(dst.primitiveType(), src.primitiveType());
+        MethodType convType = MethodType.methodType(dst.primitiveType(), src.primitiveType());
         assertEquals(convType, conv.type());
         MethodHandle converter = conv.asType(conv.type().changeReturnType(Object.class));
         for (;;) {
@@ -206,9 +203,7 @@
                 case CHAR:    result = converter.invokeExact((char)n); break;
                 case BYTE:    result = converter.invokeExact((byte)n); break;
                 case SHORT:   result = converter.invokeExact((short)n); break;
-                case OBJECT:  result = converter.invokeExact((Object)n); break;
                 case BOOLEAN: result = converter.invokeExact((n & 1) != 0); break;
-                case VOID:    result = converter.invokeExact(); break;
                 default:  throw new AssertionError();
             }
             assertEquals("(src,dst,n,testValue)="+Arrays.asList(src,dst,"0x"+Long.toHexString(n),testValue),