提交 b1cb58f0 编写于 作者: V vlivanov

8050877: Improve code for pairwise argument conversions and value boxing/unboxing

Reviewed-by: vlivanov, psandoz
Contributed-by: john.r.rose@oracle.com
上级 67f60891
...@@ -771,7 +771,7 @@ public abstract class MethodHandle { ...@@ -771,7 +771,7 @@ public abstract class MethodHandle {
/*non-public*/ MethodHandle asTypeUncached(MethodType newType) { /*non-public*/ MethodHandle asTypeUncached(MethodType newType) {
if (!type.isConvertibleTo(newType)) if (!type.isConvertibleTo(newType))
throw new WrongMethodTypeException("cannot convert "+this+" to "+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 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123)); ...@@ -984,7 +984,7 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
int collectArgPos = type().parameterCount()-1; int collectArgPos = type().parameterCount()-1;
MethodHandle target = this; MethodHandle target = this;
if (arrayType != type().parameterType(collectArgPos)) 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); MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength);
return MethodHandles.collectArguments(target, collectArgPos, collector); return MethodHandles.collectArguments(target, collectArgPos, collector);
} }
......
...@@ -179,45 +179,49 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -179,45 +179,49 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
* integral widening or narrowing, and floating point widening or narrowing. * integral widening or narrowing, and floating point widening or narrowing.
* @param srcType required call type * @param srcType required call type
* @param target original method handle * @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, * @return an adapter to the original handle with the desired new type,
* or the original target if the types are already identical * or the original target if the types are already identical
* or null if the adaptation cannot be made * or null if the adaptation cannot be made
*/ */
static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, int level) { static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType,
assert(level >= 0 && level <= 2); boolean strict, boolean monobox) {
MethodType dstType = target.type(); MethodType dstType = target.type();
assert(dstType.parameterCount() == target.type().parameterCount()); assert(dstType.parameterCount() == target.type().parameterCount());
if (srcType == dstType) if (srcType == dstType)
return target; return target;
return makePairwiseConvertIndirect(target, srcType, strict, monobox);
}
// Calculate extra arguments (temporaries) required in the names array. private static int countNonNull(Object[] array) {
// FIXME: Use an ArrayList<Name>. Some arguments require more than one conversion step. int count = 0;
final int INARG_COUNT = srcType.parameterCount(); for (Object x : array) {
int conversions = 0; if (x != null) ++count;
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++;
} }
return count;
} }
boolean retConv = needConv[INARG_COUNT];
if (retConv && srcType.returnType() == void.class) { static MethodHandle makePairwiseConvertIndirect(MethodHandle target, MethodType srcType,
boolean strict, boolean monobox) {
// Calculate extra arguments (temporaries) required in the names array.
Object[] convSpecs = computeValueConversions(srcType, target.type(), strict, monobox);
final int INARG_COUNT = srcType.parameterCount();
int convCount = countNonNull(convSpecs);
boolean retConv = (convSpecs[INARG_COUNT] != null);
boolean retVoid = srcType.returnType() == void.class;
if (retConv && retVoid) {
convCount -= 1;
retConv = false; retConv = false;
conversions--;
} }
final int IN_MH = 0; final int IN_MH = 0;
final int INARG_BASE = 1; final int INARG_BASE = 1;
final int INARG_LIMIT = INARG_BASE + INARG_COUNT; 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 RETURN_CONV = (!retConv ? -1 : NAME_LIMIT - 1);
final int OUT_CALL = (!retConv ? NAME_LIMIT : RETURN_CONV) - 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. // Now build a LambdaForm.
MethodType lambdaType = srcType.basicType().invokerType(); MethodType lambdaType = srcType.basicType().invokerType();
...@@ -229,59 +233,21 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -229,59 +233,21 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
int nameCursor = INARG_LIMIT; int nameCursor = INARG_LIMIT;
for (int i = 0; i < INARG_COUNT; i++) { for (int i = 0; i < INARG_COUNT; i++) {
Class<?> src = srcType.parameterType(i); Object convSpec = convSpecs[i];
Class<?> dst = dstType.parameterType(i); if (convSpec == null) {
if (!needConv[i]) {
// do nothing: difference is trivial // do nothing: difference is trivial
outArgs[OUTARG_BASE + i] = names[INARG_BASE + i]; outArgs[OUTARG_BASE + i] = names[INARG_BASE + i];
continue; continue;
} }
// Tricky case analysis follows. Name conv;
MethodHandle fn = null; if (convSpec instanceof Class) {
if (src.isPrimitive()) { Class<?> convClass = (Class<?>) convSpec;
if (dst.isPrimitive()) { conv = new Name(Lazy.MH_castReference, convClass, names[INARG_BASE + i]);
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));
}
} 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 { } else {
// Simple reference conversion. MethodHandle fn = (MethodHandle) convSpec;
// Note: Do not check for a class hierarchy relation conv = new Name(fn, names[INARG_BASE + i]);
// between src and dst. In all cases a 'null' argument
// will pass the cast conversion.
fn = ValueConversions.cast(dst, Lazy.MH_castReference);
}
} }
Name conv = new Name(fn, names[INARG_BASE + i]);
assert(names[nameCursor] == null); assert(names[nameCursor] == null);
names[nameCursor++] = conv; names[nameCursor++] = conv;
assert(outArgs[OUTARG_BASE + i] == null); assert(outArgs[OUTARG_BASE + i] == null);
...@@ -292,25 +258,25 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -292,25 +258,25 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
assert(nameCursor == OUT_CALL); assert(nameCursor == OUT_CALL);
names[OUT_CALL] = new Name(target, outArgs); names[OUT_CALL] = new Name(target, outArgs);
if (RETURN_CONV < 0) { Object convSpec = convSpecs[INARG_COUNT];
if (!retConv) {
assert(OUT_CALL == names.length-1); assert(OUT_CALL == names.length-1);
} else { } else {
Class<?> needReturn = srcType.returnType(); Name conv;
Class<?> haveReturn = dstType.returnType(); if (convSpec == void.class) {
MethodHandle fn; conv = new Name(LambdaForm.constantZero(BasicType.basicType(srcType.returnType())));
Object[] arg = { names[OUT_CALL] }; } else if (convSpec instanceof Class) {
if (haveReturn == void.class) { Class<?> convClass = (Class<?>) convSpec;
// synthesize a zero value for the given void conv = new Name(Lazy.MH_castReference, convClass, names[OUT_CALL]);
Object zero = Wrapper.forBasicType(needReturn).zero();
fn = MethodHandles.constant(needReturn, zero);
arg = new Object[0]; // don't pass names[OUT_CALL] to conversion
} else { } else {
MethodHandle identity = MethodHandles.identity(needReturn); MethodHandle fn = (MethodHandle) convSpec;
MethodType needConversion = identity.type().changeParameterType(0, haveReturn); if (fn.type().parameterCount() == 0)
fn = makePairwiseConvert(identity, needConversion, level); conv = new Name(fn); // don't pass retval to void conversion
else
conv = new Name(fn, names[OUT_CALL]);
} }
assert(names[RETURN_CONV] == null); assert(names[RETURN_CONV] == null);
names[RETURN_CONV] = new Name(fn, arg); names[RETURN_CONV] = conv;
assert(RETURN_CONV == names.length-1); assert(RETURN_CONV == names.length-1);
} }
...@@ -345,6 +311,81 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -345,6 +311,81 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
return SimpleMethodHandle.make(MethodType.methodType(refType, refType), form); 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) { static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
MethodType type = target.type(); MethodType type = target.type();
int last = type.parameterCount() - 1; int last = type.parameterCount() - 1;
...@@ -720,10 +761,16 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -720,10 +761,16 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType); MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType);
// Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore(). // Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore().
MethodHandle unboxResult; MethodHandle unboxResult;
if (type.returnType().isPrimitive()) { Class<?> rtype = type.returnType();
unboxResult = ValueConversions.unbox(type.returnType()); if (rtype.isPrimitive()) {
if (rtype == void.class) {
unboxResult = ValueConversions.ignore();
} else {
Wrapper w = Wrapper.forPrimitiveType(type.returnType());
unboxResult = ValueConversions.unboxExact(w);
}
} else { } else {
unboxResult = ValueConversions.identity(); unboxResult = MethodHandles.identity(Object.class);
} }
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL();
...@@ -773,7 +820,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -773,7 +820,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity)); mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity));
return mh; 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; } static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
......
...@@ -2027,7 +2027,7 @@ return invoker; ...@@ -2027,7 +2027,7 @@ return invoker;
if (!target.type().isCastableTo(newType)) { if (!target.type().isCastableTo(newType)) {
throw new WrongMethodTypeException("cannot explicitly cast "+target+" to "+newType); throw new WrongMethodTypeException("cannot explicitly cast "+target+" to "+newType);
} }
return MethodHandleImpl.makePairwiseConvert(target, newType, 2); return MethodHandleImpl.makePairwiseConvert(target, newType, false);
} }
/** /**
......
...@@ -35,12 +35,28 @@ public class ValueConversions { ...@@ -35,12 +35,28 @@ public class ValueConversions {
private static final Class<?> THIS_CLASS = ValueConversions.class; private static final Class<?> THIS_CLASS = ValueConversions.class;
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
private static EnumMap<Wrapper, MethodHandle>[] newWrapperCaches(int n) { /** Thread-safe canonicalized mapping from Wrapper to MethodHandle
@SuppressWarnings("unchecked") // generic array creation * with unsynchronized reads and synchronized writes.
EnumMap<Wrapper, MethodHandle>[] caches * It's safe to publish MethodHandles by data race because they are immutable. */
= (EnumMap<Wrapper, MethodHandle>[]) new EnumMap<?,?>[n]; 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++) for (int i = 0; i < n; i++)
caches[i] = new EnumMap<>(Wrapper.class); caches[i] = new WrapperCache();
return caches; return caches;
} }
...@@ -51,63 +67,92 @@ public class ValueConversions { ...@@ -51,63 +67,92 @@ public class ValueConversions {
// implicit conversions sanctioned by JLS 5.1.2, etc. // implicit conversions sanctioned by JLS 5.1.2, etc.
// explicit conversions as allowed by explicitCastArguments // explicit conversions as allowed by explicitCastArguments
static int unboxInteger(Integer x) {
return x;
}
static int unboxInteger(Object x, boolean cast) { static int unboxInteger(Object x, boolean cast) {
if (x instanceof Integer) if (x instanceof Integer)
return ((Integer) x).intValue(); return (Integer) x;
return primitiveConversion(Wrapper.INT, x, cast).intValue(); return primitiveConversion(Wrapper.INT, x, cast).intValue();
} }
static byte unboxByte(Byte x) {
return x;
}
static byte unboxByte(Object x, boolean cast) { static byte unboxByte(Object x, boolean cast) {
if (x instanceof Byte) if (x instanceof Byte)
return ((Byte) x).byteValue(); return (Byte) x;
return primitiveConversion(Wrapper.BYTE, x, cast).byteValue(); return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();
} }
static short unboxShort(Short x) {
return x;
}
static short unboxShort(Object x, boolean cast) { static short unboxShort(Object x, boolean cast) {
if (x instanceof Short) if (x instanceof Short)
return ((Short) x).shortValue(); return (Short) x;
return primitiveConversion(Wrapper.SHORT, x, cast).shortValue(); return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();
} }
static boolean unboxBoolean(Boolean x) {
return x;
}
static boolean unboxBoolean(Object x, boolean cast) { static boolean unboxBoolean(Object x, boolean cast) {
if (x instanceof Boolean) if (x instanceof Boolean)
return ((Boolean) x).booleanValue(); return (Boolean) x;
return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0; return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;
} }
static char unboxCharacter(Character x) {
return x;
}
static char unboxCharacter(Object x, boolean cast) { static char unboxCharacter(Object x, boolean cast) {
if (x instanceof Character) if (x instanceof Character)
return ((Character) x).charValue(); return (Character) x;
return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue(); return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();
} }
static long unboxLong(Long x) {
return x;
}
static long unboxLong(Object x, boolean cast) { static long unboxLong(Object x, boolean cast) {
if (x instanceof Long) if (x instanceof Long)
return ((Long) x).longValue(); return (Long) x;
return primitiveConversion(Wrapper.LONG, x, cast).longValue(); return primitiveConversion(Wrapper.LONG, x, cast).longValue();
} }
static float unboxFloat(Float x) {
return x;
}
static float unboxFloat(Object x, boolean cast) { static float unboxFloat(Object x, boolean cast) {
if (x instanceof Float) if (x instanceof Float)
return ((Float) x).floatValue(); return (Float) x;
return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue(); return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();
} }
static double unboxDouble(Double x) {
return x;
}
static double unboxDouble(Object x, boolean cast) { static double unboxDouble(Object x, boolean cast) {
if (x instanceof Double) if (x instanceof Double)
return ((Double) x).doubleValue(); return (Double) x;
return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue(); 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); return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);
} }
private static final EnumMap<Wrapper, MethodHandle>[] private static final WrapperCache[] UNBOX_CONVERSIONS = newWrapperCaches(4);
UNBOX_CONVERSIONS = newWrapperCaches(2);
private static MethodHandle unbox(Wrapper wrap, boolean cast) { private static MethodHandle unbox(Wrapper wrap, int kind) {
EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)]; // 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); MethodHandle mh = cache.get(wrap);
if (mh != null) { if (mh != null) {
return mh; return mh;
...@@ -115,41 +160,59 @@ public class ValueConversions { ...@@ -115,41 +160,59 @@ public class ValueConversions {
// slow path // slow path
switch (wrap) { switch (wrap) {
case OBJECT: case OBJECT:
mh = IDENTITY; break;
case VOID: case VOID:
mh = IGNORE; break; throw new IllegalArgumentException("unbox "+wrap);
}
if (mh != null) {
cache.put(wrap, mh);
return mh;
} }
// look up the method // look up the method
String name = "unbox" + wrap.wrapperSimpleName(); String name = "unbox" + wrap.wrapperSimpleName();
MethodType type = unboxType(wrap); MethodType type = unboxType(wrap, kind);
try { try {
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
} catch (ReflectiveOperationException ex) { } catch (ReflectiveOperationException ex) {
mh = null; mh = null;
} }
if (mh != null) { if (mh != null) {
if (kind > 0) {
boolean cast = (kind != 2);
mh = MethodHandles.insertArguments(mh, 1, cast); mh = MethodHandles.insertArguments(mh, 1, cast);
cache.put(wrap, mh); }
return mh; 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 throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
+ (cast ? " (cast)" : "")); + (kind <= 1 ? " (exact)" : kind == 3 ? " (cast)" : ""));
} }
public static MethodHandle unboxCast(Wrapper type) { /** Return an exact unboxer for the given primitive type. */
return unbox(type, true); public static MethodHandle unboxExact(Wrapper type) {
return unbox(type, 0);
} }
public static MethodHandle unbox(Class<?> type) { /** Return an exact unboxer for the given primitive type, with optional null-to-zero conversion.
return unbox(Wrapper.forPrimitiveType(type), false); * 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);
}
/** 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 a casting unboxer for the given primitive type.
return unbox(Wrapper.forPrimitiveType(type), true); * 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; static private final Integer ZERO_INT = 0, ONE_INT = 1;
...@@ -246,57 +309,26 @@ public class ValueConversions { ...@@ -246,57 +309,26 @@ public class ValueConversions {
return MethodType.methodType(boxType, wrap.primitiveType()); return MethodType.methodType(boxType, wrap.primitiveType());
} }
private static final EnumMap<Wrapper, MethodHandle>[] private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1);
BOX_CONVERSIONS = newWrapperCaches(2);
private static MethodHandle box(Wrapper wrap, boolean exact) { public static MethodHandle boxExact(Wrapper wrap) {
EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)]; WrapperCache cache = BOX_CONVERSIONS[0];
MethodHandle mh = cache.get(wrap); MethodHandle mh = cache.get(wrap);
if (mh != null) { if (mh != null) {
return mh; 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 // look up the method
String name = "box" + wrap.wrapperSimpleName(); String name = "box" + wrap.wrapperSimpleName();
MethodType type = boxType(wrap); MethodType type = boxType(wrap);
if (exact) {
try { try {
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
} catch (ReflectiveOperationException ex) { } catch (ReflectiveOperationException ex) {
mh = null; mh = null;
} }
} else {
mh = box(wrap, !exact).asType(type.erase());
}
if (mh != null) { if (mh != null) {
cache.put(wrap, mh); return cache.put(wrap, mh);
return mh;
} }
throw new IllegalArgumentException("cannot find box adapter for " throw new IllegalArgumentException("cannot find box adapter for " + wrap);
+ 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);
} }
/// Constant functions /// Constant functions
...@@ -328,11 +360,10 @@ public class ValueConversions { ...@@ -328,11 +360,10 @@ public class ValueConversions {
return 0; return 0;
} }
private static final EnumMap<Wrapper, MethodHandle>[] private static final WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2);
CONSTANT_FUNCTIONS = newWrapperCaches(2);
public static MethodHandle zeroConstantFunction(Wrapper wrap) { public static MethodHandle zeroConstantFunction(Wrapper wrap) {
EnumMap<Wrapper, MethodHandle> cache = CONSTANT_FUNCTIONS[0]; WrapperCache cache = CONSTANT_FUNCTIONS[0];
MethodHandle mh = cache.get(wrap); MethodHandle mh = cache.get(wrap);
if (mh != null) { if (mh != null) {
return mh; return mh;
...@@ -353,15 +384,13 @@ public class ValueConversions { ...@@ -353,15 +384,13 @@ public class ValueConversions {
break; break;
} }
if (mh != null) { if (mh != null) {
cache.put(wrap, mh); return cache.put(wrap, mh);
return mh;
} }
// use zeroInt and cast the result // use zeroInt and cast the result
if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) { if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {
mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type); mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);
cache.put(wrap, mh); return cache.put(wrap, mh);
return mh;
} }
throw new IllegalArgumentException("cannot find zero constant for " + wrap); throw new IllegalArgumentException("cannot find zero constant for " + wrap);
} }
...@@ -423,15 +452,13 @@ public class ValueConversions { ...@@ -423,15 +452,13 @@ public class ValueConversions {
return x; return x;
} }
private static final MethodHandle IDENTITY, CAST_REFERENCE, ZERO_OBJECT, IGNORE, EMPTY; private static final MethodHandle IDENTITY, CAST_REFERENCE, IGNORE, EMPTY;
static { static {
try { try {
MethodType idType = MethodType.genericMethodType(1); MethodType idType = MethodType.genericMethodType(1);
MethodType ignoreType = idType.changeReturnType(void.class); MethodType ignoreType = idType.changeReturnType(void.class);
MethodType zeroObjectType = MethodType.genericMethodType(0);
IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType); IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", 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); IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1)); EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
} catch (NoSuchMethodException | IllegalAccessException ex) { } catch (NoSuchMethodException | IllegalAccessException ex) {
...@@ -439,30 +466,8 @@ public class ValueConversions { ...@@ -439,30 +466,8 @@ public class ValueConversions {
} }
} }
private static final EnumMap<Wrapper, MethodHandle>[] WRAPPER_CASTS public static MethodHandle ignore() {
= newWrapperCaches(1); return IGNORE;
/** 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 identity() { public static MethodHandle identity() {
...@@ -477,7 +482,7 @@ public class ValueConversions { ...@@ -477,7 +482,7 @@ public class ValueConversions {
} }
public static MethodHandle identity(Wrapper wrap) { public static MethodHandle identity(Wrapper wrap) {
EnumMap<Wrapper, MethodHandle> cache = CONSTANT_FUNCTIONS[1]; WrapperCache cache = CONSTANT_FUNCTIONS[1];
MethodHandle mh = cache.get(wrap); MethodHandle mh = cache.get(wrap);
if (mh != null) { if (mh != null) {
return mh; return mh;
...@@ -495,17 +500,16 @@ public class ValueConversions { ...@@ -495,17 +500,16 @@ public class ValueConversions {
mh = EMPTY; // #(){} : #()void mh = EMPTY; // #(){} : #()void
} }
if (mh != null) { if (mh != null) {
cache.put(wrap, mh); return cache.put(wrap, mh);
return mh;
}
if (mh != null) {
cache.put(wrap, mh);
return mh;
} }
throw new IllegalArgumentException("cannot find identity for " + wrap); 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. /// Primitive conversions.
// These are supported directly by the JVM, usually by a single instruction. // 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. // In the case of narrowing to a subword, there may be a pair of instructions.
...@@ -712,11 +716,10 @@ public class ValueConversions { ...@@ -712,11 +716,10 @@ public class ValueConversions {
return (x ? (byte)1 : (byte)0); return (x ? (byte)1 : (byte)0);
} }
private static final EnumMap<Wrapper, MethodHandle>[] private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) { 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); MethodHandle mh = cache.get(wdst);
if (mh != null) { if (mh != null) {
return mh; return mh;
...@@ -724,17 +727,9 @@ public class ValueConversions { ...@@ -724,17 +727,9 @@ public class ValueConversions {
// slow path // slow path
Class<?> src = wsrc.primitiveType(); Class<?> src = wsrc.primitiveType();
Class<?> dst = wdst.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) { if (wsrc == wdst) {
mh = identity(src); mh = MethodHandles.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);
} else { } else {
assert(src.isPrimitive() && dst.isPrimitive()); assert(src.isPrimitive() && dst.isPrimitive());
try { try {
...@@ -745,8 +740,7 @@ public class ValueConversions { ...@@ -745,8 +740,7 @@ public class ValueConversions {
} }
if (mh != null) { if (mh != null) {
assert(mh.type() == type) : mh; assert(mh.type() == type) : mh;
cache.put(wdst, mh); return cache.put(wdst, mh);
return mh;
} }
throw new IllegalArgumentException("cannot find primitive conversion function for " + throw new IllegalArgumentException("cannot find primitive conversion function for " +
......
...@@ -25,7 +25,7 @@ package test.sun.invoke.util; ...@@ -25,7 +25,7 @@ package test.sun.invoke.util;
import sun.invoke.util.ValueConversions; import sun.invoke.util.ValueConversions;
import sun.invoke.util.Wrapper; import sun.invoke.util.Wrapper;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.io.Serializable; import java.io.Serializable;
...@@ -65,6 +65,7 @@ public class ValueConversionsTest { ...@@ -65,6 +65,7 @@ public class ValueConversionsTest {
private void testUnbox(boolean doCast, Wrapper dst, Wrapper src) throws Throwable { private void testUnbox(boolean doCast, Wrapper dst, Wrapper src) throws Throwable {
boolean expectThrow = !doCast && !dst.isConvertibleFrom(src); boolean expectThrow = !doCast && !dst.isConvertibleFrom(src);
if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT) return; // must have prims 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) if (dst == Wrapper.OBJECT)
expectThrow = false; // everything (even VOID==null here) converts to OBJECT expectThrow = false; // everything (even VOID==null here) converts to OBJECT
try { try {
...@@ -78,9 +79,9 @@ public class ValueConversionsTest { ...@@ -78,9 +79,9 @@ public class ValueConversionsTest {
} }
MethodHandle unboxer; MethodHandle unboxer;
if (doCast) if (doCast)
unboxer = ValueConversions.unboxCast(dst.primitiveType()); unboxer = ValueConversions.unboxCast(dst);
else else
unboxer = ValueConversions.unbox(dst.primitiveType()); unboxer = ValueConversions.unboxWiden(dst);
Object expResult = (box == null) ? dst.zero() : dst.wrap(box); Object expResult = (box == null) ? dst.zero() : dst.wrap(box);
Object result = null; Object result = null;
switch (dst) { switch (dst) {
...@@ -91,9 +92,7 @@ public class ValueConversionsTest { ...@@ -91,9 +92,7 @@ public class ValueConversionsTest {
case CHAR: result = (char) unboxer.invokeExact(box); break; case CHAR: result = (char) unboxer.invokeExact(box); break;
case BYTE: result = (byte) unboxer.invokeExact(box); break; case BYTE: result = (byte) unboxer.invokeExact(box); break;
case SHORT: result = (short) 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 BOOLEAN: result = (boolean) unboxer.invokeExact(box); break;
case VOID: result = null; unboxer.invokeExact(box); break;
} }
if (expectThrow) { if (expectThrow) {
expResult = "(need an exception)"; expResult = "(need an exception)";
...@@ -112,21 +111,21 @@ public class ValueConversionsTest { ...@@ -112,21 +111,21 @@ public class ValueConversionsTest {
public void testBox() throws Throwable { public void testBox() throws Throwable {
for (Wrapper w : Wrapper.values()) { 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++) { for (int n = -5; n < 10; n++) {
Object box = w.wrap(n); Object box = w.wrap(n);
MethodHandle boxer = ValueConversions.box(w.primitiveType()); MethodHandle boxer = ValueConversions.boxExact(w);
Object expResult = box; Object expResult = box;
Object result = null; Object result = null;
switch (w) { switch (w) {
case INT: result = boxer.invokeExact(/*int*/n); break; case INT: result = (Integer) boxer.invokeExact(/*int*/n); break;
case LONG: result = boxer.invokeExact((long)n); break; case LONG: result = (Long) boxer.invokeExact((long)n); break;
case FLOAT: result = boxer.invokeExact((float)n); break; case FLOAT: result = (Float) boxer.invokeExact((float)n); break;
case DOUBLE: result = boxer.invokeExact((double)n); break; case DOUBLE: result = (Double) boxer.invokeExact((double)n); break;
case CHAR: result = boxer.invokeExact((char)n); break; case CHAR: result = (Character) boxer.invokeExact((char)n); break;
case BYTE: result = boxer.invokeExact((byte)n); break; case BYTE: result = (Byte) boxer.invokeExact((byte)n); break;
case SHORT: result = boxer.invokeExact((short)n); break; case SHORT: result = (Short) boxer.invokeExact((short)n); break;
case OBJECT: result = boxer.invokeExact((Object)n); break; case BOOLEAN: result = (Boolean) boxer.invokeExact((n & 1) != 0); break;
case BOOLEAN: result = boxer.invokeExact((n & 1) != 0); break;
} }
assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box), assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
expResult, result); expResult, result);
...@@ -139,8 +138,8 @@ public class ValueConversionsTest { ...@@ -139,8 +138,8 @@ public class ValueConversionsTest {
Class<?>[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class }; Class<?>[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class };
Object[] objects = { new Object(), Boolean.FALSE, "hello", (Long)12L, (Integer)6 }; Object[] objects = { new Object(), Boolean.FALSE, "hello", (Long)12L, (Integer)6 };
for (Class<?> dst : types) { for (Class<?> dst : types) {
MethodHandle caster = ValueConversions.cast(dst); MethodHandle caster = ValueConversions.cast().bindTo(dst);
assertEquals(caster.type(), ValueConversions.identity().type()); assertEquals(caster.type(), MethodHandles.identity(Object.class).type());
for (Object obj : objects) { for (Object obj : objects) {
Class<?> src = obj.getClass(); Class<?> src = obj.getClass();
boolean canCast = dst.isAssignableFrom(src); boolean canCast = dst.isAssignableFrom(src);
...@@ -183,14 +182,12 @@ public class ValueConversionsTest { ...@@ -183,14 +182,12 @@ public class ValueConversionsTest {
} }
} }
static void testConvert(Wrapper src, Wrapper dst, long tval) throws Throwable { 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); boolean testSingleCase = (tval != 0);
final long tvalInit = tval; final long tvalInit = tval;
MethodHandle conv = ValueConversions.convertPrimitive(src, dst); MethodHandle conv = ValueConversions.convertPrimitive(src, dst);
MethodType convType; MethodType convType = MethodType.methodType(dst.primitiveType(), src.primitiveType());
if (src == Wrapper.VOID)
convType = MethodType.methodType(dst.primitiveType() /* , void */);
else
convType = MethodType.methodType(dst.primitiveType(), src.primitiveType());
assertEquals(convType, conv.type()); assertEquals(convType, conv.type());
MethodHandle converter = conv.asType(conv.type().changeReturnType(Object.class)); MethodHandle converter = conv.asType(conv.type().changeReturnType(Object.class));
for (;;) { for (;;) {
...@@ -206,9 +203,7 @@ public class ValueConversionsTest { ...@@ -206,9 +203,7 @@ public class ValueConversionsTest {
case CHAR: result = converter.invokeExact((char)n); break; case CHAR: result = converter.invokeExact((char)n); break;
case BYTE: result = converter.invokeExact((byte)n); break; case BYTE: result = converter.invokeExact((byte)n); break;
case SHORT: result = converter.invokeExact((short)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 BOOLEAN: result = converter.invokeExact((n & 1) != 0); break;
case VOID: result = converter.invokeExact(); break;
default: throw new AssertionError(); default: throw new AssertionError();
} }
assertEquals("(src,dst,n,testValue)="+Arrays.asList(src,dst,"0x"+Long.toHexString(n),testValue), assertEquals("(src,dst,n,testValue)="+Arrays.asList(src,dst,"0x"+Long.toHexString(n),testValue),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册