8050877: Improve code for pairwise argument conversions and value boxing/unboxing
Reviewed-by: vlivanov, psandoz
Contributed-by: john.r.rose@oracle.com
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:50 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/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:50 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/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Sep 10 19:19:50 2014 +0400
@@ -2026,7 +2026,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/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java Wed Sep 10 19:19:50 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)" : ""));
+ }
+
+ /** Return an exact unboxer for the given primitive type. */
+ public static MethodHandle unboxExact(Wrapper type) {
+ return unbox(type, 0);
}
- public static MethodHandle unboxCast(Wrapper type) {
- return unbox(type, true);
+ /** 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 unbox(Class<?> type) {
- return unbox(Wrapper.forPrimitiveType(type), false);
+ /** 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);
}
- public static MethodHandle unboxCast(Class<?> type) {
- return unbox(Wrapper.forPrimitiveType(type), true);
+ /** 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/jdk/test/sun/invoke/util/ValueConversionsTest.java Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/test/sun/invoke/util/ValueConversionsTest.java Wed Sep 10 19:19:50 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),