--- old/src/share/classes/java/lang/invoke/MethodHandle.java 2014-07-16 20:33:23.000000000 +0400 +++ new/src/share/classes/java/lang/invoke/MethodHandle.java 2014-07-16 20:33:23.000000000 +0400 @@ -763,7 +763,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); } /** @@ -976,7 +976,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); } --- old/src/share/classes/java/lang/invoke/MethodHandleImpl.java 2014-07-16 20:33:25.000000000 +0400 +++ new/src/share/classes/java/lang/invoke/MethodHandleImpl.java 2014-07-16 20:33:25.000000000 +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. 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,83 @@ 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 +763,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 +822,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 Empty throwException(T t) throws T { throw t; } --- old/src/share/classes/java/lang/invoke/MethodHandles.java 2014-07-16 20:33:27.000000000 +0400 +++ new/src/share/classes/java/lang/invoke/MethodHandles.java 2014-07-16 20:33:26.000000000 +0400 @@ -2020,7 +2020,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); } /** --- old/src/share/classes/sun/invoke/util/ValueConversions.java 2014-07-16 20:33:29.000000000 +0400 +++ new/src/share/classes/sun/invoke/util/ValueConversions.java 2014-07-16 20:33:28.000000000 +0400 @@ -35,12 +35,28 @@ private static final Class THIS_CLASS = ValueConversions.class; private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); - private static EnumMap[] newWrapperCaches(int n) { - @SuppressWarnings("unchecked") // generic array creation - EnumMap[] caches - = (EnumMap[]) 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 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[] - UNBOX_CONVERSIONS = newWrapperCaches(2); + private static final WrapperCache[] UNBOX_CONVERSIONS = newWrapperCaches(4); - private static MethodHandle unbox(Wrapper wrap, boolean cast) { - EnumMap 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[] - BOX_CONVERSIONS = newWrapperCaches(2); + private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1); - private static MethodHandle box(Wrapper wrap, boolean exact) { - EnumMap 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[] - CONSTANT_FUNCTIONS = newWrapperCaches(2); + private static final WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2); public static MethodHandle zeroConstantFunction(Wrapper wrap) { - EnumMap 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_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 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 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[] - 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 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 " + --- old/test/sun/invoke/util/ValueConversionsTest.java 2014-07-16 20:33:30.000000000 +0400 +++ new/test/sun/invoke/util/ValueConversionsTest.java 2014-07-16 20:33:30.000000000 +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),