提交 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 {
/*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 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
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);
}
......
......@@ -179,45 +179,49 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
* 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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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; }
......
......@@ -2027,7 +2027,7 @@ return invoker;
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);
}
/**
......
......@@ -35,12 +35,28 @@ public class ValueConversions {
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 @@ public class ValueConversions {
// 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 @@ public class ValueConversions {
// 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);
}
/** 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 @@ public class ValueConversions {
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 @@ public class ValueConversions {
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 @@ public class ValueConversions {
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 @@ public class ValueConversions {
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 @@ public class ValueConversions {
}
}
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 class ValueConversions {
}
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 @@ public class ValueConversions {
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 @@ public class ValueConversions {
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 @@ public class ValueConversions {
// 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 @@ public class ValueConversions {
}
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 " +
......
......@@ -25,7 +25,7 @@ package test.sun.invoke.util;
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 @@ public class ValueConversionsTest {
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 @@ public class ValueConversionsTest {
}
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 @@ public class ValueConversionsTest {
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 @@ public class ValueConversionsTest {
@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 @@ public class ValueConversionsTest {
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 @@ public class ValueConversionsTest {
}
}
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 @@ public class ValueConversionsTest {
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),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册