Merge
This commit is contained in:
commit
fdf41bb6d1
@ -27,6 +27,7 @@ package java.lang.invoke;
|
|||||||
|
|
||||||
import sun.invoke.util.VerifyType;
|
import sun.invoke.util.VerifyType;
|
||||||
import sun.invoke.util.Wrapper;
|
import sun.invoke.util.Wrapper;
|
||||||
|
import sun.invoke.util.ValueConversions;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||||
import static java.lang.invoke.MethodHandleStatics.*;
|
import static java.lang.invoke.MethodHandleStatics.*;
|
||||||
@ -55,29 +56,35 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
this(target, newType, conv, null);
|
this(target, newType, conv, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getConversion() { return conversion; }
|
||||||
|
|
||||||
// TO DO: When adapting another MH with a null conversion, clone
|
// TO DO: When adapting another MH with a null conversion, clone
|
||||||
// the target and change its type, instead of adding another layer.
|
// the target and change its type, instead of adding another layer.
|
||||||
|
|
||||||
/** Can a JVM-level adapter directly implement the proposed
|
/** Can a JVM-level adapter directly implement the proposed
|
||||||
* argument conversions, as if by MethodHandles.convertArguments?
|
* argument conversions, as if by MethodHandles.convertArguments?
|
||||||
*/
|
*/
|
||||||
static boolean canPairwiseConvert(MethodType newType, MethodType oldType) {
|
static boolean canPairwiseConvert(MethodType newType, MethodType oldType, int level) {
|
||||||
// same number of args, of course
|
// same number of args, of course
|
||||||
int len = newType.parameterCount();
|
int len = newType.parameterCount();
|
||||||
if (len != oldType.parameterCount())
|
if (len != oldType.parameterCount())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check return type. (Not much can be done with it.)
|
// Check return type.
|
||||||
Class<?> exp = newType.returnType();
|
Class<?> exp = newType.returnType();
|
||||||
Class<?> ret = oldType.returnType();
|
Class<?> ret = oldType.returnType();
|
||||||
if (!VerifyType.isNullConversion(ret, exp))
|
if (!VerifyType.isNullConversion(ret, exp)) {
|
||||||
return false;
|
if (!convOpSupported(OP_COLLECT_ARGS))
|
||||||
|
return false;
|
||||||
|
if (!canConvertArgument(ret, exp, level))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check args pairwise.
|
// Check args pairwise.
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
Class<?> src = newType.parameterType(i); // source type
|
Class<?> src = newType.parameterType(i); // source type
|
||||||
Class<?> dst = oldType.parameterType(i); // destination type
|
Class<?> dst = oldType.parameterType(i); // destination type
|
||||||
if (!canConvertArgument(src, dst))
|
if (!canConvertArgument(src, dst, level))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,11 +94,14 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
/** Can a JVM-level adapter directly implement the proposed
|
/** Can a JVM-level adapter directly implement the proposed
|
||||||
* argument conversion, as if by MethodHandles.convertArguments?
|
* argument conversion, as if by MethodHandles.convertArguments?
|
||||||
*/
|
*/
|
||||||
static boolean canConvertArgument(Class<?> src, Class<?> dst) {
|
static boolean canConvertArgument(Class<?> src, Class<?> dst, int level) {
|
||||||
// ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
|
// ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
|
||||||
// so we don't need to repeat so much decision making.
|
// so we don't need to repeat so much decision making.
|
||||||
if (VerifyType.isNullConversion(src, dst)) {
|
if (VerifyType.isNullConversion(src, dst)) {
|
||||||
return true;
|
return true;
|
||||||
|
} else if (convOpSupported(OP_COLLECT_ARGS)) {
|
||||||
|
// If we can build filters, we can convert anything to anything.
|
||||||
|
return true;
|
||||||
} else if (src.isPrimitive()) {
|
} else if (src.isPrimitive()) {
|
||||||
if (dst.isPrimitive())
|
if (dst.isPrimitive())
|
||||||
return canPrimCast(src, dst);
|
return canPrimCast(src, dst);
|
||||||
@ -99,7 +109,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
return canBoxArgument(src, dst);
|
return canBoxArgument(src, dst);
|
||||||
} else {
|
} else {
|
||||||
if (dst.isPrimitive())
|
if (dst.isPrimitive())
|
||||||
return canUnboxArgument(src, dst);
|
return canUnboxArgument(src, dst, level);
|
||||||
else
|
else
|
||||||
return true; // any two refs can be interconverted
|
return true; // any two refs can be interconverted
|
||||||
}
|
}
|
||||||
@ -109,21 +119,20 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
* Create a JVM-level adapter method handle to conform the given method
|
* Create a JVM-level adapter method handle to conform the given method
|
||||||
* handle to the similar newType, using only pairwise argument conversions.
|
* handle to the similar newType, using only pairwise argument conversions.
|
||||||
* For each argument, convert incoming argument to the exact type needed.
|
* For each argument, convert incoming argument to the exact type needed.
|
||||||
* Only null conversions are allowed on the return value (until
|
* The argument conversions allowed are casting, boxing and unboxing,
|
||||||
* the JVM supports ricochet adapters).
|
|
||||||
* The argument conversions allowed are casting, unboxing,
|
|
||||||
* integral widening or narrowing, and floating point widening or narrowing.
|
* integral widening or narrowing, and floating point widening or narrowing.
|
||||||
* @param newType required call type
|
* @param newType required call type
|
||||||
* @param target original method handle
|
* @param target original method handle
|
||||||
|
* @param level which strength of conversion is allowed
|
||||||
* @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(MethodType newType, MethodHandle target) {
|
static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target, int level) {
|
||||||
MethodType oldType = target.type();
|
MethodType oldType = target.type();
|
||||||
if (newType == oldType) return target;
|
if (newType == oldType) return target;
|
||||||
|
|
||||||
if (!canPairwiseConvert(newType, oldType))
|
if (!canPairwiseConvert(newType, oldType, level))
|
||||||
return null;
|
return null;
|
||||||
// (after this point, it is an assertion error to fail to convert)
|
// (after this point, it is an assertion error to fail to convert)
|
||||||
|
|
||||||
@ -138,9 +147,14 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Class<?> needReturn = newType.returnType();
|
||||||
|
Class<?> haveReturn = oldType.returnType();
|
||||||
|
boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
|
||||||
|
|
||||||
// Now build a chain of one or more adapters.
|
// Now build a chain of one or more adapters.
|
||||||
MethodHandle adapter = target;
|
MethodHandle adapter = target, adapter2;
|
||||||
MethodType midType = oldType.changeReturnType(newType.returnType());
|
MethodType midType = oldType;
|
||||||
for (int i = 0; i <= lastConv; i++) {
|
for (int i = 0; i <= lastConv; i++) {
|
||||||
Class<?> src = newType.parameterType(i); // source type
|
Class<?> src = newType.parameterType(i); // source type
|
||||||
Class<?> dst = midType.parameterType(i); // destination type
|
Class<?> dst = midType.parameterType(i); // destination type
|
||||||
@ -149,22 +163,23 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Work the current type backward toward the desired caller type:
|
// Work the current type backward toward the desired caller type:
|
||||||
if (i != lastConv) {
|
midType = midType.changeParameterType(i, src);
|
||||||
midType = midType.changeParameterType(i, src);
|
if (i == lastConv) {
|
||||||
} else {
|
|
||||||
// When doing the last (or only) real conversion,
|
// When doing the last (or only) real conversion,
|
||||||
// force all remaining null conversions to happen also.
|
// force all remaining null conversions to happen also.
|
||||||
assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src)));
|
MethodType lastMidType = newType;
|
||||||
midType = newType;
|
if (retConv) lastMidType = lastMidType.changeReturnType(haveReturn);
|
||||||
|
assert(VerifyType.isNullConversion(lastMidType, midType));
|
||||||
|
midType = lastMidType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tricky case analysis follows.
|
// Tricky case analysis follows.
|
||||||
// It parallels canConvertArgument() above.
|
// It parallels canConvertArgument() above.
|
||||||
if (src.isPrimitive()) {
|
if (src.isPrimitive()) {
|
||||||
if (dst.isPrimitive()) {
|
if (dst.isPrimitive()) {
|
||||||
adapter = makePrimCast(midType, adapter, i, dst);
|
adapter2 = makePrimCast(midType, adapter, i, dst);
|
||||||
} else {
|
} else {
|
||||||
adapter = makeBoxArgument(midType, adapter, i, dst);
|
adapter2 = makeBoxArgument(midType, adapter, i, src);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (dst.isPrimitive()) {
|
if (dst.isPrimitive()) {
|
||||||
@ -174,29 +189,53 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
// conversions supported by reflect.Method.invoke.
|
// conversions supported by reflect.Method.invoke.
|
||||||
// Those conversions require a big nest of if/then/else logic,
|
// Those conversions require a big nest of if/then/else logic,
|
||||||
// which we prefer to make a user responsibility.
|
// which we prefer to make a user responsibility.
|
||||||
adapter = makeUnboxArgument(midType, adapter, i, dst);
|
adapter2 = makeUnboxArgument(midType, adapter, i, dst, level);
|
||||||
} else {
|
} else {
|
||||||
// Simple reference conversion.
|
// Simple reference conversion.
|
||||||
// Note: Do not check for a class hierarchy relation
|
// Note: Do not check for a class hierarchy relation
|
||||||
// between src and dst. In all cases a 'null' argument
|
// between src and dst. In all cases a 'null' argument
|
||||||
// will pass the cast conversion.
|
// will pass the cast conversion.
|
||||||
adapter = makeCheckCast(midType, adapter, i, dst);
|
adapter2 = makeCheckCast(midType, adapter, i, dst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(adapter != null);
|
assert(adapter2 != null) : Arrays.asList(src, dst, midType, adapter, i, target, newType);
|
||||||
assert(adapter.type() == midType);
|
assert(adapter2.type() == midType);
|
||||||
|
adapter = adapter2;
|
||||||
|
}
|
||||||
|
if (retConv) {
|
||||||
|
adapter2 = makeReturnConversion(adapter, haveReturn, needReturn);
|
||||||
|
assert(adapter2 != null);
|
||||||
|
adapter = adapter2;
|
||||||
}
|
}
|
||||||
if (adapter.type() != newType) {
|
if (adapter.type() != newType) {
|
||||||
// Only trivial conversions remain.
|
// Only trivial conversions remain.
|
||||||
adapter = makeRetypeOnly(newType, adapter);
|
adapter2 = makeRetypeOnly(newType, adapter);
|
||||||
assert(adapter != null);
|
assert(adapter2 != null);
|
||||||
|
adapter = adapter2;
|
||||||
// Actually, that's because there were no non-trivial ones:
|
// Actually, that's because there were no non-trivial ones:
|
||||||
assert(lastConv == -1);
|
assert(lastConv == -1 || retConv);
|
||||||
}
|
}
|
||||||
assert(adapter.type() == newType);
|
assert(adapter.type() == newType);
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MethodHandle makeReturnConversion(MethodHandle target, Class<?> haveReturn, Class<?> needReturn) {
|
||||||
|
MethodHandle adjustReturn;
|
||||||
|
if (haveReturn == void.class) {
|
||||||
|
// synthesize a zero value for the given void
|
||||||
|
Object zero = Wrapper.forBasicType(needReturn).zero();
|
||||||
|
adjustReturn = MethodHandles.constant(needReturn, zero);
|
||||||
|
} else {
|
||||||
|
MethodType needConversion = MethodType.methodType(needReturn, haveReturn);
|
||||||
|
adjustReturn = MethodHandles.identity(needReturn).asType(needConversion);
|
||||||
|
}
|
||||||
|
if (!canCollectArguments(adjustReturn.type(), target.type(), 0, false)) {
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||||
|
throw new InternalError("NYI");
|
||||||
|
}
|
||||||
|
return makeCollectArguments(adjustReturn, target, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a JVM-level adapter method handle to permute the arguments
|
* Create a JVM-level adapter method handle to permute the arguments
|
||||||
* of the given method.
|
* of the given method.
|
||||||
@ -224,7 +263,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
if (argumentMap.length != oldType.parameterCount())
|
if (argumentMap.length != oldType.parameterCount())
|
||||||
throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
|
throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
|
||||||
if (nullPermutation) {
|
if (nullPermutation) {
|
||||||
MethodHandle res = makePairwiseConvert(newType, target);
|
MethodHandle res = makePairwiseConvert(newType, target, 0);
|
||||||
// well, that was easy
|
// well, that was easy
|
||||||
if (res == null)
|
if (res == null)
|
||||||
throw newIllegalArgumentException("cannot convert pairwise: "+newType);
|
throw newIllegalArgumentException("cannot convert pairwise: "+newType);
|
||||||
@ -310,11 +349,25 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT;
|
return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int extractStackMove(int convOp) {
|
||||||
|
int spChange = convOp >> CONV_STACK_MOVE_SHIFT;
|
||||||
|
return spChange / MethodHandleNatives.JVM_STACK_MOVE_UNIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int extractStackMove(MethodHandle target) {
|
||||||
|
if (target instanceof AdapterMethodHandle) {
|
||||||
|
AdapterMethodHandle amh = (AdapterMethodHandle) target;
|
||||||
|
return extractStackMove(amh.getConversion());
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Construct an adapter conversion descriptor for a single-argument conversion. */
|
/** Construct an adapter conversion descriptor for a single-argument conversion. */
|
||||||
private static long makeConv(int convOp, int argnum, int src, int dest) {
|
private static long makeConv(int convOp, int argnum, int src, int dest) {
|
||||||
assert(src == (src & 0xF));
|
assert(src == (src & CONV_TYPE_MASK));
|
||||||
assert(dest == (dest & 0xF));
|
assert(dest == (dest & CONV_TYPE_MASK));
|
||||||
assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF);
|
assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF || convOp == OP_COLLECT_ARGS);
|
||||||
int stackMove = type2size(dest) - type2size(src);
|
int stackMove = type2size(dest) - type2size(src);
|
||||||
return ((long) argnum << 32 |
|
return ((long) argnum << 32 |
|
||||||
(long) convOp << CONV_OP_SHIFT |
|
(long) convOp << CONV_OP_SHIFT |
|
||||||
@ -323,11 +376,10 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
insertStackMove(stackMove)
|
insertStackMove(stackMove)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private static long makeConv(int convOp, int argnum, int stackMove) {
|
private static long makeDupConv(int convOp, int argnum, int stackMove) {
|
||||||
assert(convOp >= OP_DUP_ARGS && convOp <= OP_SPREAD_ARGS);
|
// simple argument motion, requiring one slot to specify
|
||||||
|
assert(convOp == OP_DUP_ARGS || convOp == OP_DROP_ARGS);
|
||||||
byte src = 0, dest = 0;
|
byte src = 0, dest = 0;
|
||||||
if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS)
|
|
||||||
src = dest = T_OBJECT;
|
|
||||||
return ((long) argnum << 32 |
|
return ((long) argnum << 32 |
|
||||||
(long) convOp << CONV_OP_SHIFT |
|
(long) convOp << CONV_OP_SHIFT |
|
||||||
(int) src << CONV_SRC_TYPE_SHIFT |
|
(int) src << CONV_SRC_TYPE_SHIFT |
|
||||||
@ -336,7 +388,8 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
private static long makeSwapConv(int convOp, int srcArg, byte type, int destSlot) {
|
private static long makeSwapConv(int convOp, int srcArg, byte type, int destSlot) {
|
||||||
assert(convOp >= OP_SWAP_ARGS && convOp <= OP_ROT_ARGS);
|
// more complex argument motion, requiring two slots to specify
|
||||||
|
assert(convOp == OP_SWAP_ARGS || convOp == OP_ROT_ARGS);
|
||||||
return ((long) srcArg << 32 |
|
return ((long) srcArg << 32 |
|
||||||
(long) convOp << CONV_OP_SHIFT |
|
(long) convOp << CONV_OP_SHIFT |
|
||||||
(int) type << CONV_SRC_TYPE_SHIFT |
|
(int) type << CONV_SRC_TYPE_SHIFT |
|
||||||
@ -344,6 +397,18 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
(int) destSlot << CONV_VMINFO_SHIFT
|
(int) destSlot << CONV_VMINFO_SHIFT
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
private static long makeSpreadConv(int convOp, int argnum, int src, int dest, int stackMove) {
|
||||||
|
// spreading or collecting, at a particular slot location
|
||||||
|
assert(convOp == OP_SPREAD_ARGS || convOp == OP_COLLECT_ARGS || convOp == OP_FOLD_ARGS);
|
||||||
|
// src = spread ? T_OBJECT (for array) : common type of collected args (else void)
|
||||||
|
// dest = spread ? element type of array : result type of collector (can be void)
|
||||||
|
return ((long) argnum << 32 |
|
||||||
|
(long) convOp << CONV_OP_SHIFT |
|
||||||
|
(int) src << CONV_SRC_TYPE_SHIFT |
|
||||||
|
(int) dest << CONV_DEST_TYPE_SHIFT |
|
||||||
|
insertStackMove(stackMove)
|
||||||
|
);
|
||||||
|
}
|
||||||
private static long makeConv(int convOp) {
|
private static long makeConv(int convOp) {
|
||||||
assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW);
|
assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW);
|
||||||
return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT); // stackMove, src, dst all zero
|
return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT); // stackMove, src, dst all zero
|
||||||
@ -570,14 +635,10 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
static boolean canPrimCast(Class<?> src, Class<?> dst) {
|
static boolean canPrimCast(Class<?> src, Class<?> dst) {
|
||||||
if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
|
if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (Wrapper.forPrimitiveType(dst).isFloating()) {
|
|
||||||
// both must be floating types
|
|
||||||
return Wrapper.forPrimitiveType(src).isFloating();
|
|
||||||
} else {
|
} else {
|
||||||
// both are integral, and all combinations work fine
|
boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
|
||||||
assert(Wrapper.forPrimitiveType(src).isIntegral() &&
|
boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
|
||||||
Wrapper.forPrimitiveType(dst).isIntegral());
|
return !(sflt | dflt); // no float support at present
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,6 +650,29 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
*/
|
*/
|
||||||
static MethodHandle makePrimCast(MethodType newType, MethodHandle target,
|
static MethodHandle makePrimCast(MethodType newType, MethodHandle target,
|
||||||
int arg, Class<?> convType) {
|
int arg, Class<?> convType) {
|
||||||
|
Class<?> src = newType.parameterType(arg);
|
||||||
|
if (canPrimCast(src, convType))
|
||||||
|
return makePrimCastOnly(newType, target, arg, convType);
|
||||||
|
Class<?> dst = convType;
|
||||||
|
boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
|
||||||
|
boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
|
||||||
|
if (sflt | dflt) {
|
||||||
|
MethodHandle convMethod;
|
||||||
|
if (sflt)
|
||||||
|
convMethod = ((src == double.class)
|
||||||
|
? ValueConversions.convertFromDouble(dst)
|
||||||
|
: ValueConversions.convertFromFloat(dst));
|
||||||
|
else
|
||||||
|
convMethod = ((dst == double.class)
|
||||||
|
? ValueConversions.convertToDouble(src)
|
||||||
|
: ValueConversions.convertToFloat(src));
|
||||||
|
long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
|
||||||
|
return new AdapterMethodHandle(target, newType, conv, convMethod);
|
||||||
|
}
|
||||||
|
throw new InternalError("makePrimCast");
|
||||||
|
}
|
||||||
|
static MethodHandle makePrimCastOnly(MethodType newType, MethodHandle target,
|
||||||
|
int arg, Class<?> convType) {
|
||||||
MethodType oldType = target.type();
|
MethodType oldType = target.type();
|
||||||
if (!canPrimCast(newType, oldType, arg, convType))
|
if (!canPrimCast(newType, oldType, arg, convType))
|
||||||
return null;
|
return null;
|
||||||
@ -602,7 +686,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
* The convType is the unboxed type; it can be either a primitive or wrapper.
|
* The convType is the unboxed type; it can be either a primitive or wrapper.
|
||||||
*/
|
*/
|
||||||
static boolean canUnboxArgument(MethodType newType, MethodType targetType,
|
static boolean canUnboxArgument(MethodType newType, MethodType targetType,
|
||||||
int arg, Class<?> convType) {
|
int arg, Class<?> convType, int level) {
|
||||||
if (!convOpSupported(OP_REF_TO_PRIM)) return false;
|
if (!convOpSupported(OP_REF_TO_PRIM)) return false;
|
||||||
Class<?> src = newType.parameterType(arg);
|
Class<?> src = newType.parameterType(arg);
|
||||||
Class<?> dst = targetType.parameterType(arg);
|
Class<?> dst = targetType.parameterType(arg);
|
||||||
@ -616,21 +700,31 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
return (diff == arg+1); // arg is sole non-trivial diff
|
return (diff == arg+1); // arg is sole non-trivial diff
|
||||||
}
|
}
|
||||||
/** Can an primitive unboxing adapter validly convert src to dst? */
|
/** Can an primitive unboxing adapter validly convert src to dst? */
|
||||||
static boolean canUnboxArgument(Class<?> src, Class<?> dst) {
|
static boolean canUnboxArgument(Class<?> src, Class<?> dst, int level) {
|
||||||
return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive());
|
assert(dst.isPrimitive());
|
||||||
|
// if we have JVM support for boxing, we can also do complex unboxing
|
||||||
|
if (convOpSupported(OP_PRIM_TO_REF)) return true;
|
||||||
|
Wrapper dw = Wrapper.forPrimitiveType(dst);
|
||||||
|
// Level 0 means cast and unbox. This works on any reference.
|
||||||
|
if (level == 0) return !src.isPrimitive();
|
||||||
|
assert(level >= 0 && level <= 2);
|
||||||
|
// Levels 1 and 2 allow widening and/or narrowing conversions.
|
||||||
|
// These are not supported directly by the JVM.
|
||||||
|
// But if the input reference is monomorphic, we can do it.
|
||||||
|
return dw.wrapperType() == src;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Factory method: Unbox the given argument.
|
/** Factory method: Unbox the given argument.
|
||||||
* Return null if this cannot be done.
|
* Return null if this cannot be done.
|
||||||
*/
|
*/
|
||||||
static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target,
|
static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target,
|
||||||
int arg, Class<?> convType) {
|
int arg, Class<?> convType, int level) {
|
||||||
MethodType oldType = target.type();
|
MethodType oldType = target.type();
|
||||||
Class<?> src = newType.parameterType(arg);
|
Class<?> src = newType.parameterType(arg);
|
||||||
Class<?> dst = oldType.parameterType(arg);
|
Class<?> dst = oldType.parameterType(arg);
|
||||||
Class<?> boxType = Wrapper.asWrapperType(convType);
|
Class<?> boxType = Wrapper.asWrapperType(convType);
|
||||||
Class<?> primType = Wrapper.asPrimitiveType(convType);
|
Class<?> primType = Wrapper.asPrimitiveType(convType);
|
||||||
if (!canUnboxArgument(newType, oldType, arg, convType))
|
if (!canUnboxArgument(newType, oldType, arg, convType, level))
|
||||||
return null;
|
return null;
|
||||||
MethodType castDone = newType;
|
MethodType castDone = newType;
|
||||||
if (!VerifyType.isNullConversion(src, boxType))
|
if (!VerifyType.isNullConversion(src, boxType))
|
||||||
@ -642,19 +736,46 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
return makeCheckCast(newType, adapter, arg, boxType);
|
return makeCheckCast(newType, adapter, arg, boxType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Can a boxing conversion validly convert src to dst? */
|
||||||
|
static boolean canBoxArgument(MethodType newType, MethodType targetType,
|
||||||
|
int arg, Class<?> convType) {
|
||||||
|
if (!convOpSupported(OP_PRIM_TO_REF)) return false;
|
||||||
|
Class<?> src = newType.parameterType(arg);
|
||||||
|
Class<?> dst = targetType.parameterType(arg);
|
||||||
|
Class<?> boxType = Wrapper.asWrapperType(convType);
|
||||||
|
convType = Wrapper.asPrimitiveType(convType);
|
||||||
|
if (!canCheckCast(boxType, dst)
|
||||||
|
|| boxType == convType
|
||||||
|
|| !VerifyType.isNullConversion(src, convType))
|
||||||
|
return false;
|
||||||
|
int diff = diffTypes(newType, targetType, false);
|
||||||
|
return (diff == arg+1); // arg is sole non-trivial diff
|
||||||
|
}
|
||||||
|
|
||||||
/** Can an primitive boxing adapter validly convert src to dst? */
|
/** Can an primitive boxing adapter validly convert src to dst? */
|
||||||
static boolean canBoxArgument(Class<?> src, Class<?> dst) {
|
static boolean canBoxArgument(Class<?> src, Class<?> dst) {
|
||||||
if (!convOpSupported(OP_PRIM_TO_REF)) return false;
|
if (!convOpSupported(OP_PRIM_TO_REF)) return false;
|
||||||
throw new UnsupportedOperationException("NYI");
|
return (src.isPrimitive() && !dst.isPrimitive());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Factory method: Unbox the given argument.
|
/** Factory method: Box the given argument.
|
||||||
* Return null if this cannot be done.
|
* Return null if this cannot be done.
|
||||||
*/
|
*/
|
||||||
static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target,
|
static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target,
|
||||||
int arg, Class<?> convType) {
|
int arg, Class<?> convType) {
|
||||||
// this is difficult to do in the JVM because it must GC
|
MethodType oldType = target.type();
|
||||||
return null;
|
Class<?> src = newType.parameterType(arg);
|
||||||
|
Class<?> dst = oldType.parameterType(arg);
|
||||||
|
Class<?> boxType = Wrapper.asWrapperType(convType);
|
||||||
|
Class<?> primType = Wrapper.asPrimitiveType(convType);
|
||||||
|
if (!canBoxArgument(newType, oldType, arg, convType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!VerifyType.isNullConversion(boxType, dst))
|
||||||
|
target = makeCheckCast(oldType.changeParameterType(arg, boxType), target, arg, dst);
|
||||||
|
MethodHandle boxerMethod = ValueConversions.box(Wrapper.forPrimitiveType(primType));
|
||||||
|
long conv = makeConv(OP_PRIM_TO_REF, arg, basicType(primType), T_OBJECT);
|
||||||
|
return new AdapterMethodHandle(target, newType, conv, boxerMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Can an adapter simply drop arguments to convert the target to newType? */
|
/** Can an adapter simply drop arguments to convert the target to newType? */
|
||||||
@ -699,7 +820,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
int slotCount = keep1InSlot - dropSlot;
|
int slotCount = keep1InSlot - dropSlot;
|
||||||
assert(slotCount >= dropArgCount);
|
assert(slotCount >= dropArgCount);
|
||||||
assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount());
|
assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount());
|
||||||
long conv = makeConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
|
long conv = makeDupConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
|
||||||
return new AdapterMethodHandle(target, newType, conv);
|
return new AdapterMethodHandle(target, newType, conv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -739,7 +860,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
int keep1InSlot = newType.parameterSlotDepth(dupArgPos);
|
int keep1InSlot = newType.parameterSlotDepth(dupArgPos);
|
||||||
int slotCount = keep1InSlot - dupSlot;
|
int slotCount = keep1InSlot - dupSlot;
|
||||||
assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount());
|
assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount());
|
||||||
long conv = makeConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
|
long conv = makeDupConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
|
||||||
return new AdapterMethodHandle(target, newType, conv);
|
return new AdapterMethodHandle(target, newType, conv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,7 +1021,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
for (int i = 0; i < spreadArgCount; i++) {
|
for (int i = 0; i < spreadArgCount; i++) {
|
||||||
Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
|
Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
|
||||||
Class<?> dst = targetType.parameterType(spreadArgPos + i);
|
Class<?> dst = targetType.parameterType(spreadArgPos + i);
|
||||||
if (src == null || !VerifyType.isNullConversion(src, dst))
|
if (src == null || !canConvertArgument(src, dst, 1))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -910,24 +1031,100 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
|||||||
/** Factory method: Spread selected argument. */
|
/** Factory method: Spread selected argument. */
|
||||||
static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target,
|
static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target,
|
||||||
Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
|
Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
|
||||||
|
// FIXME: Get rid of newType; derive new arguments from structure of spreadArgType
|
||||||
MethodType targetType = target.type();
|
MethodType targetType = target.type();
|
||||||
if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
|
if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
|
||||||
return null;
|
return null;
|
||||||
|
// dest is not significant; remove?
|
||||||
|
int dest = T_VOID;
|
||||||
|
for (int i = 0; i < spreadArgCount; i++) {
|
||||||
|
Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
|
||||||
|
if (arg == null) arg = Object.class;
|
||||||
|
int dest2 = basicType(arg);
|
||||||
|
if (dest == T_VOID) dest = dest2;
|
||||||
|
else if (dest != dest2) dest = T_VOID;
|
||||||
|
if (dest == T_VOID) break;
|
||||||
|
targetType = targetType.changeParameterType(spreadArgPos + i, arg);
|
||||||
|
}
|
||||||
|
target = target.asType(targetType);
|
||||||
|
int arrayArgSize = 1; // always a reference
|
||||||
// in arglist: [0: ...keep1 | spos: spreadArg | spos+1: keep2... ]
|
// in arglist: [0: ...keep1 | spos: spreadArg | spos+1: keep2... ]
|
||||||
// out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ]
|
// out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ]
|
||||||
int keep2OutPos = spreadArgPos + spreadArgCount;
|
int keep2OutPos = spreadArgPos + spreadArgCount;
|
||||||
int spreadSlot = targetType.parameterSlotDepth(keep2OutPos);
|
int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos); // leading edge of |spread...|
|
||||||
int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos);
|
int spreadSlot = targetType.parameterSlotDepth(keep2OutPos); // trailing edge of |spread...|
|
||||||
int slotCount = keep1OutSlot - spreadSlot;
|
assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+arrayArgSize));
|
||||||
assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+1));
|
int slotCount = keep1OutSlot - spreadSlot; // slots in |spread...|
|
||||||
assert(slotCount >= spreadArgCount);
|
assert(slotCount >= spreadArgCount);
|
||||||
long conv = makeConv(OP_SPREAD_ARGS, spreadArgPos, slotCount-1);
|
int stackMove = - arrayArgSize + slotCount; // pop array, push N slots
|
||||||
|
long conv = makeSpreadConv(OP_SPREAD_ARGS, spreadArgPos, T_OBJECT, dest, stackMove);
|
||||||
MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType);
|
MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType);
|
||||||
assert(res.type().parameterType(spreadArgPos) == spreadArgType);
|
assert(res.type().parameterType(spreadArgPos) == spreadArgType);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO DO: makeCollectArguments, makeFlyby, makeRicochet
|
/** Can an adapter collect a series of arguments, replacing them by zero or one results? */
|
||||||
|
static boolean canCollectArguments(MethodType targetType,
|
||||||
|
MethodType collectorType, int collectArgPos, boolean retainOriginalArgs) {
|
||||||
|
if (!convOpSupported(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS)) return false;
|
||||||
|
int collectArgCount = collectorType.parameterCount();
|
||||||
|
Class<?> rtype = collectorType.returnType();
|
||||||
|
assert(rtype == void.class || targetType.parameterType(collectArgPos) == rtype)
|
||||||
|
// [(Object)Object[], (Object[])Object[], 0, 1]
|
||||||
|
: Arrays.asList(targetType, collectorType, collectArgPos, collectArgCount)
|
||||||
|
;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Factory method: Collect or filter selected argument(s). */
|
||||||
|
static MethodHandle makeCollectArguments(MethodHandle target,
|
||||||
|
MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
|
||||||
|
assert(canCollectArguments(target.type(), collector.type(), collectArgPos, retainOriginalArgs));
|
||||||
|
MethodType targetType = target.type();
|
||||||
|
MethodType collectorType = collector.type();
|
||||||
|
int collectArgCount = collectorType.parameterCount();
|
||||||
|
Class<?> collectValType = collectorType.returnType();
|
||||||
|
int collectValCount = (collectValType == void.class ? 0 : 1);
|
||||||
|
int collectValSlots = collectorType.returnSlotCount();
|
||||||
|
MethodType newType = targetType
|
||||||
|
.dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
|
||||||
|
if (!retainOriginalArgs) {
|
||||||
|
newType = newType
|
||||||
|
.insertParameterTypes(collectArgPos, collectorType.parameterList());
|
||||||
|
} else {
|
||||||
|
// parameter types at the fold point must be the same
|
||||||
|
assert(diffParamTypes(newType, collectArgPos, targetType, collectValCount, collectArgCount, false) == 0)
|
||||||
|
: Arrays.asList(target, collector, collectArgPos, retainOriginalArgs);
|
||||||
|
}
|
||||||
|
// in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ]
|
||||||
|
// out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
|
||||||
|
// out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ]
|
||||||
|
int keep2InPos = collectArgPos + collectArgCount;
|
||||||
|
int keep1InSlot = newType.parameterSlotDepth(collectArgPos); // leading edge of |collect...|
|
||||||
|
int collectSlot = newType.parameterSlotDepth(keep2InPos); // trailing edge of |collect...|
|
||||||
|
int slotCount = keep1InSlot - collectSlot; // slots in |collect...|
|
||||||
|
assert(slotCount >= collectArgCount);
|
||||||
|
assert(collectSlot == targetType.parameterSlotDepth(
|
||||||
|
collectArgPos + collectValCount + (retainOriginalArgs ? collectArgCount : 0) ));
|
||||||
|
int dest = basicType(collectValType);
|
||||||
|
int src = T_VOID;
|
||||||
|
// src is not significant; remove?
|
||||||
|
for (int i = 0; i < collectArgCount; i++) {
|
||||||
|
int src2 = basicType(collectorType.parameterType(i));
|
||||||
|
if (src == T_VOID) src = src2;
|
||||||
|
else if (src != src2) src = T_VOID;
|
||||||
|
if (src == T_VOID) break;
|
||||||
|
}
|
||||||
|
int stackMove = collectValSlots; // push 0..2 results
|
||||||
|
if (!retainOriginalArgs) stackMove -= slotCount; // pop N arguments
|
||||||
|
int lastCollectArg = keep2InPos-1;
|
||||||
|
long conv = makeSpreadConv(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS,
|
||||||
|
lastCollectArg, src, dest, stackMove);
|
||||||
|
MethodHandle res = new AdapterMethodHandle(target, newType, conv, collector);
|
||||||
|
assert(res.type().parameterList().subList(collectArgPos, collectArgPos+collectArgCount)
|
||||||
|
.equals(collector.type().parameterList()));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -273,9 +273,9 @@ public class CallSite {
|
|||||||
Object binding;
|
Object binding;
|
||||||
info = maybeReBox(info);
|
info = maybeReBox(info);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
binding = bootstrapMethod.invokeGeneric(caller, name, type);
|
binding = bootstrapMethod.invoke(caller, name, type);
|
||||||
} else if (!info.getClass().isArray()) {
|
} else if (!info.getClass().isArray()) {
|
||||||
binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
|
binding = bootstrapMethod.invoke(caller, name, type, info);
|
||||||
} else {
|
} else {
|
||||||
Object[] argv = (Object[]) info;
|
Object[] argv = (Object[]) info;
|
||||||
maybeReBoxElements(argv);
|
maybeReBoxElements(argv);
|
||||||
@ -283,10 +283,10 @@ public class CallSite {
|
|||||||
throw new BootstrapMethodError("too many bootstrap method arguments");
|
throw new BootstrapMethodError("too many bootstrap method arguments");
|
||||||
MethodType bsmType = bootstrapMethod.type();
|
MethodType bsmType = bootstrapMethod.type();
|
||||||
if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
|
if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
|
||||||
binding = bootstrapMethod.invokeGeneric(caller, name, type, argv);
|
binding = bootstrapMethod.invoke(caller, name, type, argv);
|
||||||
else
|
else
|
||||||
binding = MethodHandles.spreadInvoker(bsmType, 3)
|
binding = MethodHandles.spreadInvoker(bsmType, 3)
|
||||||
.invokeGeneric(bootstrapMethod, caller, name, type, argv);
|
.invoke(bootstrapMethod, caller, name, type, argv);
|
||||||
}
|
}
|
||||||
//System.out.println("BSM for "+name+type+" => "+binding);
|
//System.out.println("BSM for "+name+type+" => "+binding);
|
||||||
if (binding instanceof CallSite) {
|
if (binding instanceof CallSite) {
|
||||||
|
@ -61,6 +61,10 @@ class FilterGeneric {
|
|||||||
return ad;
|
return ad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
|
||||||
|
}
|
||||||
|
|
||||||
Adapter makeInstance(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
|
Adapter makeInstance(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
|
||||||
Adapter ad = getAdapter(kind, pos);
|
Adapter ad = getAdapter(kind, pos);
|
||||||
return ad.makeInstance(ad.prototypeEntryPoint(), filter, target);
|
return ad.makeInstance(ad.prototypeEntryPoint(), filter, target);
|
||||||
|
@ -67,6 +67,10 @@ class FilterOneArgument extends BoundMethodHandle {
|
|||||||
this.target = target;
|
this.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
|
||||||
|
}
|
||||||
|
|
||||||
public static MethodHandle make(MethodHandle filter, MethodHandle target) {
|
public static MethodHandle make(MethodHandle filter, MethodHandle target) {
|
||||||
if (filter == null) return target;
|
if (filter == null) return target;
|
||||||
if (target == null) return filter;
|
if (target == null) return filter;
|
||||||
|
@ -98,6 +98,10 @@ class FromGeneric {
|
|||||||
this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
|
this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The typed target will be called according to targetType.
|
* The typed target will be called according to targetType.
|
||||||
* The adapter code will in fact see the raw result from internalType,
|
* The adapter code will in fact see the raw result from internalType,
|
||||||
@ -112,10 +116,10 @@ class FromGeneric {
|
|||||||
assert(iret == Object.class);
|
assert(iret == Object.class);
|
||||||
return ValueConversions.identity();
|
return ValueConversions.identity();
|
||||||
} else if (wrap.primitiveType() == iret) {
|
} else if (wrap.primitiveType() == iret) {
|
||||||
return ValueConversions.box(wrap, false);
|
return ValueConversions.box(wrap);
|
||||||
} else {
|
} else {
|
||||||
assert(tret == double.class ? iret == long.class : iret == int.class);
|
assert(tret == double.class ? iret == long.class : iret == int.class);
|
||||||
return ValueConversions.boxRaw(wrap, false);
|
return ValueConversions.boxRaw(wrap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +139,7 @@ class FromGeneric {
|
|||||||
MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
|
MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
|
||||||
MethodHandle fixArgs = MethodHandleImpl.convertArguments(
|
MethodHandle fixArgs = MethodHandleImpl.convertArguments(
|
||||||
invoker, Invokers.invokerType(fixArgsType),
|
invoker, Invokers.invokerType(fixArgsType),
|
||||||
invoker.type(), null);
|
invoker.type(), 0);
|
||||||
if (fixArgs == null)
|
if (fixArgs == null)
|
||||||
throw new InternalError("bad fixArgs");
|
throw new InternalError("bad fixArgs");
|
||||||
// reinterpret the calling sequence as raw:
|
// reinterpret the calling sequence as raw:
|
||||||
@ -160,7 +164,6 @@ class FromGeneric {
|
|||||||
/** Build an adapter of the given generic type, which invokes typedTarget
|
/** Build an adapter of the given generic type, which invokes typedTarget
|
||||||
* on the incoming arguments, after unboxing as necessary.
|
* on the incoming arguments, after unboxing as necessary.
|
||||||
* The return value is boxed if necessary.
|
* The return value is boxed if necessary.
|
||||||
* @param genericType the required type of the result
|
|
||||||
* @param typedTarget the target
|
* @param typedTarget the target
|
||||||
* @return an adapter method handle
|
* @return an adapter method handle
|
||||||
*/
|
*/
|
||||||
@ -231,7 +234,7 @@ class FromGeneric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Adapter buildAdapterFromBytecodes(MethodType internalType) {
|
static Adapter buildAdapterFromBytecodes(MethodType internalType) {
|
||||||
throw new UnsupportedOperationException("NYI");
|
throw new UnsupportedOperationException("NYI "+internalType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,12 +29,12 @@ import sun.invoke.util.*;
|
|||||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapters which manage MethodHandle.invokeGeneric calls.
|
* Adapters which manage inexact MethodHandle.invoke calls.
|
||||||
* The JVM calls one of these when the exact type match fails.
|
* The JVM calls one of these when the exact type match fails.
|
||||||
* @author jrose
|
* @author jrose
|
||||||
*/
|
*/
|
||||||
class InvokeGeneric {
|
class InvokeGeneric {
|
||||||
// erased type for the call, which originates from an invokeGeneric site
|
// erased type for the call, which originates from an inexact invoke site
|
||||||
private final MethodType erasedCallerType;
|
private final MethodType erasedCallerType;
|
||||||
// an invoker of type (MT, MH; A...) -> R
|
// an invoker of type (MT, MH; A...) -> R
|
||||||
private final MethodHandle initialInvoker;
|
private final MethodHandle initialInvoker;
|
||||||
@ -56,7 +56,7 @@ class InvokeGeneric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Return the adapter information for this type's erasure. */
|
/** Return the adapter information for this type's erasure. */
|
||||||
/*non-public*/ static MethodHandle genericInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException {
|
/*non-public*/ static MethodHandle generalInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException {
|
||||||
InvokeGeneric gen = new InvokeGeneric(erasedCallerType);
|
InvokeGeneric gen = new InvokeGeneric(erasedCallerType);
|
||||||
return gen.initialInvoker;
|
return gen.initialInvoker;
|
||||||
}
|
}
|
||||||
|
@ -43,10 +43,10 @@ class Invokers {
|
|||||||
private /*lazy*/ MethodHandle erasedInvoker;
|
private /*lazy*/ MethodHandle erasedInvoker;
|
||||||
/*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric
|
/*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric
|
||||||
|
|
||||||
// generic (untyped) invoker for the outgoing call
|
// general invoker for the outgoing call
|
||||||
private /*lazy*/ MethodHandle genericInvoker;
|
private /*lazy*/ MethodHandle generalInvoker;
|
||||||
|
|
||||||
// generic (untyped) invoker for the outgoing call; accepts a single Object[]
|
// general invoker for the outgoing call; accepts a single Object[]
|
||||||
private final /*lazy*/ MethodHandle[] spreadInvokers;
|
private final /*lazy*/ MethodHandle[] spreadInvokers;
|
||||||
|
|
||||||
// invoker for an unbound callsite
|
// invoker for an unbound callsite
|
||||||
@ -77,13 +77,13 @@ class Invokers {
|
|||||||
return invoker;
|
return invoker;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*non-public*/ MethodHandle genericInvoker() {
|
/*non-public*/ MethodHandle generalInvoker() {
|
||||||
MethodHandle invoker1 = exactInvoker();
|
MethodHandle invoker1 = exactInvoker();
|
||||||
MethodHandle invoker = genericInvoker;
|
MethodHandle invoker = generalInvoker;
|
||||||
if (invoker != null) return invoker;
|
if (invoker != null) return invoker;
|
||||||
MethodType genericType = targetType.generic();
|
MethodType generalType = targetType.generic();
|
||||||
invoker = MethodHandles.convertArguments(invoker1, invokerType(genericType));
|
invoker = invoker1.asType(invokerType(generalType));
|
||||||
genericInvoker = invoker;
|
generalInvoker = invoker;
|
||||||
return invoker;
|
return invoker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,9 +93,9 @@ class Invokers {
|
|||||||
if (invoker != null) return invoker;
|
if (invoker != null) return invoker;
|
||||||
MethodType erasedType = targetType.erase();
|
MethodType erasedType = targetType.erase();
|
||||||
if (erasedType == targetType.generic())
|
if (erasedType == targetType.generic())
|
||||||
invoker = genericInvoker();
|
invoker = generalInvoker();
|
||||||
else
|
else
|
||||||
invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType));
|
invoker = invoker1.asType(invokerType(erasedType));
|
||||||
erasedInvoker = invoker;
|
erasedInvoker = invoker;
|
||||||
return invoker;
|
return invoker;
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ class Invokers {
|
|||||||
/*non-public*/ MethodHandle spreadInvoker(int objectArgCount) {
|
/*non-public*/ MethodHandle spreadInvoker(int objectArgCount) {
|
||||||
MethodHandle vaInvoker = spreadInvokers[objectArgCount];
|
MethodHandle vaInvoker = spreadInvokers[objectArgCount];
|
||||||
if (vaInvoker != null) return vaInvoker;
|
if (vaInvoker != null) return vaInvoker;
|
||||||
MethodHandle gInvoker = genericInvoker();
|
MethodHandle gInvoker = generalInvoker();
|
||||||
vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount);
|
vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount);
|
||||||
spreadInvokers[objectArgCount] = vaInvoker;
|
spreadInvokers[objectArgCount] = vaInvoker;
|
||||||
return vaInvoker;
|
return vaInvoker;
|
||||||
|
@ -525,7 +525,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
|||||||
/** A factory type for resolving member names with the help of the VM.
|
/** A factory type for resolving member names with the help of the VM.
|
||||||
* TBD: Define access-safe public constructors for this factory.
|
* TBD: Define access-safe public constructors for this factory.
|
||||||
*/
|
*/
|
||||||
public static class Factory {
|
/*non-public*/ static class Factory {
|
||||||
private Factory() { } // singleton pattern
|
private Factory() { } // singleton pattern
|
||||||
static Factory INSTANCE = new Factory();
|
static Factory INSTANCE = new Factory();
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
package java.lang.invoke;
|
package java.lang.invoke;
|
||||||
|
|
||||||
|
|
||||||
|
import sun.invoke.util.ValueConversions;
|
||||||
import static java.lang.invoke.MethodHandleStatics.*;
|
import static java.lang.invoke.MethodHandleStatics.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,12 +54,12 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
|||||||
* and the kinds of transformations that apply to it.
|
* and the kinds of transformations that apply to it.
|
||||||
* <p>
|
* <p>
|
||||||
* A method handle contains a pair of special invoker methods
|
* A method handle contains a pair of special invoker methods
|
||||||
* called {@link #invokeExact invokeExact} and {@link #invokeGeneric invokeGeneric}.
|
* called {@link #invokeExact invokeExact} and {@link #invoke invoke}.
|
||||||
* Both invoker methods provide direct access to the method handle's
|
* Both invoker methods provide direct access to the method handle's
|
||||||
* underlying method, constructor, field, or other operation,
|
* underlying method, constructor, field, or other operation,
|
||||||
* as modified by transformations of arguments and return values.
|
* as modified by transformations of arguments and return values.
|
||||||
* Both invokers accept calls which exactly match the method handle's own type.
|
* Both invokers accept calls which exactly match the method handle's own type.
|
||||||
* The {@code invokeGeneric} invoker also accepts a range of other call types.
|
* The plain, inexact invoker also accepts a range of other call types.
|
||||||
* <p>
|
* <p>
|
||||||
* Method handles are immutable and have no visible state.
|
* Method handles are immutable and have no visible state.
|
||||||
* Of course, they can be bound to underlying methods or data which exhibit state.
|
* Of course, they can be bound to underlying methods or data which exhibit state.
|
||||||
@ -76,7 +77,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
|||||||
* may change from time to time or across implementations from different vendors.
|
* may change from time to time or across implementations from different vendors.
|
||||||
*
|
*
|
||||||
* <h3>Method handle compilation</h3>
|
* <h3>Method handle compilation</h3>
|
||||||
* A Java method call expression naming {@code invokeExact} or {@code invokeGeneric}
|
* A Java method call expression naming {@code invokeExact} or {@code invoke}
|
||||||
* can invoke a method handle from Java source code.
|
* can invoke a method handle from Java source code.
|
||||||
* From the viewpoint of source code, these methods can take any arguments
|
* From the viewpoint of source code, these methods can take any arguments
|
||||||
* and their result can be cast to any return type.
|
* and their result can be cast to any return type.
|
||||||
@ -86,7 +87,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
|||||||
* which connects this freedom of invocation directly to the JVM execution stack.
|
* which connects this freedom of invocation directly to the JVM execution stack.
|
||||||
* <p>
|
* <p>
|
||||||
* As is usual with virtual methods, source-level calls to {@code invokeExact}
|
* As is usual with virtual methods, source-level calls to {@code invokeExact}
|
||||||
* and {@code invokeGeneric} compile to an {@code invokevirtual} instruction.
|
* and {@code invoke} compile to an {@code invokevirtual} instruction.
|
||||||
* More unusually, the compiler must record the actual argument types,
|
* More unusually, the compiler must record the actual argument types,
|
||||||
* and may not perform method invocation conversions on the arguments.
|
* and may not perform method invocation conversions on the arguments.
|
||||||
* Instead, it must push them on the stack according to their own unconverted types.
|
* Instead, it must push them on the stack according to their own unconverted types.
|
||||||
@ -109,7 +110,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
|||||||
* The first time a {@code invokevirtual} instruction is executed
|
* The first time a {@code invokevirtual} instruction is executed
|
||||||
* it is linked, by symbolically resolving the names in the instruction
|
* it is linked, by symbolically resolving the names in the instruction
|
||||||
* and verifying that the method call is statically legal.
|
* and verifying that the method call is statically legal.
|
||||||
* This is true of calls to {@code invokeExact} and {@code invokeGeneric}.
|
* This is true of calls to {@code invokeExact} and {@code invoke}.
|
||||||
* In this case, the type descriptor emitted by the compiler is checked for
|
* In this case, the type descriptor emitted by the compiler is checked for
|
||||||
* correct syntax and names it contains are resolved.
|
* correct syntax and names it contains are resolved.
|
||||||
* Thus, an {@code invokevirtual} instruction which invokes
|
* Thus, an {@code invokevirtual} instruction which invokes
|
||||||
@ -127,18 +128,18 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
|||||||
* In the case of {@code invokeExact}, the type descriptor of the invocation
|
* In the case of {@code invokeExact}, the type descriptor of the invocation
|
||||||
* (after resolving symbolic type names) must exactly match the method type
|
* (after resolving symbolic type names) must exactly match the method type
|
||||||
* of the receiving method handle.
|
* of the receiving method handle.
|
||||||
* In the case of {@code invokeGeneric}, the resolved type descriptor
|
* In the case of plain, inexact {@code invoke}, the resolved type descriptor
|
||||||
* must be a valid argument to the receiver's {@link #asType asType} method.
|
* must be a valid argument to the receiver's {@link #asType asType} method.
|
||||||
* Thus, {@code invokeGeneric} is more permissive than {@code invokeExact}.
|
* Thus, plain {@code invoke} is more permissive than {@code invokeExact}.
|
||||||
* <p>
|
* <p>
|
||||||
* After type matching, a call to {@code invokeExact} directly
|
* After type matching, a call to {@code invokeExact} directly
|
||||||
* and immediately invoke the method handle's underlying method
|
* and immediately invoke the method handle's underlying method
|
||||||
* (or other behavior, as the case may be).
|
* (or other behavior, as the case may be).
|
||||||
* <p>
|
* <p>
|
||||||
* A call to {@code invokeGeneric} works the same as a call to
|
* A call to plain {@code invoke} works the same as a call to
|
||||||
* {@code invokeExact}, if the type descriptor specified by the caller
|
* {@code invokeExact}, if the type descriptor specified by the caller
|
||||||
* exactly matches the method handle's own type.
|
* exactly matches the method handle's own type.
|
||||||
* If there is a type mismatch, {@code invokeGeneric} attempts
|
* If there is a type mismatch, {@code invoke} attempts
|
||||||
* to adjust the type of the receiving method handle,
|
* to adjust the type of the receiving method handle,
|
||||||
* as if by a call to {@link #asType asType},
|
* as if by a call to {@link #asType asType},
|
||||||
* to obtain an exactly invokable method handle {@code M2}.
|
* to obtain an exactly invokable method handle {@code M2}.
|
||||||
@ -152,7 +153,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
|||||||
* In typical programs, method handle type matching will usually succeed.
|
* In typical programs, method handle type matching will usually succeed.
|
||||||
* But if a match fails, the JVM will throw a {@link WrongMethodTypeException},
|
* But if a match fails, the JVM will throw a {@link WrongMethodTypeException},
|
||||||
* either directly (in the case of {@code invokeExact}) or indirectly as if
|
* either directly (in the case of {@code invokeExact}) or indirectly as if
|
||||||
* by a failed call to {@code asType} (in the case of {@code invokeGeneric}).
|
* by a failed call to {@code asType} (in the case of {@code invoke}).
|
||||||
* <p>
|
* <p>
|
||||||
* Thus, a method type mismatch which might show up as a linkage error
|
* Thus, a method type mismatch which might show up as a linkage error
|
||||||
* in a statically typed program can show up as
|
* in a statically typed program can show up as
|
||||||
@ -249,8 +250,8 @@ assert(s.equals("savvy"));
|
|||||||
mt = MethodType.methodType(java.util.List.class, Object[].class);
|
mt = MethodType.methodType(java.util.List.class, Object[].class);
|
||||||
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
|
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
|
||||||
assert(mh.isVarargsCollector());
|
assert(mh.isVarargsCollector());
|
||||||
x = mh.invokeGeneric("one", "two");
|
x = mh.invoke("one", "two");
|
||||||
// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
|
// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
|
||||||
assert(x.equals(java.util.Arrays.asList("one","two")));
|
assert(x.equals(java.util.Arrays.asList("one","two")));
|
||||||
// mt is (Object,Object,Object)Object
|
// mt is (Object,Object,Object)Object
|
||||||
mt = MethodType.genericMethodType(3);
|
mt = MethodType.genericMethodType(3);
|
||||||
@ -269,12 +270,12 @@ mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
|
|||||||
mh.invokeExact(System.out, "Hello, world.");
|
mh.invokeExact(System.out, "Hello, world.");
|
||||||
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
|
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
* Each of the above calls to {@code invokeExact} or {@code invokeGeneric}
|
* Each of the above calls to {@code invokeExact} or plain {@code invoke}
|
||||||
* generates a single invokevirtual instruction with
|
* generates a single invokevirtual instruction with
|
||||||
* the type descriptor indicated in the following comment.
|
* the type descriptor indicated in the following comment.
|
||||||
*
|
*
|
||||||
* <h3>Exceptions</h3>
|
* <h3>Exceptions</h3>
|
||||||
* The methods {@code invokeExact} and {@code invokeGeneric} are declared
|
* The methods {@code invokeExact} and {@code invoke} are declared
|
||||||
* to throw {@link java.lang.Throwable Throwable},
|
* to throw {@link java.lang.Throwable Throwable},
|
||||||
* which is to say that there is no static restriction on what a method handle
|
* which is to say that there is no static restriction on what a method handle
|
||||||
* can throw. Since the JVM does not distinguish between checked
|
* can throw. Since the JVM does not distinguish between checked
|
||||||
@ -288,7 +289,7 @@ mh.invokeExact(System.out, "Hello, world.");
|
|||||||
*
|
*
|
||||||
* <h3><a name="sigpoly"></a>Signature polymorphism</h3>
|
* <h3><a name="sigpoly"></a>Signature polymorphism</h3>
|
||||||
* The unusual compilation and linkage behavior of
|
* The unusual compilation and linkage behavior of
|
||||||
* {@code invokeExact} and {@code invokeGeneric}
|
* {@code invokeExact} and plain {@code invoke}
|
||||||
* is referenced by the term <em>signature polymorphism</em>.
|
* is referenced by the term <em>signature polymorphism</em>.
|
||||||
* A signature polymorphic method is one which can operate with
|
* A signature polymorphic method is one which can operate with
|
||||||
* any of a wide range of call signatures and return types.
|
* any of a wide range of call signatures and return types.
|
||||||
@ -322,7 +323,7 @@ mh.invokeExact(System.out, "Hello, world.");
|
|||||||
* The following methods (and no others) are signature polymorphic:
|
* The following methods (and no others) are signature polymorphic:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link java.lang.invoke.MethodHandle#invokeExact MethodHandle.invokeExact}
|
* <li>{@link java.lang.invoke.MethodHandle#invokeExact MethodHandle.invokeExact}
|
||||||
* <li>{@link java.lang.invoke.MethodHandle#invokeGeneric MethodHandle.invokeGeneric}
|
* <li>{@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* A signature polymorphic method will be declared with the following properties:
|
* A signature polymorphic method will be declared with the following properties:
|
||||||
@ -374,24 +375,34 @@ mh.invokeExact(System.out, "Hello, world.");
|
|||||||
* <p>
|
* <p>
|
||||||
* As a special case,
|
* As a special case,
|
||||||
* when the Core Reflection API is used to view the signature polymorphic
|
* when the Core Reflection API is used to view the signature polymorphic
|
||||||
* methods {@code invokeExact} or {@code invokeGeneric} in this class,
|
* methods {@code invokeExact} or plain {@code invoke} in this class,
|
||||||
* they appear as single, non-polymorphic native methods.
|
* they appear as ordinary non-polymorphic methods.
|
||||||
* Calls to these native methods do not result in method handle invocations.
|
* Their reflective appearance, as viewed by
|
||||||
|
* {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod},
|
||||||
|
* is unaffected by their special status in this API.
|
||||||
|
* For example, {@link java.lang.reflect.Method#getModifiers Method.getModifiers}
|
||||||
|
* will report exactly those modifier bits required for any similarly
|
||||||
|
* declared method, including in this case {@code native} and {@code varargs} bits.
|
||||||
|
* <p>
|
||||||
|
* As with any reflected method, these methods (when reflected) may be
|
||||||
|
* invoked via {@link java.lang.reflect.Method#invoke Method.invoke}.
|
||||||
|
* However, such reflective calls do not result in method handle invocations.
|
||||||
|
* Such a call, if passed the required argument
|
||||||
|
* (a single one, of type {@code Object[]}), will ignore the argument and
|
||||||
|
* will throw an {@code UnsupportedOperationException}.
|
||||||
|
* <p>
|
||||||
* Since {@code invokevirtual} instructions can natively
|
* Since {@code invokevirtual} instructions can natively
|
||||||
* invoke method handles under any type descriptor, this reflective view conflicts
|
* invoke method handles under any type descriptor, this reflective view conflicts
|
||||||
* with the normal presentation via bytecodes.
|
* with the normal presentation of these methods via bytecodes.
|
||||||
* Thus, these two native methods, as viewed by
|
* Thus, these two native methods, when reflectively viewed by
|
||||||
* {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod},
|
* {@code Class.getDeclaredMethod}, may be regarded as placeholders only.
|
||||||
* are placeholders only.
|
|
||||||
* If invoked via {@link java.lang.reflect.Method#invoke Method.invoke},
|
|
||||||
* they will throw {@code UnsupportedOperationException}.
|
|
||||||
* <p>
|
* <p>
|
||||||
* In order to obtain an invoker method for a particular type descriptor,
|
* In order to obtain an invoker method for a particular type descriptor,
|
||||||
* use {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker},
|
* use {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker},
|
||||||
* or {@link java.lang.invoke.MethodHandles#genericInvoker MethodHandles.genericInvoker}.
|
* or {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}.
|
||||||
* The {@link java.lang.invoke.MethodHandles.Lookup#findVirtual Lookup.findVirtual}
|
* The {@link java.lang.invoke.MethodHandles.Lookup#findVirtual Lookup.findVirtual}
|
||||||
* API is also able to return a method handle
|
* API is also able to return a method handle
|
||||||
* to call {@code invokeExact} or {@code invokeGeneric},
|
* to call {@code invokeExact} or plain {@code invoke},
|
||||||
* for any specified type descriptor .
|
* for any specified type descriptor .
|
||||||
*
|
*
|
||||||
* <h3>Interoperation between method handles and Java generics</h3>
|
* <h3>Interoperation between method handles and Java generics</h3>
|
||||||
@ -523,7 +534,7 @@ public abstract class MethodHandle {
|
|||||||
* adaptations directly on the caller's arguments,
|
* adaptations directly on the caller's arguments,
|
||||||
* and call the target method handle according to its own exact type.
|
* and call the target method handle according to its own exact type.
|
||||||
* <p>
|
* <p>
|
||||||
* The type descriptor at the call site of {@code invokeGeneric} must
|
* The type descriptor at the call site of {@code invoke} must
|
||||||
* be a valid argument to the receivers {@code asType} method.
|
* be a valid argument to the receivers {@code asType} method.
|
||||||
* In particular, the caller must specify the same argument arity
|
* In particular, the caller must specify the same argument arity
|
||||||
* as the callee's type,
|
* as the callee's type,
|
||||||
@ -539,11 +550,18 @@ public abstract class MethodHandle {
|
|||||||
* @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails
|
* @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails
|
||||||
* @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
|
* @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
|
||||||
*/
|
*/
|
||||||
|
public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <em>Temporary alias</em> for {@link #invoke}, for backward compatibility with some versions of JSR 292.
|
||||||
|
* On some JVMs, support can be excluded by the flags {@code -XX:+UnlockExperimentalVMOptions -XX:-AllowInvokeGeneric}.
|
||||||
|
* @deprecated Will be removed for JSR 292 Proposed Final Draft.
|
||||||
|
*/
|
||||||
public final native @PolymorphicSignature Object invokeGeneric(Object... args) throws Throwable;
|
public final native @PolymorphicSignature Object invokeGeneric(Object... args) throws Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a varargs invocation, passing the arguments in the given array
|
* Performs a varargs invocation, passing the arguments in the given array
|
||||||
* to the method handle, as if via {@link #invokeGeneric invokeGeneric} from a call site
|
* to the method handle, as if via an inexact {@link #invoke invoke} from a call site
|
||||||
* which mentions only the type {@code Object}, and whose arity is the length
|
* which mentions only the type {@code Object}, and whose arity is the length
|
||||||
* of the argument array.
|
* of the argument array.
|
||||||
* <p>
|
* <p>
|
||||||
@ -553,7 +571,7 @@ public abstract class MethodHandle {
|
|||||||
* <ul>
|
* <ul>
|
||||||
* <li>Determine the length of the argument array as {@code N}.
|
* <li>Determine the length of the argument array as {@code N}.
|
||||||
* For a null reference, {@code N=0}. </li>
|
* For a null reference, {@code N=0}. </li>
|
||||||
* <li>Determine the generic type {@code TN} of {@code N} arguments as
|
* <li>Determine the general type {@code TN} of {@code N} arguments as
|
||||||
* as {@code TN=MethodType.genericMethodType(N)}.</li>
|
* as {@code TN=MethodType.genericMethodType(N)}.</li>
|
||||||
* <li>Force the original target method handle {@code MH0} to the
|
* <li>Force the original target method handle {@code MH0} to the
|
||||||
* required type, as {@code MH1 = MH0.asType(TN)}. </li>
|
* required type, as {@code MH1 = MH0.asType(TN)}. </li>
|
||||||
@ -580,7 +598,7 @@ public abstract class MethodHandle {
|
|||||||
* Object result = invoker.invokeExact(this, arguments);
|
* Object result = invoker.invokeExact(this, arguments);
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
* <p>
|
* <p>
|
||||||
* Unlike the signature polymorphic methods {@code invokeExact} and {@code invokeGeneric},
|
* Unlike the signature polymorphic methods {@code invokeExact} and {@code invoke},
|
||||||
* {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI.
|
* {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI.
|
||||||
* It can therefore be used as a bridge between native or reflective code and method handles.
|
* It can therefore be used as a bridge between native or reflective code and method handles.
|
||||||
*
|
*
|
||||||
@ -595,11 +613,11 @@ public abstract class MethodHandle {
|
|||||||
int argc = arguments == null ? 0 : arguments.length;
|
int argc = arguments == null ? 0 : arguments.length;
|
||||||
MethodType type = type();
|
MethodType type = type();
|
||||||
if (type.parameterCount() != argc) {
|
if (type.parameterCount() != argc) {
|
||||||
// simulate invokeGeneric
|
// simulate invoke
|
||||||
return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments);
|
return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments);
|
||||||
}
|
}
|
||||||
if (argc <= 10) {
|
if (argc <= 10) {
|
||||||
MethodHandle invoker = type.invokers().genericInvoker();
|
MethodHandle invoker = type.invokers().generalInvoker();
|
||||||
switch (argc) {
|
switch (argc) {
|
||||||
case 0: return invoker.invokeExact(this);
|
case 0: return invoker.invokeExact(this);
|
||||||
case 1: return invoker.invokeExact(this,
|
case 1: return invoker.invokeExact(this,
|
||||||
@ -644,7 +662,7 @@ public abstract class MethodHandle {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a varargs invocation, passing the arguments in the given array
|
* Performs a varargs invocation, passing the arguments in the given array
|
||||||
* to the method handle, as if via {@link #invokeGeneric invokeGeneric} from a call site
|
* to the method handle, as if via an inexact {@link #invoke invoke} from a call site
|
||||||
* which mentions only the type {@code Object}, and whose arity is the length
|
* which mentions only the type {@code Object}, and whose arity is the length
|
||||||
* of the argument array.
|
* of the argument array.
|
||||||
* <p>
|
* <p>
|
||||||
@ -672,9 +690,9 @@ public abstract class MethodHandle {
|
|||||||
* If the original type and new type are equal, returns {@code this}.
|
* If the original type and new type are equal, returns {@code this}.
|
||||||
* <p>
|
* <p>
|
||||||
* This method provides the crucial behavioral difference between
|
* This method provides the crucial behavioral difference between
|
||||||
* {@link #invokeExact invokeExact} and {@link #invokeGeneric invokeGeneric}. The two methods
|
* {@link #invokeExact invokeExact} and plain, inexact {@link #invoke invoke}. The two methods
|
||||||
* perform the same steps when the caller's type descriptor is identical
|
* perform the same steps when the caller's type descriptor is identical
|
||||||
* with the callee's, but when the types differ, {@link #invokeGeneric invokeGeneric}
|
* with the callee's, but when the types differ, plain {@link #invoke invoke}
|
||||||
* also calls {@code asType} (or some internal equivalent) in order
|
* also calls {@code asType} (or some internal equivalent) in order
|
||||||
* to match up the caller's and callee's types.
|
* to match up the caller's and callee's types.
|
||||||
* <p>
|
* <p>
|
||||||
@ -689,6 +707,9 @@ public abstract class MethodHandle {
|
|||||||
* @see MethodHandles#convertArguments
|
* @see MethodHandles#convertArguments
|
||||||
*/
|
*/
|
||||||
public MethodHandle asType(MethodType newType) {
|
public MethodHandle asType(MethodType newType) {
|
||||||
|
if (!type.isConvertibleTo(newType)) {
|
||||||
|
throw new WrongMethodTypeException("cannot convert "+type+" to "+newType);
|
||||||
|
}
|
||||||
return MethodHandles.convertArguments(this, newType);
|
return MethodHandles.convertArguments(this, newType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -731,13 +752,9 @@ public abstract class MethodHandle {
|
|||||||
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
|
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
|
||||||
Class<?> arrayElement = arrayType.getComponentType();
|
Class<?> arrayElement = arrayType.getComponentType();
|
||||||
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
|
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
|
||||||
MethodType oldType = type();
|
int nargs = type().parameterCount();
|
||||||
int nargs = oldType.parameterCount();
|
|
||||||
if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length");
|
if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length");
|
||||||
int keepPosArgs = nargs - arrayLength;
|
return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
|
||||||
MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs);
|
|
||||||
newType = newType.insertParameterTypes(keepPosArgs, arrayType);
|
|
||||||
return MethodHandles.spreadArguments(this, newType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -780,15 +797,18 @@ public abstract class MethodHandle {
|
|||||||
* @see #asVarargsCollector
|
* @see #asVarargsCollector
|
||||||
*/
|
*/
|
||||||
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
|
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
|
||||||
|
asCollectorChecks(arrayType, arrayLength);
|
||||||
|
MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
|
||||||
|
return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void asCollectorChecks(Class<?> arrayType, int arrayLength) {
|
||||||
Class<?> arrayElement = arrayType.getComponentType();
|
Class<?> arrayElement = arrayType.getComponentType();
|
||||||
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
|
if (arrayElement == null)
|
||||||
MethodType oldType = type();
|
throw newIllegalArgumentException("not an array type", arrayType);
|
||||||
int nargs = oldType.parameterCount();
|
int nargs = type().parameterCount();
|
||||||
if (nargs == 0) throw newIllegalArgumentException("no trailing argument");
|
if (nargs == 0 || !type().parameterType(nargs-1).isAssignableFrom(arrayType))
|
||||||
MethodType newType = oldType.dropParameterTypes(nargs-1, nargs);
|
throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
|
||||||
newType = newType.insertParameterTypes(nargs-1,
|
|
||||||
java.util.Collections.<Class<?>>nCopies(arrayLength, arrayElement));
|
|
||||||
return MethodHandles.collectArguments(this, newType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -798,7 +818,7 @@ public abstract class MethodHandle {
|
|||||||
* <p>
|
* <p>
|
||||||
* The type and behavior of the adapter will be the same as
|
* The type and behavior of the adapter will be the same as
|
||||||
* the type and behavior of the target, except that certain
|
* the type and behavior of the target, except that certain
|
||||||
* {@code invokeGeneric} and {@code asType} requests can lead to
|
* {@code invoke} and {@code asType} requests can lead to
|
||||||
* trailing positional arguments being collected into target's
|
* trailing positional arguments being collected into target's
|
||||||
* trailing parameter.
|
* trailing parameter.
|
||||||
* Also, the last parameter type of the adapter will be
|
* Also, the last parameter type of the adapter will be
|
||||||
@ -812,17 +832,17 @@ public abstract class MethodHandle {
|
|||||||
* since it accepts a whole array of indeterminate length,
|
* since it accepts a whole array of indeterminate length,
|
||||||
* rather than a fixed number of arguments.)
|
* rather than a fixed number of arguments.)
|
||||||
* <p>
|
* <p>
|
||||||
* When called with {@link #invokeGeneric invokeGeneric}, if the caller
|
* When called with plain, inexact {@link #invoke invoke}, if the caller
|
||||||
* type is the same as the adapter, the adapter invokes the target as with
|
* type is the same as the adapter, the adapter invokes the target as with
|
||||||
* {@code invokeExact}.
|
* {@code invokeExact}.
|
||||||
* (This is the normal behavior for {@code invokeGeneric} when types match.)
|
* (This is the normal behavior for {@code invoke} when types match.)
|
||||||
* <p>
|
* <p>
|
||||||
* Otherwise, if the caller and adapter arity are the same, and the
|
* Otherwise, if the caller and adapter arity are the same, and the
|
||||||
* trailing parameter type of the caller is a reference type identical to
|
* trailing parameter type of the caller is a reference type identical to
|
||||||
* or assignable to the trailing parameter type of the adapter,
|
* or assignable to the trailing parameter type of the adapter,
|
||||||
* the arguments and return values are converted pairwise,
|
* the arguments and return values are converted pairwise,
|
||||||
* as if by {@link MethodHandles#convertArguments convertArguments}.
|
* as if by {@link MethodHandles#convertArguments convertArguments}.
|
||||||
* (This is also normal behavior for {@code invokeGeneric} in such a case.)
|
* (This is also normal behavior for {@code invoke} in such a case.)
|
||||||
* <p>
|
* <p>
|
||||||
* Otherwise, the arities differ, or the adapter's trailing parameter
|
* Otherwise, the arities differ, or the adapter's trailing parameter
|
||||||
* type is not assignable from the corresponding caller type.
|
* type is not assignable from the corresponding caller type.
|
||||||
@ -838,7 +858,7 @@ public abstract class MethodHandle {
|
|||||||
* where {@code N} is the arity of the target.
|
* where {@code N} is the arity of the target.
|
||||||
* Also, there must exist conversions from the incoming arguments
|
* Also, there must exist conversions from the incoming arguments
|
||||||
* to the target's arguments.
|
* to the target's arguments.
|
||||||
* As with other uses of {@code invokeGeneric}, if these basic
|
* As with other uses of plain {@code invoke}, if these basic
|
||||||
* requirements are not fulfilled, a {@code WrongMethodTypeException}
|
* requirements are not fulfilled, a {@code WrongMethodTypeException}
|
||||||
* may be thrown.
|
* may be thrown.
|
||||||
* <p>
|
* <p>
|
||||||
@ -856,7 +876,7 @@ public abstract class MethodHandle {
|
|||||||
* <p>
|
* <p>
|
||||||
* The behavior of {@link #asType asType} is also specialized for
|
* The behavior of {@link #asType asType} is also specialized for
|
||||||
* variable arity adapters, to maintain the invariant that
|
* variable arity adapters, to maintain the invariant that
|
||||||
* {@code invokeGeneric} is always equivalent to an {@code asType}
|
* plain, inexact {@code invoke} is always equivalent to an {@code asType}
|
||||||
* call to adjust the target type, followed by {@code invokeExact}.
|
* call to adjust the target type, followed by {@code invokeExact}.
|
||||||
* Therefore, a variable arity adapter responds
|
* Therefore, a variable arity adapter responds
|
||||||
* to an {@code asType} request by building a fixed arity collector,
|
* to an {@code asType} request by building a fixed arity collector,
|
||||||
@ -893,12 +913,12 @@ public abstract class MethodHandle {
|
|||||||
MethodHandle asList = publicLookup()
|
MethodHandle asList = publicLookup()
|
||||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
||||||
.asVarargsCollector(Object[].class);
|
.asVarargsCollector(Object[].class);
|
||||||
assertEquals("[]", asList.invokeGeneric().toString());
|
assertEquals("[]", asList.invoke().toString());
|
||||||
assertEquals("[1]", asList.invokeGeneric(1).toString());
|
assertEquals("[1]", asList.invoke(1).toString());
|
||||||
assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
|
assertEquals("[two, too]", asList.invoke("two", "too").toString());
|
||||||
Object[] argv = { "three", "thee", "tee" };
|
Object[] argv = { "three", "thee", "tee" };
|
||||||
assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
|
assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
|
||||||
List ls = (List) asList.invokeGeneric((Object)argv);
|
List ls = (List) asList.invoke((Object)argv);
|
||||||
assertEquals(1, ls.size());
|
assertEquals(1, ls.size());
|
||||||
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
@ -926,9 +946,9 @@ MethodHandle vamh = publicLookup()
|
|||||||
.asVarargsCollector(Object[].class);
|
.asVarargsCollector(Object[].class);
|
||||||
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
|
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
|
||||||
assert(vamh.type().equals(mh.type()));
|
assert(vamh.type().equals(mh.type()));
|
||||||
assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
|
assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString());
|
||||||
boolean failed = false;
|
boolean failed = false;
|
||||||
try { mh.invokeGeneric(1,2,3); }
|
try { mh.invoke(1,2,3); }
|
||||||
catch (WrongMethodTypeException ex) { failed = true; }
|
catch (WrongMethodTypeException ex) { failed = true; }
|
||||||
assert(failed);
|
assert(failed);
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
@ -960,7 +980,7 @@ assert(failed);
|
|||||||
* <li>an {@code ldc} instruction of a {@code CONSTANT_MethodHandle}
|
* <li>an {@code ldc} instruction of a {@code CONSTANT_MethodHandle}
|
||||||
* which resolves to a variable arity Java method or constructor
|
* which resolves to a variable arity Java method or constructor
|
||||||
* </ul>
|
* </ul>
|
||||||
* @return true if this method handle accepts more than one arity of {@code invokeGeneric} calls
|
* @return true if this method handle accepts more than one arity of plain, inexact {@code invoke} calls
|
||||||
* @see #asVarargsCollector
|
* @see #asVarargsCollector
|
||||||
*/
|
*/
|
||||||
public boolean isVarargsCollector() {
|
public boolean isVarargsCollector() {
|
||||||
|
@ -121,11 +121,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
if (nargs < INVOKES.length) {
|
if (nargs < INVOKES.length) {
|
||||||
MethodHandle invoke = INVOKES[nargs];
|
MethodHandle invoke = INVOKES[nargs];
|
||||||
MethodType conType = CON_TYPES[nargs];
|
MethodType conType = CON_TYPES[nargs];
|
||||||
MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, null);
|
MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, 0);
|
||||||
if (gcon == null) return null;
|
if (gcon == null) return null;
|
||||||
MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon);
|
MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon);
|
||||||
assert(galloc.type() == newType.generic());
|
assert(galloc.type() == newType.generic());
|
||||||
return convertArguments(galloc, newType, galloc.type(), null);
|
return convertArguments(galloc, newType, galloc.type(), 0);
|
||||||
} else {
|
} else {
|
||||||
MethodHandle invoke = VARARGS_INVOKE;
|
MethodHandle invoke = VARARGS_INVOKE;
|
||||||
MethodType conType = CON_TYPES[nargs];
|
MethodType conType = CON_TYPES[nargs];
|
||||||
@ -256,8 +256,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
FieldAccessor.ahandle(arrayClass, true)
|
FieldAccessor.ahandle(arrayClass, true)
|
||||||
};
|
};
|
||||||
if (mhs[0].type().parameterType(0) == Class.class) {
|
if (mhs[0].type().parameterType(0) == Class.class) {
|
||||||
mhs[0] = MethodHandles.insertArguments(mhs[0], 0, elemClass);
|
mhs[0] = mhs[0].bindTo(elemClass);
|
||||||
mhs[1] = MethodHandles.insertArguments(mhs[1], 0, elemClass);
|
mhs[1] = mhs[1].bindTo(elemClass);
|
||||||
}
|
}
|
||||||
synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier
|
synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier
|
||||||
FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
|
FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
|
||||||
@ -372,7 +372,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
if (evclass != vclass || (!isStatic && ecclass != cclass)) {
|
if (evclass != vclass || (!isStatic && ecclass != cclass)) {
|
||||||
MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
|
MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
|
||||||
strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
|
strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
|
||||||
mh = MethodHandles.convertArguments(mh, strongType);
|
mh = convertArguments(mh, strongType, 0);
|
||||||
}
|
}
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
@ -439,8 +439,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
}
|
}
|
||||||
if (caclass != null) {
|
if (caclass != null) {
|
||||||
MethodType strongType = FieldAccessor.atype(caclass, isSetter);
|
MethodType strongType = FieldAccessor.atype(caclass, isSetter);
|
||||||
mh = MethodHandles.insertArguments(mh, 0, caclass);
|
mh = mh.bindTo(caclass);
|
||||||
mh = MethodHandles.convertArguments(mh, strongType);
|
mh = convertArguments(mh, strongType, 0);
|
||||||
}
|
}
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
@ -465,7 +465,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) {
|
dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) {
|
||||||
MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0);
|
MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0);
|
||||||
MethodType newType = target.type().dropParameterTypes(0, 1);
|
MethodType newType = target.type().dropParameterTypes(0, 1);
|
||||||
return convertArguments(bmh, newType, bmh.type(), null);
|
return convertArguments(bmh, newType, bmh.type(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -486,301 +486,378 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
return new BoundMethodHandle(target, receiver, argnum);
|
return new BoundMethodHandle(target, receiver, argnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
static MethodHandle convertArguments(MethodHandle target,
|
static MethodHandle permuteArguments(MethodHandle target,
|
||||||
MethodType newType,
|
MethodType newType,
|
||||||
MethodType oldType,
|
MethodType oldType,
|
||||||
int[] permutationOrNull) {
|
int[] permutationOrNull) {
|
||||||
assert(oldType.parameterCount() == target.type().parameterCount());
|
assert(oldType.parameterCount() == target.type().parameterCount());
|
||||||
if (permutationOrNull != null) {
|
int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
|
||||||
int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
|
if (permutationOrNull.length != outargs)
|
||||||
if (permutationOrNull.length != outargs)
|
throw newIllegalArgumentException("wrong number of arguments in permutation");
|
||||||
throw newIllegalArgumentException("wrong number of arguments in permutation");
|
// Make the individual outgoing argument types match up first.
|
||||||
// Make the individual outgoing argument types match up first.
|
Class<?>[] callTypeArgs = new Class<?>[outargs];
|
||||||
Class<?>[] callTypeArgs = new Class<?>[outargs];
|
for (int i = 0; i < outargs; i++)
|
||||||
for (int i = 0; i < outargs; i++)
|
callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
|
||||||
callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
|
MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
|
||||||
MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
|
target = convertArguments(target, callType, oldType, 0);
|
||||||
target = convertArguments(target, callType, oldType, null);
|
assert(target != null);
|
||||||
assert(target != null);
|
oldType = target.type();
|
||||||
oldType = target.type();
|
List<Integer> goal = new ArrayList<Integer>(); // i*TOKEN
|
||||||
List<Integer> goal = new ArrayList<Integer>(); // i*TOKEN
|
List<Integer> state = new ArrayList<Integer>(); // i*TOKEN
|
||||||
List<Integer> state = new ArrayList<Integer>(); // i*TOKEN
|
List<Integer> drops = new ArrayList<Integer>(); // not tokens
|
||||||
List<Integer> drops = new ArrayList<Integer>(); // not tokens
|
List<Integer> dups = new ArrayList<Integer>(); // not tokens
|
||||||
List<Integer> dups = new ArrayList<Integer>(); // not tokens
|
final int TOKEN = 10; // to mark items which are symbolic only
|
||||||
final int TOKEN = 10; // to mark items which are symbolic only
|
// state represents the argument values coming into target
|
||||||
// state represents the argument values coming into target
|
for (int i = 0; i < outargs; i++) {
|
||||||
for (int i = 0; i < outargs; i++) {
|
state.add(permutationOrNull[i] * TOKEN);
|
||||||
state.add(permutationOrNull[i] * TOKEN);
|
}
|
||||||
}
|
// goal represents the desired state
|
||||||
// goal represents the desired state
|
for (int i = 0; i < inargs; i++) {
|
||||||
for (int i = 0; i < inargs; i++) {
|
if (state.contains(i * TOKEN)) {
|
||||||
if (state.contains(i * TOKEN)) {
|
goal.add(i * TOKEN);
|
||||||
goal.add(i * TOKEN);
|
} else {
|
||||||
} else {
|
// adapter must initially drop all unused arguments
|
||||||
// adapter must initially drop all unused arguments
|
drops.add(i);
|
||||||
drops.add(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// detect duplications
|
|
||||||
while (state.size() > goal.size()) {
|
|
||||||
for (int i2 = 0; i2 < state.size(); i2++) {
|
|
||||||
int arg1 = state.get(i2);
|
|
||||||
int i1 = state.indexOf(arg1);
|
|
||||||
if (i1 != i2) {
|
|
||||||
// found duplicate occurrence at i2
|
|
||||||
int arg2 = (inargs++) * TOKEN;
|
|
||||||
state.set(i2, arg2);
|
|
||||||
dups.add(goal.indexOf(arg1));
|
|
||||||
goal.add(arg2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(state.size() == goal.size());
|
|
||||||
int size = goal.size();
|
|
||||||
while (!state.equals(goal)) {
|
|
||||||
// Look for a maximal sequence of adjacent misplaced arguments,
|
|
||||||
// and try to rotate them into place.
|
|
||||||
int bestRotArg = -10 * TOKEN, bestRotLen = 0;
|
|
||||||
int thisRotArg = -10 * TOKEN, thisRotLen = 0;
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
int arg = state.get(i);
|
|
||||||
// Does this argument match the current run?
|
|
||||||
if (arg == thisRotArg + TOKEN) {
|
|
||||||
thisRotArg = arg;
|
|
||||||
thisRotLen += 1;
|
|
||||||
if (bestRotLen < thisRotLen) {
|
|
||||||
bestRotLen = thisRotLen;
|
|
||||||
bestRotArg = thisRotArg;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The old sequence (if any) stops here.
|
|
||||||
thisRotLen = 0;
|
|
||||||
thisRotArg = -10 * TOKEN;
|
|
||||||
// But maybe a new one starts here also.
|
|
||||||
int wantArg = goal.get(i);
|
|
||||||
final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION;
|
|
||||||
if (arg != wantArg &&
|
|
||||||
arg >= wantArg - TOKEN * MAX_ARG_ROTATION &&
|
|
||||||
arg <= wantArg + TOKEN * MAX_ARG_ROTATION) {
|
|
||||||
thisRotArg = arg;
|
|
||||||
thisRotLen = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bestRotLen >= 2) {
|
|
||||||
// Do a rotation if it can improve argument positioning
|
|
||||||
// by at least 2 arguments. This is not always optimal,
|
|
||||||
// but it seems to catch common cases.
|
|
||||||
int dstEnd = state.indexOf(bestRotArg);
|
|
||||||
int srcEnd = goal.indexOf(bestRotArg);
|
|
||||||
int rotBy = dstEnd - srcEnd;
|
|
||||||
int dstBeg = dstEnd - (bestRotLen - 1);
|
|
||||||
int srcBeg = srcEnd - (bestRotLen - 1);
|
|
||||||
assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs
|
|
||||||
// Make a span which covers both source and destination.
|
|
||||||
int rotBeg = Math.min(dstBeg, srcBeg);
|
|
||||||
int rotEnd = Math.max(dstEnd, srcEnd);
|
|
||||||
int score = 0;
|
|
||||||
for (int i = rotBeg; i <= rotEnd; i++) {
|
|
||||||
if ((int)state.get(i) != (int)goal.get(i))
|
|
||||||
score += 1;
|
|
||||||
}
|
|
||||||
List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1);
|
|
||||||
Collections.rotate(rotSpan, -rotBy); // reverse direction
|
|
||||||
for (int i = rotBeg; i <= rotEnd; i++) {
|
|
||||||
if ((int)state.get(i) != (int)goal.get(i))
|
|
||||||
score -= 1;
|
|
||||||
}
|
|
||||||
if (score >= 2) {
|
|
||||||
// Improved at least two argument positions. Do it.
|
|
||||||
List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
|
|
||||||
Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy);
|
|
||||||
MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes);
|
|
||||||
MethodHandle nextTarget
|
|
||||||
= AdapterMethodHandle.makeRotateArguments(rotType, target,
|
|
||||||
rotBeg, rotSpan.size(), rotBy);
|
|
||||||
if (nextTarget != null) {
|
|
||||||
//System.out.println("Rot: "+rotSpan+" by "+rotBy);
|
|
||||||
target = nextTarget;
|
|
||||||
oldType = rotType;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Else de-rotate, and drop through to the swap-fest.
|
|
||||||
Collections.rotate(rotSpan, rotBy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now swap like the wind!
|
|
||||||
List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
// What argument do I want here?
|
|
||||||
int arg = goal.get(i);
|
|
||||||
if (arg != state.get(i)) {
|
|
||||||
// Where is it now?
|
|
||||||
int j = state.indexOf(arg);
|
|
||||||
Collections.swap(ptypes, i, j);
|
|
||||||
MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes);
|
|
||||||
target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j);
|
|
||||||
if (target == null) throw newIllegalArgumentException("cannot swap");
|
|
||||||
assert(target.type() == swapType);
|
|
||||||
oldType = swapType;
|
|
||||||
Collections.swap(state, i, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// One pass of swapping must finish the job.
|
|
||||||
assert(state.equals(goal));
|
|
||||||
}
|
|
||||||
while (!dups.isEmpty()) {
|
|
||||||
// Grab a contiguous trailing sequence of dups.
|
|
||||||
int grab = dups.size() - 1;
|
|
||||||
int dupArgPos = dups.get(grab), dupArgCount = 1;
|
|
||||||
while (grab - 1 >= 0) {
|
|
||||||
int dup0 = dups.get(grab - 1);
|
|
||||||
if (dup0 != dupArgPos - 1) break;
|
|
||||||
dupArgPos -= 1;
|
|
||||||
dupArgCount += 1;
|
|
||||||
grab -= 1;
|
|
||||||
}
|
|
||||||
//if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size()));
|
|
||||||
dups.subList(grab, dups.size()).clear();
|
|
||||||
// In the new target type drop that many args from the tail:
|
|
||||||
List<Class<?>> ptypes = oldType.parameterList();
|
|
||||||
ptypes = ptypes.subList(0, ptypes.size() - dupArgCount);
|
|
||||||
MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes);
|
|
||||||
target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount);
|
|
||||||
if (target == null)
|
|
||||||
throw newIllegalArgumentException("cannot dup");
|
|
||||||
oldType = target.type();
|
|
||||||
}
|
|
||||||
while (!drops.isEmpty()) {
|
|
||||||
// Grab a contiguous initial sequence of drops.
|
|
||||||
int dropArgPos = drops.get(0), dropArgCount = 1;
|
|
||||||
while (dropArgCount < drops.size()) {
|
|
||||||
int drop1 = drops.get(dropArgCount);
|
|
||||||
if (drop1 != dropArgPos + dropArgCount) break;
|
|
||||||
dropArgCount += 1;
|
|
||||||
}
|
|
||||||
//if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount));
|
|
||||||
drops.subList(0, dropArgCount).clear();
|
|
||||||
List<Class<?>> dropTypes = newType.parameterList()
|
|
||||||
.subList(dropArgPos, dropArgPos + dropArgCount);
|
|
||||||
MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes);
|
|
||||||
target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount);
|
|
||||||
if (target == null) throw newIllegalArgumentException("cannot drop");
|
|
||||||
oldType = target.type();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// detect duplications
|
||||||
|
while (state.size() > goal.size()) {
|
||||||
|
for (int i2 = 0; i2 < state.size(); i2++) {
|
||||||
|
int arg1 = state.get(i2);
|
||||||
|
int i1 = state.indexOf(arg1);
|
||||||
|
if (i1 != i2) {
|
||||||
|
// found duplicate occurrence at i2
|
||||||
|
int arg2 = (inargs++) * TOKEN;
|
||||||
|
state.set(i2, arg2);
|
||||||
|
dups.add(goal.indexOf(arg1));
|
||||||
|
goal.add(arg2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(state.size() == goal.size());
|
||||||
|
int size = goal.size();
|
||||||
|
while (!state.equals(goal)) {
|
||||||
|
// Look for a maximal sequence of adjacent misplaced arguments,
|
||||||
|
// and try to rotate them into place.
|
||||||
|
int bestRotArg = -10 * TOKEN, bestRotLen = 0;
|
||||||
|
int thisRotArg = -10 * TOKEN, thisRotLen = 0;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
int arg = state.get(i);
|
||||||
|
// Does this argument match the current run?
|
||||||
|
if (arg == thisRotArg + TOKEN) {
|
||||||
|
thisRotArg = arg;
|
||||||
|
thisRotLen += 1;
|
||||||
|
if (bestRotLen < thisRotLen) {
|
||||||
|
bestRotLen = thisRotLen;
|
||||||
|
bestRotArg = thisRotArg;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The old sequence (if any) stops here.
|
||||||
|
thisRotLen = 0;
|
||||||
|
thisRotArg = -10 * TOKEN;
|
||||||
|
// But maybe a new one starts here also.
|
||||||
|
int wantArg = goal.get(i);
|
||||||
|
final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION;
|
||||||
|
if (arg != wantArg &&
|
||||||
|
arg >= wantArg - TOKEN * MAX_ARG_ROTATION &&
|
||||||
|
arg <= wantArg + TOKEN * MAX_ARG_ROTATION) {
|
||||||
|
thisRotArg = arg;
|
||||||
|
thisRotLen = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestRotLen >= 2) {
|
||||||
|
// Do a rotation if it can improve argument positioning
|
||||||
|
// by at least 2 arguments. This is not always optimal,
|
||||||
|
// but it seems to catch common cases.
|
||||||
|
int dstEnd = state.indexOf(bestRotArg);
|
||||||
|
int srcEnd = goal.indexOf(bestRotArg);
|
||||||
|
int rotBy = dstEnd - srcEnd;
|
||||||
|
int dstBeg = dstEnd - (bestRotLen - 1);
|
||||||
|
int srcBeg = srcEnd - (bestRotLen - 1);
|
||||||
|
assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs
|
||||||
|
// Make a span which covers both source and destination.
|
||||||
|
int rotBeg = Math.min(dstBeg, srcBeg);
|
||||||
|
int rotEnd = Math.max(dstEnd, srcEnd);
|
||||||
|
int score = 0;
|
||||||
|
for (int i = rotBeg; i <= rotEnd; i++) {
|
||||||
|
if ((int)state.get(i) != (int)goal.get(i))
|
||||||
|
score += 1;
|
||||||
|
}
|
||||||
|
List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1);
|
||||||
|
Collections.rotate(rotSpan, -rotBy); // reverse direction
|
||||||
|
for (int i = rotBeg; i <= rotEnd; i++) {
|
||||||
|
if ((int)state.get(i) != (int)goal.get(i))
|
||||||
|
score -= 1;
|
||||||
|
}
|
||||||
|
if (score >= 2) {
|
||||||
|
// Improved at least two argument positions. Do it.
|
||||||
|
List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
|
||||||
|
Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy);
|
||||||
|
MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes);
|
||||||
|
MethodHandle nextTarget
|
||||||
|
= AdapterMethodHandle.makeRotateArguments(rotType, target,
|
||||||
|
rotBeg, rotSpan.size(), rotBy);
|
||||||
|
if (nextTarget != null) {
|
||||||
|
//System.out.println("Rot: "+rotSpan+" by "+rotBy);
|
||||||
|
target = nextTarget;
|
||||||
|
oldType = rotType;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Else de-rotate, and drop through to the swap-fest.
|
||||||
|
Collections.rotate(rotSpan, rotBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now swap like the wind!
|
||||||
|
List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
// What argument do I want here?
|
||||||
|
int arg = goal.get(i);
|
||||||
|
if (arg != state.get(i)) {
|
||||||
|
// Where is it now?
|
||||||
|
int j = state.indexOf(arg);
|
||||||
|
Collections.swap(ptypes, i, j);
|
||||||
|
MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes);
|
||||||
|
target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j);
|
||||||
|
if (target == null) throw newIllegalArgumentException("cannot swap");
|
||||||
|
assert(target.type() == swapType);
|
||||||
|
oldType = swapType;
|
||||||
|
Collections.swap(state, i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// One pass of swapping must finish the job.
|
||||||
|
assert(state.equals(goal));
|
||||||
|
}
|
||||||
|
while (!dups.isEmpty()) {
|
||||||
|
// Grab a contiguous trailing sequence of dups.
|
||||||
|
int grab = dups.size() - 1;
|
||||||
|
int dupArgPos = dups.get(grab), dupArgCount = 1;
|
||||||
|
while (grab - 1 >= 0) {
|
||||||
|
int dup0 = dups.get(grab - 1);
|
||||||
|
if (dup0 != dupArgPos - 1) break;
|
||||||
|
dupArgPos -= 1;
|
||||||
|
dupArgCount += 1;
|
||||||
|
grab -= 1;
|
||||||
|
}
|
||||||
|
//if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size()));
|
||||||
|
dups.subList(grab, dups.size()).clear();
|
||||||
|
// In the new target type drop that many args from the tail:
|
||||||
|
List<Class<?>> ptypes = oldType.parameterList();
|
||||||
|
ptypes = ptypes.subList(0, ptypes.size() - dupArgCount);
|
||||||
|
MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes);
|
||||||
|
target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount);
|
||||||
|
if (target == null)
|
||||||
|
throw newIllegalArgumentException("cannot dup");
|
||||||
|
oldType = target.type();
|
||||||
|
}
|
||||||
|
while (!drops.isEmpty()) {
|
||||||
|
// Grab a contiguous initial sequence of drops.
|
||||||
|
int dropArgPos = drops.get(0), dropArgCount = 1;
|
||||||
|
while (dropArgCount < drops.size()) {
|
||||||
|
int drop1 = drops.get(dropArgCount);
|
||||||
|
if (drop1 != dropArgPos + dropArgCount) break;
|
||||||
|
dropArgCount += 1;
|
||||||
|
}
|
||||||
|
//if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount));
|
||||||
|
drops.subList(0, dropArgCount).clear();
|
||||||
|
List<Class<?>> dropTypes = newType.parameterList()
|
||||||
|
.subList(dropArgPos, dropArgPos + dropArgCount);
|
||||||
|
MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes);
|
||||||
|
target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount);
|
||||||
|
if (target == null) throw newIllegalArgumentException("cannot drop");
|
||||||
|
oldType = target.type();
|
||||||
|
}
|
||||||
|
return convertArguments(target, newType, oldType, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*non-public*/ static
|
||||||
|
MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) {
|
||||||
|
MethodType oldType = target.type();
|
||||||
|
if (oldType.equals(newType))
|
||||||
|
return target;
|
||||||
|
assert(level > 1 || oldType.isConvertibleTo(newType));
|
||||||
|
MethodHandle retFilter = null;
|
||||||
|
Class<?> oldRT = oldType.returnType();
|
||||||
|
Class<?> newRT = newType.returnType();
|
||||||
|
if (!VerifyType.isNullConversion(oldRT, newRT)) {
|
||||||
|
if (oldRT == void.class) {
|
||||||
|
Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT;
|
||||||
|
retFilter = ValueConversions.zeroConstantFunction(wrap);
|
||||||
|
} else {
|
||||||
|
retFilter = MethodHandles.identity(newRT);
|
||||||
|
retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level);
|
||||||
|
}
|
||||||
|
newType = newType.changeReturnType(oldRT);
|
||||||
|
}
|
||||||
|
MethodHandle res = null;
|
||||||
|
Exception ex = null;
|
||||||
|
try {
|
||||||
|
res = convertArguments(target, newType, oldType, level);
|
||||||
|
} catch (IllegalArgumentException ex1) {
|
||||||
|
ex = ex1;
|
||||||
|
}
|
||||||
|
if (res == null) {
|
||||||
|
WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target);
|
||||||
|
wmt.initCause(ex);
|
||||||
|
throw wmt;
|
||||||
|
}
|
||||||
|
if (retFilter != null)
|
||||||
|
res = MethodHandles.filterReturnValue(res, retFilter);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MethodHandle convertArguments(MethodHandle target,
|
||||||
|
MethodType newType,
|
||||||
|
MethodType oldType,
|
||||||
|
int level) {
|
||||||
|
assert(oldType.parameterCount() == target.type().parameterCount());
|
||||||
if (newType == oldType)
|
if (newType == oldType)
|
||||||
return target;
|
return target;
|
||||||
if (oldType.parameterCount() != newType.parameterCount())
|
if (oldType.parameterCount() != newType.parameterCount())
|
||||||
throw newIllegalArgumentException("mismatched parameter count");
|
throw newIllegalArgumentException("mismatched parameter count", oldType, newType);
|
||||||
MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target);
|
MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target, level);
|
||||||
if (res != null)
|
if (res != null)
|
||||||
return res;
|
return res;
|
||||||
|
// We can come here in the case of target(int)void => (Object)void,
|
||||||
|
// because the unboxing logic for Object => int is complex.
|
||||||
int argc = oldType.parameterCount();
|
int argc = oldType.parameterCount();
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||||
// The JVM can't do it directly, so fill in the gap with a Java adapter.
|
// The JVM can't do it directly, so fill in the gap with a Java adapter.
|
||||||
// TO DO: figure out what to put here from case-by-case experience
|
// TO DO: figure out what to put here from case-by-case experience
|
||||||
// Use a heavier method: Convert all the arguments to Object,
|
// Use a heavier method: Convert all the arguments to Object,
|
||||||
// then back to the desired types. We might have to use Java-based
|
// then back to the desired types. We might have to use Java-based
|
||||||
// method handles to do this.
|
// method handles to do this.
|
||||||
MethodType objType = MethodType.genericMethodType(argc);
|
MethodType objType = MethodType.genericMethodType(argc);
|
||||||
MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target);
|
MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target, level);
|
||||||
if (objTarget == null)
|
if (objTarget == null)
|
||||||
objTarget = FromGeneric.make(target);
|
objTarget = FromGeneric.make(target);
|
||||||
res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget);
|
res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget, level);
|
||||||
if (res != null)
|
if (res != null)
|
||||||
return res;
|
return res;
|
||||||
return ToGeneric.make(newType, objTarget);
|
return ToGeneric.make(newType, objTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) {
|
||||||
|
MethodType oldType = target.type();
|
||||||
|
int nargs = oldType.parameterCount();
|
||||||
|
int keepPosArgs = nargs - arrayLength;
|
||||||
|
MethodType newType = oldType
|
||||||
|
.dropParameterTypes(keepPosArgs, nargs)
|
||||||
|
.insertParameterTypes(keepPosArgs, arrayType);
|
||||||
|
return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength);
|
||||||
|
}
|
||||||
|
static MethodHandle spreadArguments(MethodHandle target, MethodType newType, int spreadArgPos) {
|
||||||
|
int arrayLength = target.type().parameterCount() - spreadArgPos;
|
||||||
|
return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength);
|
||||||
|
}
|
||||||
static MethodHandle spreadArguments(MethodHandle target,
|
static MethodHandle spreadArguments(MethodHandle target,
|
||||||
MethodType newType,
|
MethodType newType,
|
||||||
int spreadArg) {
|
int spreadArgPos,
|
||||||
|
Class<?> arrayType,
|
||||||
|
int arrayLength) {
|
||||||
// TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
|
// TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
|
||||||
MethodType oldType = target.type();
|
MethodType oldType = target.type();
|
||||||
// spread the last argument of newType to oldType
|
// spread the last argument of newType to oldType
|
||||||
int spreadCount = oldType.parameterCount() - spreadArg;
|
assert(arrayLength == oldType.parameterCount() - spreadArgPos);
|
||||||
Class<Object[]> spreadArgType = Object[].class;
|
assert(newType.parameterType(spreadArgPos) == arrayType);
|
||||||
MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, spreadArgType, spreadArg, spreadCount);
|
MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength);
|
||||||
if (res != null)
|
if (res == null) throw new IllegalArgumentException("spread on "+target+" with "+arrayType.getSimpleName());
|
||||||
return res;
|
|
||||||
// try an intermediate adapter
|
|
||||||
Class<?> spreadType = null;
|
|
||||||
if (spreadArg < 0 || spreadArg >= newType.parameterCount()
|
|
||||||
|| !VerifyType.isSpreadArgType(spreadType = newType.parameterType(spreadArg)))
|
|
||||||
throw newIllegalArgumentException("no restarg in "+newType);
|
|
||||||
Class<?>[] ptypes = oldType.parameterArray();
|
|
||||||
for (int i = 0; i < spreadCount; i++)
|
|
||||||
ptypes[spreadArg + i] = VerifyType.spreadArgElementType(spreadType, i);
|
|
||||||
MethodType midType = MethodType.methodType(newType.returnType(), ptypes);
|
|
||||||
// after spreading, some arguments may need further conversion
|
|
||||||
MethodHandle target2 = convertArguments(target, midType, oldType, null);
|
|
||||||
if (target2 == null)
|
|
||||||
throw new UnsupportedOperationException("NYI: convert "+midType+" =calls=> "+oldType);
|
|
||||||
res = AdapterMethodHandle.makeSpreadArguments(newType, target2, spreadArgType, spreadArg, spreadCount);
|
|
||||||
if (res != null)
|
|
||||||
return res;
|
|
||||||
res = SpreadGeneric.make(target2, spreadCount);
|
|
||||||
if (res != null)
|
|
||||||
res = convertArguments(res, newType, res.type(), null);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MethodHandle collectArguments(MethodHandle target,
|
||||||
|
int collectArg,
|
||||||
|
MethodHandle collector) {
|
||||||
|
MethodType type = target.type();
|
||||||
|
Class<?> collectType = collector.type().returnType();
|
||||||
|
if (collectType != type.parameterType(collectArg))
|
||||||
|
target = target.asType(type.changeParameterType(collectArg, collectType));
|
||||||
|
MethodType newType = type
|
||||||
|
.dropParameterTypes(collectArg, collectArg+1)
|
||||||
|
.insertParameterTypes(collectArg, collector.type().parameterArray());
|
||||||
|
return collectArguments(target, newType, collectArg, collector);
|
||||||
|
}
|
||||||
static MethodHandle collectArguments(MethodHandle target,
|
static MethodHandle collectArguments(MethodHandle target,
|
||||||
MethodType newType,
|
MethodType newType,
|
||||||
int collectArg,
|
int collectArg,
|
||||||
MethodHandle collector) {
|
MethodHandle collector) {
|
||||||
MethodType oldType = target.type(); // (a...,c)=>r
|
MethodType oldType = target.type(); // (a...,c)=>r
|
||||||
if (collector == null) {
|
|
||||||
int numCollect = newType.parameterCount() - oldType.parameterCount() + 1;
|
|
||||||
collector = ValueConversions.varargsArray(numCollect);
|
|
||||||
}
|
|
||||||
// newType // (a..., b...)=>r
|
// newType // (a..., b...)=>r
|
||||||
MethodType colType = collector.type(); // (b...)=>c
|
MethodType colType = collector.type(); // (b...)=>c
|
||||||
// oldType // (a..., b...)=>r
|
// oldType // (a..., b...)=>r
|
||||||
assert(newType.parameterCount() == collectArg + colType.parameterCount());
|
assert(newType.parameterCount() == collectArg + colType.parameterCount());
|
||||||
assert(oldType.parameterCount() == collectArg + 1);
|
assert(oldType.parameterCount() == collectArg + 1);
|
||||||
MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null);
|
MethodHandle result = null;
|
||||||
MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, null);
|
if (AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false)) {
|
||||||
if (gtarget == null || gcollector == null) return null;
|
result = AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false);
|
||||||
MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget);
|
}
|
||||||
MethodHandle result = convertArguments(gresult, newType, gresult.type(), null);
|
if (result == null) {
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||||
|
MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, 0);
|
||||||
|
MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, 0);
|
||||||
|
if (gtarget == null || gcollector == null) return null;
|
||||||
|
MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget);
|
||||||
|
result = convertArguments(gresult, newType, gresult.type(), 0);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static MethodHandle filterArgument(MethodHandle target,
|
static MethodHandle filterArgument(MethodHandle target,
|
||||||
int pos,
|
int pos,
|
||||||
MethodHandle filter) {
|
MethodHandle filter) {
|
||||||
MethodType ttype = target.type(), gttype = ttype.generic();
|
MethodType ttype = target.type();
|
||||||
|
MethodType ftype = filter.type();
|
||||||
|
assert(ftype.parameterCount() == 1);
|
||||||
|
MethodType rtype = ttype.changeParameterType(pos, ftype.parameterType(0));
|
||||||
|
MethodType gttype = ttype.generic();
|
||||||
if (ttype != gttype) {
|
if (ttype != gttype) {
|
||||||
target = convertArguments(target, gttype, ttype, null);
|
target = convertArguments(target, gttype, ttype, 0);
|
||||||
ttype = gttype;
|
ttype = gttype;
|
||||||
}
|
}
|
||||||
MethodType ftype = filter.type(), gftype = ftype.generic();
|
MethodType gftype = ftype.generic();
|
||||||
if (ftype.parameterCount() != 1)
|
|
||||||
throw new InternalError();
|
|
||||||
if (ftype != gftype) {
|
if (ftype != gftype) {
|
||||||
filter = convertArguments(filter, gftype, ftype, null);
|
filter = convertArguments(filter, gftype, ftype, 0);
|
||||||
ftype = gftype;
|
ftype = gftype;
|
||||||
}
|
}
|
||||||
if (ftype == ttype) {
|
MethodHandle result = null;
|
||||||
// simple unary case
|
if (AdapterMethodHandle.canCollectArguments(ttype, ftype, pos, false)) {
|
||||||
return FilterOneArgument.make(filter, target);
|
result = AdapterMethodHandle.makeCollectArguments(target, filter, pos, false);
|
||||||
}
|
}
|
||||||
return FilterGeneric.makeArgumentFilter(pos, filter, target);
|
if (result == null) {
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||||
|
if (ftype == ttype) {
|
||||||
|
// simple unary case
|
||||||
|
result = FilterOneArgument.make(filter, target);
|
||||||
|
} else {
|
||||||
|
result = FilterGeneric.makeArgumentFilter(pos, filter, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.type() != rtype)
|
||||||
|
result = result.asType(rtype);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static MethodHandle foldArguments(MethodHandle target,
|
static MethodHandle foldArguments(MethodHandle target,
|
||||||
MethodType newType,
|
MethodType newType,
|
||||||
MethodHandle combiner) {
|
int foldPos,
|
||||||
|
MethodHandle combiner) {
|
||||||
MethodType oldType = target.type();
|
MethodType oldType = target.type();
|
||||||
MethodType ctype = combiner.type();
|
MethodType ctype = combiner.type();
|
||||||
MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null);
|
if (AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true)) {
|
||||||
MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, null);
|
MethodHandle res = AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true);
|
||||||
|
if (res != null) return res;
|
||||||
|
}
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||||
|
if (foldPos != 0) return null;
|
||||||
|
MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, 0);
|
||||||
|
MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, 0);
|
||||||
|
if (ctype.returnType() == void.class) {
|
||||||
|
gtarget = dropArguments(gtarget, oldType.generic().insertParameterTypes(foldPos, Object.class), foldPos);
|
||||||
|
}
|
||||||
if (gtarget == null || gcombiner == null) return null;
|
if (gtarget == null || gcombiner == null) return null;
|
||||||
MethodHandle gresult = FilterGeneric.makeArgumentFolder(gcombiner, gtarget);
|
MethodHandle gresult = FilterGeneric.makeArgumentFolder(gcombiner, gtarget);
|
||||||
MethodHandle result = convertArguments(gresult, newType, gresult.type(), null);
|
return convertArguments(gresult, newType, gresult.type(), 0);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
@ -802,6 +879,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
this.target = target;
|
this.target = target;
|
||||||
this.fallback = fallback;
|
this.fallback = fallback;
|
||||||
}
|
}
|
||||||
|
// FIXME: Build the control flow out of foldArguments.
|
||||||
static MethodHandle make(MethodHandle test, MethodHandle target, MethodHandle fallback) {
|
static MethodHandle make(MethodHandle test, MethodHandle target, MethodHandle fallback) {
|
||||||
MethodType type = target.type();
|
MethodType type = target.type();
|
||||||
int nargs = type.parameterCount();
|
int nargs = type.parameterCount();
|
||||||
@ -809,12 +887,12 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
MethodHandle invoke = INVOKES[nargs];
|
MethodHandle invoke = INVOKES[nargs];
|
||||||
MethodType gtype = type.generic();
|
MethodType gtype = type.generic();
|
||||||
assert(invoke.type().dropParameterTypes(0,1) == gtype);
|
assert(invoke.type().dropParameterTypes(0,1) == gtype);
|
||||||
MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), null);
|
MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), 0);
|
||||||
MethodHandle gtarget = convertArguments(target, gtype, type, null);
|
MethodHandle gtarget = convertArguments(target, gtype, type, 0);
|
||||||
MethodHandle gfallback = convertArguments(fallback, gtype, type, null);
|
MethodHandle gfallback = convertArguments(fallback, gtype, type, 0);
|
||||||
if (gtest == null || gtarget == null || gfallback == null) return null;
|
if (gtest == null || gtarget == null || gfallback == null) return null;
|
||||||
MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback);
|
MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback);
|
||||||
return convertArguments(gguard, type, gtype, null);
|
return convertArguments(gguard, type, gtype, 0);
|
||||||
} else {
|
} else {
|
||||||
MethodHandle invoke = VARARGS_INVOKE;
|
MethodHandle invoke = VARARGS_INVOKE;
|
||||||
MethodType gtype = MethodType.genericMethodType(1);
|
MethodType gtype = MethodType.genericMethodType(1);
|
||||||
@ -925,8 +1003,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
|
GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
|
||||||
this(INVOKES[target.type().parameterCount()], target, exType, catcher);
|
this(INVOKES[target.type().parameterCount()], target, exType, catcher);
|
||||||
}
|
}
|
||||||
GuardWithCatch(MethodHandle invoker,
|
// FIXME: Build the control flow out of foldArguments.
|
||||||
MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
|
GuardWithCatch(MethodHandle invoker,
|
||||||
|
MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
|
||||||
super(invoker);
|
super(invoker);
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.exType = exType;
|
this.exType = exType;
|
||||||
@ -1057,11 +1136,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
if (nargs < GuardWithCatch.INVOKES.length) {
|
if (nargs < GuardWithCatch.INVOKES.length) {
|
||||||
MethodType gtype = type.generic();
|
MethodType gtype = type.generic();
|
||||||
MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
|
MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
|
||||||
MethodHandle gtarget = convertArguments(target, gtype, type, null);
|
MethodHandle gtarget = convertArguments(target, gtype, type, 0);
|
||||||
MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, null);
|
MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 0);
|
||||||
MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher);
|
MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher);
|
||||||
if (gtarget == null || gcatcher == null || gguard == null) return null;
|
if (gtarget == null || gcatcher == null || gguard == null) return null;
|
||||||
return convertArguments(gguard, type, gtype, null);
|
return convertArguments(gguard, type, gtype, 0);
|
||||||
} else {
|
} else {
|
||||||
MethodType gtype = MethodType.genericMethodType(0, true);
|
MethodType gtype = MethodType.genericMethodType(0, true);
|
||||||
MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
|
MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
|
||||||
|
@ -115,6 +115,8 @@ class MethodHandleNatives {
|
|||||||
|
|
||||||
/** Which conv-ops are implemented by the JVM? */
|
/** Which conv-ops are implemented by the JVM? */
|
||||||
static final int CONV_OP_IMPLEMENTED_MASK;
|
static final int CONV_OP_IMPLEMENTED_MASK;
|
||||||
|
/** Derived mode flag. Only false on some old JVM implementations. */
|
||||||
|
static final boolean HAVE_RICOCHET_FRAMES;
|
||||||
|
|
||||||
private static native void registerNatives();
|
private static native void registerNatives();
|
||||||
static {
|
static {
|
||||||
@ -141,6 +143,7 @@ class MethodHandleNatives {
|
|||||||
if (CONV_OP_IMPLEMENTED_MASK_ == 0)
|
if (CONV_OP_IMPLEMENTED_MASK_ == 0)
|
||||||
CONV_OP_IMPLEMENTED_MASK_ = DEFAULT_CONV_OP_IMPLEMENTED_MASK;
|
CONV_OP_IMPLEMENTED_MASK_ = DEFAULT_CONV_OP_IMPLEMENTED_MASK;
|
||||||
CONV_OP_IMPLEMENTED_MASK = CONV_OP_IMPLEMENTED_MASK_;
|
CONV_OP_IMPLEMENTED_MASK = CONV_OP_IMPLEMENTED_MASK_;
|
||||||
|
HAVE_RICOCHET_FRAMES = (CONV_OP_IMPLEMENTED_MASK & (1<<OP_COLLECT_ARGS)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All compile-time constants go here.
|
// All compile-time constants go here.
|
||||||
@ -186,25 +189,26 @@ class MethodHandleNatives {
|
|||||||
*/
|
*/
|
||||||
static final int
|
static final int
|
||||||
OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype
|
OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype
|
||||||
OP_RETYPE_RAW = 0x1, // no argument changes; straight retype
|
OP_RETYPE_RAW = 0x1, // straight retype, trusted (void->int, Object->T)
|
||||||
OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument
|
OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument
|
||||||
OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another
|
OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another
|
||||||
OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive
|
OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive
|
||||||
OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper (NYI)
|
OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper
|
||||||
OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg)
|
OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg)
|
||||||
OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg)
|
OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg)
|
||||||
OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS)
|
OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS)
|
||||||
OP_DROP_ARGS = 0x9, // remove one or more argument slots
|
OP_DROP_ARGS = 0x9, // remove one or more argument slots
|
||||||
OP_COLLECT_ARGS = 0xA, // combine one or more arguments into a varargs (NYI)
|
OP_COLLECT_ARGS = 0xA, // combine arguments using an auxiliary function
|
||||||
OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
|
OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
|
||||||
OP_FLYBY = 0xC, // operate first on reified argument list (NYI)
|
OP_FOLD_ARGS = 0xC, // combine but do not remove arguments; prepend result
|
||||||
OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI)
|
//OP_UNUSED_13 = 0xD, // unused code, perhaps for reified argument lists
|
||||||
CONV_OP_LIMIT = 0xE; // limit of CONV_OP enumeration
|
CONV_OP_LIMIT = 0xE; // limit of CONV_OP enumeration
|
||||||
/** Shift and mask values for decoding the AMH.conversion field.
|
/** Shift and mask values for decoding the AMH.conversion field.
|
||||||
* These numbers are shared with the JVM for creating AMHs.
|
* These numbers are shared with the JVM for creating AMHs.
|
||||||
*/
|
*/
|
||||||
static final int
|
static final int
|
||||||
CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
|
CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
|
||||||
|
CONV_TYPE_MASK = 0x0F, // fits T_ADDRESS and below
|
||||||
CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
|
CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
|
||||||
CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK
|
CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK
|
||||||
CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK
|
CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK
|
||||||
@ -244,8 +248,9 @@ class MethodHandleNatives {
|
|||||||
T_LONG = 11,
|
T_LONG = 11,
|
||||||
T_OBJECT = 12,
|
T_OBJECT = 12,
|
||||||
//T_ARRAY = 13
|
//T_ARRAY = 13
|
||||||
T_VOID = 14;
|
T_VOID = 14,
|
||||||
//T_ADDRESS = 15
|
//T_ADDRESS = 15
|
||||||
|
T_ILLEGAL = 99;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
|
* Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
|
||||||
@ -273,16 +278,29 @@ class MethodHandleNatives {
|
|||||||
try {
|
try {
|
||||||
Field con = Constants.class.getDeclaredField(name);
|
Field con = Constants.class.getDeclaredField(name);
|
||||||
int jval = con.getInt(null);
|
int jval = con.getInt(null);
|
||||||
if (jval != vmval)
|
if (jval == vmval) continue;
|
||||||
throw new InternalError(name+": JVM has "+vmval+" while Java has "+jval);
|
String err = (name+": JVM has "+vmval+" while Java has "+jval);
|
||||||
|
if (name.equals("CONV_OP_LIMIT")) {
|
||||||
|
System.err.println("warning: "+err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw new InternalError(err);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
if (ex instanceof NoSuchFieldException) {
|
||||||
|
String err = (name+": JVM has "+vmval+" which Java does not define");
|
||||||
|
// ignore exotic ops the JVM cares about; we just wont issue them
|
||||||
|
if (name.startsWith("OP_") || name.startsWith("GC_")) {
|
||||||
|
System.err.println("warning: "+err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
throw new InternalError(name+": access failed, got "+ex);
|
throw new InternalError(name+": access failed, got "+ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
static {
|
static {
|
||||||
verifyConstants();
|
assert(verifyConstants());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Up-calls from the JVM.
|
// Up-calls from the JVM.
|
||||||
@ -313,7 +331,7 @@ class MethodHandleNatives {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The JVM wants to use a MethodType with invokeGeneric. Give the runtime fair warning.
|
* The JVM wants to use a MethodType with inexact invoke. Give the runtime fair warning.
|
||||||
*/
|
*/
|
||||||
static void notifyGenericMethodType(MethodType type) {
|
static void notifyGenericMethodType(MethodType type) {
|
||||||
type.form().notifyGenericMethodType();
|
type.form().notifyGenericMethodType();
|
||||||
@ -323,15 +341,39 @@ class MethodHandleNatives {
|
|||||||
* The JVM wants to raise an exception. Here's the path.
|
* The JVM wants to raise an exception. Here's the path.
|
||||||
*/
|
*/
|
||||||
static void raiseException(int code, Object actual, Object required) {
|
static void raiseException(int code, Object actual, Object required) {
|
||||||
String message;
|
String message = null;
|
||||||
// disregard the identity of the actual object, if it is not a class:
|
|
||||||
if (!(actual instanceof Class) && !(actual instanceof MethodType))
|
|
||||||
actual = actual.getClass();
|
|
||||||
if (actual != null)
|
|
||||||
message = "required "+required+" but encountered "+actual;
|
|
||||||
else
|
|
||||||
message = "required "+required;
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
|
case 190: // arraylength
|
||||||
|
try {
|
||||||
|
String reqLength = "";
|
||||||
|
if (required instanceof AdapterMethodHandle) {
|
||||||
|
int conv = ((AdapterMethodHandle)required).getConversion();
|
||||||
|
int spChange = AdapterMethodHandle.extractStackMove(conv);
|
||||||
|
reqLength = " of length "+(spChange+1);
|
||||||
|
}
|
||||||
|
int actualLength = actual == null ? 0 : java.lang.reflect.Array.getLength(actual);
|
||||||
|
message = "required array"+reqLength+", but encountered wrong length "+actualLength;
|
||||||
|
break;
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
}
|
||||||
|
required = Object[].class; // should have been an array
|
||||||
|
code = 192; // checkcast
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// disregard the identity of the actual object, if it is not a class:
|
||||||
|
if (message == null) {
|
||||||
|
if (!(actual instanceof Class) && !(actual instanceof MethodType))
|
||||||
|
actual = actual.getClass();
|
||||||
|
if (actual != null)
|
||||||
|
message = "required "+required+" but encountered "+actual;
|
||||||
|
else
|
||||||
|
message = "required "+required;
|
||||||
|
}
|
||||||
|
switch (code) {
|
||||||
|
case 190: // arraylength
|
||||||
|
throw new ArrayIndexOutOfBoundsException(message);
|
||||||
|
case 50: //_aaload
|
||||||
|
throw new ClassCastException(message);
|
||||||
case 192: // checkcast
|
case 192: // checkcast
|
||||||
throw new ClassCastException(message);
|
throw new ClassCastException(message);
|
||||||
default:
|
default:
|
||||||
@ -365,4 +407,13 @@ class MethodHandleNatives {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This assertion marks code which was written before ricochet frames were implemented.
|
||||||
|
* Such code will go away when the ports catch up.
|
||||||
|
*/
|
||||||
|
static boolean workaroundWithoutRicochetFrames() {
|
||||||
|
assert(!HAVE_RICOCHET_FRAMES) : "this code should not be executed if `-XX:+UseRicochetFrames is enabled";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,8 +63,17 @@ package java.lang.invoke;
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void checkSpreadArgument(Object av, int n) {
|
static void checkSpreadArgument(Object av, int n) {
|
||||||
if (av == null ? n != 0 : ((Object[])av).length != n)
|
if (av == null) {
|
||||||
throw newIllegalArgumentException("Array is not of length "+n);
|
if (n == 0) return;
|
||||||
|
} else if (av instanceof Object[]) {
|
||||||
|
int len = ((Object[])av).length;
|
||||||
|
if (len == n) return;
|
||||||
|
} else {
|
||||||
|
int len = java.lang.reflect.Array.getLength(av);
|
||||||
|
if (len == n) return;
|
||||||
|
}
|
||||||
|
// fall through to error:
|
||||||
|
throw newIllegalArgumentException("Array is not of length "+n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handy shared exception makers (they simplify the common case code)
|
// handy shared exception makers (they simplify the common case code)
|
||||||
@ -80,6 +89,9 @@ package java.lang.invoke;
|
|||||||
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj) {
|
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj) {
|
||||||
return new IllegalArgumentException(message(message, obj));
|
return new IllegalArgumentException(message(message, obj));
|
||||||
}
|
}
|
||||||
|
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) {
|
||||||
|
return new IllegalArgumentException(message(message, obj, obj2));
|
||||||
|
}
|
||||||
/*non-public*/ static Error uncaughtException(Exception ex) {
|
/*non-public*/ static Error uncaughtException(Exception ex) {
|
||||||
Error err = new InternalError("uncaught exception");
|
Error err = new InternalError("uncaught exception");
|
||||||
err.initCause(ex);
|
err.initCause(ex);
|
||||||
@ -89,4 +101,8 @@ package java.lang.invoke;
|
|||||||
if (obj != null) message = message + ": " + obj;
|
if (obj != null) message = message + ": " + obj;
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
private static String message(String message, Object obj, Object obj2) {
|
||||||
|
if (obj != null || obj2 != null) message = message + ": " + obj + ", " + obj2;
|
||||||
|
return message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ public class MethodHandles {
|
|||||||
* is not symbolically accessible from the lookup class's loader,
|
* is not symbolically accessible from the lookup class's loader,
|
||||||
* the lookup can still succeed.
|
* the lookup can still succeed.
|
||||||
* For example, lookups for {@code MethodHandle.invokeExact} and
|
* For example, lookups for {@code MethodHandle.invokeExact} and
|
||||||
* {@code MethodHandle.invokeGeneric} will always succeed, regardless of requested type.
|
* {@code MethodHandle.invoke} will always succeed, regardless of requested type.
|
||||||
* <li>If there is a security manager installed, it can forbid the lookup
|
* <li>If there is a security manager installed, it can forbid the lookup
|
||||||
* on various grounds (<a href="#secmgr">see below</a>).
|
* on various grounds (<a href="#secmgr">see below</a>).
|
||||||
* By contrast, the {@code ldc} instruction is not subject to
|
* By contrast, the {@code ldc} instruction is not subject to
|
||||||
@ -590,10 +590,10 @@ public class MethodHandles {
|
|||||||
* Because of the general equivalence between {@code invokevirtual}
|
* Because of the general equivalence between {@code invokevirtual}
|
||||||
* instructions and method handles produced by {@code findVirtual},
|
* instructions and method handles produced by {@code findVirtual},
|
||||||
* if the class is {@code MethodHandle} and the name string is
|
* if the class is {@code MethodHandle} and the name string is
|
||||||
* {@code invokeExact} or {@code invokeGeneric}, the resulting
|
* {@code invokeExact} or {@code invoke}, the resulting
|
||||||
* method handle is equivalent to one produced by
|
* method handle is equivalent to one produced by
|
||||||
* {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker} or
|
* {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker} or
|
||||||
* {@link java.lang.invoke.MethodHandles#genericInvoker MethodHandles.genericInvoker}
|
* {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}
|
||||||
* with the same {@code type} argument.
|
* with the same {@code type} argument.
|
||||||
*
|
*
|
||||||
* @param refc the class or interface from which the method is accessed
|
* @param refc the class or interface from which the method is accessed
|
||||||
@ -1080,7 +1080,7 @@ return mh1;
|
|||||||
MethodType rawType = mh.type();
|
MethodType rawType = mh.type();
|
||||||
if (rawType.parameterType(0) == caller) return mh;
|
if (rawType.parameterType(0) == caller) return mh;
|
||||||
MethodType narrowType = rawType.changeParameterType(0, caller);
|
MethodType narrowType = rawType.changeParameterType(0, caller);
|
||||||
MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, null);
|
MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, 0);
|
||||||
return fixVarargs(narrowMH, mh);
|
return fixVarargs(narrowMH, mh);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1148,7 +1148,7 @@ return mh1;
|
|||||||
* <li>an {@code Object[]} array containing more arguments
|
* <li>an {@code Object[]} array containing more arguments
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* The invoker will behave like a call to {@link MethodHandle#invokeGeneric invokeGeneric} with
|
* The invoker will behave like a call to {@link MethodHandle#invoke invoke} with
|
||||||
* the indicated {@code type}.
|
* the indicated {@code type}.
|
||||||
* That is, if the target is exactly of the given {@code type}, it will behave
|
* That is, if the target is exactly of the given {@code type}, it will behave
|
||||||
* like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType}
|
* like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType}
|
||||||
@ -1166,7 +1166,7 @@ return mh1;
|
|||||||
* <p>
|
* <p>
|
||||||
* This method is equivalent to the following code (though it may be more efficient):
|
* This method is equivalent to the following code (though it may be more efficient):
|
||||||
* <p><blockquote><pre>
|
* <p><blockquote><pre>
|
||||||
MethodHandle invoker = MethodHandles.genericInvoker(type);
|
MethodHandle invoker = MethodHandles.invoker(type);
|
||||||
int spreadArgCount = type.parameterCount - objectArgCount;
|
int spreadArgCount = type.parameterCount - objectArgCount;
|
||||||
invoker = invoker.asSpreader(Object[].class, spreadArgCount);
|
invoker = invoker.asSpreader(Object[].class, spreadArgCount);
|
||||||
return invoker;
|
return invoker;
|
||||||
@ -1186,7 +1186,7 @@ return invoker;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a special <em>invoker method handle</em> which can be used to
|
* Produces a special <em>invoker method handle</em> which can be used to
|
||||||
* invoke any method handle of the given type, as if by {@code invokeExact}.
|
* invoke any method handle of the given type, as if by {@link MethodHandle#invokeExact invokeExact}.
|
||||||
* The resulting invoker will have a type which is
|
* The resulting invoker will have a type which is
|
||||||
* exactly equal to the desired type, except that it will accept
|
* exactly equal to the desired type, except that it will accept
|
||||||
* an additional leading argument of type {@code MethodHandle}.
|
* an additional leading argument of type {@code MethodHandle}.
|
||||||
@ -1203,7 +1203,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
|
|||||||
* For example, to emulate an {@code invokeExact} call to a variable method
|
* For example, to emulate an {@code invokeExact} call to a variable method
|
||||||
* handle {@code M}, extract its type {@code T},
|
* handle {@code M}, extract its type {@code T},
|
||||||
* look up the invoker method {@code X} for {@code T},
|
* look up the invoker method {@code X} for {@code T},
|
||||||
* and call the invoker method, as {@code X.invokeGeneric(T, A...)}.
|
* and call the invoker method, as {@code X.invoke(T, A...)}.
|
||||||
* (It would not work to call {@code X.invokeExact}, since the type {@code T}
|
* (It would not work to call {@code X.invokeExact}, since the type {@code T}
|
||||||
* is unknown.)
|
* is unknown.)
|
||||||
* If spreading, collecting, or other argument transformations are required,
|
* If spreading, collecting, or other argument transformations are required,
|
||||||
@ -1212,7 +1212,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
|
|||||||
* <p>
|
* <p>
|
||||||
* <em>(Note: The invoker method is not available via the Core Reflection API.
|
* <em>(Note: The invoker method is not available via the Core Reflection API.
|
||||||
* An attempt to call {@linkplain java.lang.reflect.Method#invoke Method.invoke}
|
* An attempt to call {@linkplain java.lang.reflect.Method#invoke Method.invoke}
|
||||||
* on the declared {@code invokeExact} or {@code invokeGeneric} method will raise an
|
* on the declared {@code invokeExact} or {@code invoke} method will raise an
|
||||||
* {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em>
|
* {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em>
|
||||||
* <p>
|
* <p>
|
||||||
* This method throws no reflective or security exceptions.
|
* This method throws no reflective or security exceptions.
|
||||||
@ -1226,20 +1226,20 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a special <em>invoker method handle</em> which can be used to
|
* Produces a special <em>invoker method handle</em> which can be used to
|
||||||
* invoke any method handle of the given type, as if by {@code invokeGeneric}.
|
* invoke any method handle compatible with the given type, as if by {@link MethodHandle#invoke invoke}.
|
||||||
* The resulting invoker will have a type which is
|
* The resulting invoker will have a type which is
|
||||||
* exactly equal to the desired type, except that it will accept
|
* exactly equal to the desired type, except that it will accept
|
||||||
* an additional leading argument of type {@code MethodHandle}.
|
* an additional leading argument of type {@code MethodHandle}.
|
||||||
* <p>
|
* <p>
|
||||||
* Before invoking its target, the invoker will apply reference casts as
|
* Before invoking its target, the invoker will apply reference casts as
|
||||||
* necessary and unbox and widen primitive arguments, as if by {@link #convertArguments convertArguments}.
|
* necessary and box, unbox, or widen primitive values, as if by {@link MethodHandle#asType asType}.
|
||||||
* The return value of the invoker will be an {@code Object} reference,
|
* Similarly, the return value will be converted as necessary.
|
||||||
* boxing a primitive value if the original type returns a primitive,
|
* If the target is a {@linkplain MethodHandle#asVarargsCollector variable arity method handle},
|
||||||
* and always null if the original type returns void.
|
* the required arity conversion will be made, again as if by {@link MethodHandle#asType asType}.
|
||||||
* <p>
|
* <p>
|
||||||
* This method is equivalent to the following code (though it may be more efficient):
|
* This method is equivalent to the following code (though it may be more efficient):
|
||||||
* <p><blockquote><pre>
|
* <p><blockquote><pre>
|
||||||
publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
|
publicLookup().findVirtual(MethodHandle.class, "invoke", type)
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
* <p>
|
* <p>
|
||||||
* This method throws no reflective or security exceptions.
|
* This method throws no reflective or security exceptions.
|
||||||
@ -1247,8 +1247,17 @@ publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
|
|||||||
* @return a method handle suitable for invoking any method handle convertible to the given type
|
* @return a method handle suitable for invoking any method handle convertible to the given type
|
||||||
*/
|
*/
|
||||||
static public
|
static public
|
||||||
|
MethodHandle invoker(MethodType type) {
|
||||||
|
return type.invokers().generalInvoker();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <em>Temporary alias</em> for {@link #invoker}, for backward compatibility with some versions of JSR 292.
|
||||||
|
* @deprecated Will be removed for JSR 292 Proposed Final Draft.
|
||||||
|
*/
|
||||||
|
public static
|
||||||
MethodHandle genericInvoker(MethodType type) {
|
MethodHandle genericInvoker(MethodType type) {
|
||||||
return type.invokers().genericInvoker();
|
return invoker(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1368,18 +1377,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
|
|||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
MethodHandle convertArguments(MethodHandle target, MethodType newType) {
|
MethodHandle convertArguments(MethodHandle target, MethodType newType) {
|
||||||
MethodType oldType = target.type();
|
return MethodHandleImpl.convertArguments(target, newType, 1);
|
||||||
if (oldType.equals(newType))
|
|
||||||
return target;
|
|
||||||
MethodHandle res = null;
|
|
||||||
try {
|
|
||||||
res = MethodHandleImpl.convertArguments(target,
|
|
||||||
newType, oldType, null);
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
}
|
|
||||||
if (res == null)
|
|
||||||
throw new WrongMethodTypeException("cannot convert to "+newType+": "+target);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1422,7 +1420,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
|
|||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
|
MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
|
||||||
return convertArguments(target, newType); // FIXME!
|
return MethodHandleImpl.convertArguments(target, newType, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1517,23 +1515,32 @@ assert((int)twice.invokeExact(21) == 42);
|
|||||||
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
|
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
|
||||||
MethodType oldType = target.type();
|
MethodType oldType = target.type();
|
||||||
checkReorder(reorder, newType, oldType);
|
checkReorder(reorder, newType, oldType);
|
||||||
return MethodHandleImpl.convertArguments(target,
|
return MethodHandleImpl.permuteArguments(target,
|
||||||
newType, oldType,
|
newType, oldType,
|
||||||
reorder);
|
reorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
|
private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
|
||||||
|
if (newType.returnType() != oldType.returnType())
|
||||||
|
throw newIllegalArgumentException("return types do not match",
|
||||||
|
oldType, newType);
|
||||||
if (reorder.length == oldType.parameterCount()) {
|
if (reorder.length == oldType.parameterCount()) {
|
||||||
int limit = newType.parameterCount();
|
int limit = newType.parameterCount();
|
||||||
boolean bad = false;
|
boolean bad = false;
|
||||||
for (int i : reorder) {
|
for (int j = 0; j < reorder.length; j++) {
|
||||||
|
int i = reorder[j];
|
||||||
if (i < 0 || i >= limit) {
|
if (i < 0 || i >= limit) {
|
||||||
bad = true; break;
|
bad = true; break;
|
||||||
}
|
}
|
||||||
|
Class<?> src = newType.parameterType(i);
|
||||||
|
Class<?> dst = oldType.parameterType(j);
|
||||||
|
if (src != dst)
|
||||||
|
throw newIllegalArgumentException("parameter types do not match after reorder",
|
||||||
|
oldType, newType);
|
||||||
}
|
}
|
||||||
if (!bad) return;
|
if (!bad) return;
|
||||||
}
|
}
|
||||||
throw newIllegalArgumentException("bad reorder array");
|
throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1622,7 +1629,7 @@ assert((int)twice.invokeExact(21) == 42);
|
|||||||
if (type == void.class)
|
if (type == void.class)
|
||||||
throw newIllegalArgumentException("void type");
|
throw newIllegalArgumentException("void type");
|
||||||
Wrapper w = Wrapper.forPrimitiveType(type);
|
Wrapper w = Wrapper.forPrimitiveType(type);
|
||||||
return identity(type).bindTo(w.convert(value, type));
|
return insertArguments(identity(type), 0, w.convert(value, type));
|
||||||
} else {
|
} else {
|
||||||
return identity(type).bindTo(type.cast(value));
|
return identity(type).bindTo(type.cast(value));
|
||||||
}
|
}
|
||||||
@ -1857,7 +1864,8 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
|
|||||||
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
|
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
|
||||||
MethodType targetType = target.type();
|
MethodType targetType = target.type();
|
||||||
MethodHandle adapter = target;
|
MethodHandle adapter = target;
|
||||||
MethodType adapterType = targetType;
|
MethodType adapterType = null;
|
||||||
|
assert((adapterType = targetType) != null);
|
||||||
int maxPos = targetType.parameterCount();
|
int maxPos = targetType.parameterCount();
|
||||||
if (pos + filters.length > maxPos)
|
if (pos + filters.length > maxPos)
|
||||||
throw newIllegalArgumentException("too many filters");
|
throw newIllegalArgumentException("too many filters");
|
||||||
@ -1865,19 +1873,23 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
|
|||||||
for (MethodHandle filter : filters) {
|
for (MethodHandle filter : filters) {
|
||||||
curPos += 1;
|
curPos += 1;
|
||||||
if (filter == null) continue; // ignore null elements of filters
|
if (filter == null) continue; // ignore null elements of filters
|
||||||
MethodType filterType = filter.type();
|
adapter = filterArgument(adapter, curPos, filter);
|
||||||
if (filterType.parameterCount() != 1
|
assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null);
|
||||||
|| filterType.returnType() != targetType.parameterType(curPos))
|
|
||||||
throw newIllegalArgumentException("target and filter types do not match");
|
|
||||||
adapterType = adapterType.changeParameterType(curPos, filterType.parameterType(0));
|
|
||||||
adapter = MethodHandleImpl.filterArgument(adapter, curPos, filter);
|
|
||||||
}
|
}
|
||||||
MethodType midType = adapter.type();
|
assert(adapterType.equals(adapter.type()));
|
||||||
if (midType != adapterType)
|
|
||||||
adapter = MethodHandleImpl.convertArguments(adapter, adapterType, midType, null);
|
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*non-public*/ static
|
||||||
|
MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
|
||||||
|
MethodType targetType = target.type();
|
||||||
|
MethodType filterType = filter.type();
|
||||||
|
if (filterType.parameterCount() != 1
|
||||||
|
|| filterType.returnType() != targetType.parameterType(pos))
|
||||||
|
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
|
||||||
|
return MethodHandleImpl.filterArgument(target, pos, filter);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapts a target method handle {@code target} by post-processing
|
* Adapts a target method handle {@code target} by post-processing
|
||||||
* its return value with a unary filter function.
|
* its return value with a unary filter function.
|
||||||
@ -1913,14 +1925,26 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
|||||||
MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
|
MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
|
||||||
MethodType targetType = target.type();
|
MethodType targetType = target.type();
|
||||||
MethodType filterType = filter.type();
|
MethodType filterType = filter.type();
|
||||||
if (filterType.parameterCount() != 1
|
Class<?> rtype = targetType.returnType();
|
||||||
|| filterType.parameterType(0) != targetType.returnType())
|
int filterValues = filterType.parameterCount();
|
||||||
throw newIllegalArgumentException("target and filter types do not match");
|
if (filterValues == 0
|
||||||
|
? (rtype != void.class)
|
||||||
|
: (rtype != filterType.parameterType(0)))
|
||||||
|
throw newIllegalArgumentException("target and filter types do not match", target, filter);
|
||||||
// result = fold( lambda(retval, arg...) { filter(retval) },
|
// result = fold( lambda(retval, arg...) { filter(retval) },
|
||||||
// lambda( arg...) { target(arg...) } )
|
// lambda( arg...) { target(arg...) } )
|
||||||
|
MethodType newType = targetType.changeReturnType(filterType.returnType());
|
||||||
|
MethodHandle result = null;
|
||||||
|
if (AdapterMethodHandle.canCollectArguments(filterType, targetType, 0, false)) {
|
||||||
|
result = AdapterMethodHandle.makeCollectArguments(filter, target, 0, false);
|
||||||
|
if (result != null) return result;
|
||||||
|
}
|
||||||
// FIXME: Too many nodes here.
|
// FIXME: Too many nodes here.
|
||||||
MethodHandle returner = dropArguments(filter, 1, targetType.parameterList());
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
|
||||||
return foldArguments(returner, target);
|
MethodHandle returner = dropArguments(filter, filterValues, targetType.parameterList());
|
||||||
|
result = foldArguments(returner, target);
|
||||||
|
assert(result.type().equals(newType));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1972,16 +1996,23 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
|||||||
MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
|
MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
|
||||||
MethodType targetType = target.type();
|
MethodType targetType = target.type();
|
||||||
MethodType combinerType = combiner.type();
|
MethodType combinerType = combiner.type();
|
||||||
|
int foldPos = 0; // always at the head, at present
|
||||||
int foldArgs = combinerType.parameterCount();
|
int foldArgs = combinerType.parameterCount();
|
||||||
boolean ok = (targetType.parameterCount() >= 1 + foldArgs);
|
int foldVals = combinerType.returnType() == void.class ? 0 : 1;
|
||||||
if (ok && !combinerType.parameterList().equals(targetType.parameterList().subList(1, foldArgs+1)))
|
int afterInsertPos = foldPos + foldVals;
|
||||||
|
boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
|
||||||
|
if (ok && !(combinerType.parameterList()
|
||||||
|
.equals(targetType.parameterList().subList(afterInsertPos,
|
||||||
|
afterInsertPos + foldArgs))))
|
||||||
ok = false;
|
ok = false;
|
||||||
if (ok && !combinerType.returnType().equals(targetType.parameterType(0)))
|
if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0)))
|
||||||
ok = false;
|
ok = false;
|
||||||
if (!ok)
|
if (!ok)
|
||||||
throw misMatchedTypes("target and combiner types", targetType, combinerType);
|
throw misMatchedTypes("target and combiner types", targetType, combinerType);
|
||||||
MethodType newType = targetType.dropParameterTypes(0, 1);
|
MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos);
|
||||||
return MethodHandleImpl.foldArguments(target, newType, combiner);
|
MethodHandle res = MethodHandleImpl.foldArguments(target, newType, foldPos, combiner);
|
||||||
|
if (res == null) throw newIllegalArgumentException("cannot fold from "+newType+" to " +targetType);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2142,7 +2173,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
|||||||
* the given {@code target} on the incoming arguments,
|
* the given {@code target} on the incoming arguments,
|
||||||
* and returning or throwing whatever the {@code target}
|
* and returning or throwing whatever the {@code target}
|
||||||
* returns or throws. The invocation will be as if by
|
* returns or throws. The invocation will be as if by
|
||||||
* {@code target.invokeGeneric}.
|
* {@code target.invoke}.
|
||||||
* The target's type will be checked before the
|
* The target's type will be checked before the
|
||||||
* instance is created, as if by a call to {@code asType},
|
* instance is created, as if by a call to {@code asType},
|
||||||
* which may result in a {@code WrongMethodTypeException}.
|
* which may result in a {@code WrongMethodTypeException}.
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package java.lang.invoke;
|
package java.lang.invoke;
|
||||||
|
|
||||||
|
import sun.invoke.util.Wrapper;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -39,7 +40,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
|||||||
* matched between a method handle and all its callers,
|
* matched between a method handle and all its callers,
|
||||||
* and the JVM's operations enforce this matching at, specifically
|
* and the JVM's operations enforce this matching at, specifically
|
||||||
* during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact}
|
* during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact}
|
||||||
* and {@link MethodHandle#invokeGeneric MethodHandle.invokeGeneric}, and during execution
|
* and {@link MethodHandle#invoke MethodHandle.invoke}, and during execution
|
||||||
* of {@code invokedynamic} instructions.
|
* of {@code invokedynamic} instructions.
|
||||||
* <p>
|
* <p>
|
||||||
* The structure is a return type accompanied by any number of parameter types.
|
* The structure is a return type accompanied by any number of parameter types.
|
||||||
@ -262,18 +263,18 @@ class MethodType implements java.io.Serializable {
|
|||||||
* Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
|
* Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
|
||||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||||
* All parameters and the return type will be {@code Object},
|
* All parameters and the return type will be {@code Object},
|
||||||
* except the final varargs parameter if any, which will be {@code Object[]}.
|
* except the final array parameter if any, which will be {@code Object[]}.
|
||||||
* @param objectArgCount number of parameters (excluding the varargs parameter if any)
|
* @param objectArgCount number of parameters (excluding the final array parameter if any)
|
||||||
* @param varargs whether there will be a varargs parameter, of type {@code Object[]}
|
* @param finalArray whether there will be a trailing array parameter, of type {@code Object[]}
|
||||||
* @return a totally generic method type, given only its count of parameters and varargs
|
* @return a generally applicable method type, for all calls of the given fixed argument count and a collected array of further arguments
|
||||||
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
|
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray})
|
||||||
* @see #genericMethodType(int)
|
* @see #genericMethodType(int)
|
||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
MethodType genericMethodType(int objectArgCount, boolean varargs) {
|
MethodType genericMethodType(int objectArgCount, boolean finalArray) {
|
||||||
MethodType mt;
|
MethodType mt;
|
||||||
checkSlotCount(objectArgCount);
|
checkSlotCount(objectArgCount);
|
||||||
int ivarargs = (!varargs ? 0 : 1);
|
int ivarargs = (!finalArray ? 0 : 1);
|
||||||
int ootIndex = objectArgCount*2 + ivarargs;
|
int ootIndex = objectArgCount*2 + ivarargs;
|
||||||
if (ootIndex < objectOnlyTypes.length) {
|
if (ootIndex < objectOnlyTypes.length) {
|
||||||
mt = objectOnlyTypes[ootIndex];
|
mt = objectOnlyTypes[ootIndex];
|
||||||
@ -294,7 +295,7 @@ class MethodType implements java.io.Serializable {
|
|||||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||||
* All parameters and the return type will be Object.
|
* All parameters and the return type will be Object.
|
||||||
* @param objectArgCount number of parameters
|
* @param objectArgCount number of parameters
|
||||||
* @return a totally generic method type, given only its count of parameters
|
* @return a generally applicable method type, for all calls of the given argument count
|
||||||
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
|
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
|
||||||
* @see #genericMethodType(int, boolean)
|
* @see #genericMethodType(int, boolean)
|
||||||
*/
|
*/
|
||||||
@ -626,6 +627,30 @@ class MethodType implements java.io.Serializable {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*non-public*/
|
||||||
|
boolean isConvertibleTo(MethodType newType) {
|
||||||
|
if (!canConvert(returnType(), newType.returnType()))
|
||||||
|
return false;
|
||||||
|
int argc = parameterCount();
|
||||||
|
if (argc != newType.parameterCount())
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
if (!canConvert(newType.parameterType(i), parameterType(i)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private static boolean canConvert(Class<?> src, Class<?> dst) {
|
||||||
|
if (src == dst || dst == void.class) return true;
|
||||||
|
if (src.isPrimitive() && dst.isPrimitive()) {
|
||||||
|
if (!Wrapper.forPrimitiveType(dst)
|
||||||
|
.isConvertibleFrom(Wrapper.forPrimitiveType(src)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Queries which have to do with the bytecode architecture
|
/// Queries which have to do with the bytecode architecture
|
||||||
|
|
||||||
/** Reports the number of JVM stack slots required to invoke a method
|
/** Reports the number of JVM stack slots required to invoke a method
|
||||||
|
@ -46,6 +46,7 @@ class MethodTypeForm {
|
|||||||
final long argCounts; // packed slot & value counts
|
final long argCounts; // packed slot & value counts
|
||||||
final long primCounts; // packed prim & double counts
|
final long primCounts; // packed prim & double counts
|
||||||
final int vmslots; // total number of parameter slots
|
final int vmslots; // total number of parameter slots
|
||||||
|
private Object vmlayout; // vm-specific information for calls
|
||||||
final MethodType erasedType; // the canonical erasure
|
final MethodType erasedType; // the canonical erasure
|
||||||
|
|
||||||
/*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers
|
/*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers
|
||||||
@ -59,7 +60,7 @@ class MethodTypeForm {
|
|||||||
/*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with
|
/*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with
|
||||||
/*lazy*/ SpreadGeneric[] spreadGeneric; // expand one argument to many
|
/*lazy*/ SpreadGeneric[] spreadGeneric; // expand one argument to many
|
||||||
/*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly
|
/*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly
|
||||||
/*lazy*/ MethodHandle genericInvoker; // hook for invokeGeneric
|
/*lazy*/ MethodHandle genericInvoker; // hook for inexact invoke
|
||||||
|
|
||||||
public MethodType erasedType() {
|
public MethodType erasedType() {
|
||||||
return erasedType;
|
return erasedType;
|
||||||
@ -460,9 +461,9 @@ class MethodTypeForm {
|
|||||||
if (genericInvoker != null) return;
|
if (genericInvoker != null) return;
|
||||||
try {
|
try {
|
||||||
// Trigger adapter creation.
|
// Trigger adapter creation.
|
||||||
genericInvoker = InvokeGeneric.genericInvokerOf(erasedType);
|
genericInvoker = InvokeGeneric.generalInvokerOf(erasedType);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Error err = new InternalError("Exception while resolving invokeGeneric");
|
Error err = new InternalError("Exception while resolving inexact invoke");
|
||||||
err.initCause(ex);
|
err.initCause(ex);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,10 @@ class SpreadGeneric {
|
|||||||
this.entryPoint = ep[0];
|
this.entryPoint = ep[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
|
||||||
|
}
|
||||||
|
|
||||||
/** From targetType remove the last spreadCount arguments, and instead
|
/** From targetType remove the last spreadCount arguments, and instead
|
||||||
* append a simple Object argument.
|
* append a simple Object argument.
|
||||||
*/
|
*/
|
||||||
|
@ -96,7 +96,7 @@ class ToGeneric {
|
|||||||
ToGeneric va2 = ToGeneric.of(primsAtEnd);
|
ToGeneric va2 = ToGeneric.of(primsAtEnd);
|
||||||
this.adapter = va2.adapter;
|
this.adapter = va2.adapter;
|
||||||
if (true) throw new UnsupportedOperationException("NYI: primitive parameters must follow references; entryType = "+entryType);
|
if (true) throw new UnsupportedOperationException("NYI: primitive parameters must follow references; entryType = "+entryType);
|
||||||
this.entryPoint = MethodHandleImpl.convertArguments(
|
this.entryPoint = MethodHandleImpl.permuteArguments(
|
||||||
va2.entryPoint, primsAtEnd, entryType, primsAtEndOrder);
|
va2.entryPoint, primsAtEnd, entryType, primsAtEndOrder);
|
||||||
// example: for entryType of (int,Object,Object), the reordered
|
// example: for entryType of (int,Object,Object), the reordered
|
||||||
// type is (Object,Object,int) and the order is {1,2,0},
|
// type is (Object,Object,int) and the order is {1,2,0},
|
||||||
@ -128,7 +128,7 @@ class ToGeneric {
|
|||||||
assert(eptWithInts.parameterType(i) == int.class);
|
assert(eptWithInts.parameterType(i) == int.class);
|
||||||
MethodType nextType = midType.changeParameterType(i, int.class);
|
MethodType nextType = midType.changeParameterType(i, int.class);
|
||||||
rawEntryPoint = MethodHandleImpl.convertArguments(
|
rawEntryPoint = MethodHandleImpl.convertArguments(
|
||||||
rawEntryPoint, nextType, midType, null);
|
rawEntryPoint, nextType, midType, 0);
|
||||||
midType = nextType;
|
midType = nextType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,6 +152,10 @@ class ToGeneric {
|
|||||||
this.invoker = makeRawArgumentFilter(invoker0, rawEntryTypeInit, entryType);
|
this.invoker = makeRawArgumentFilter(invoker0, rawEntryTypeInit, entryType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
|
||||||
|
}
|
||||||
|
|
||||||
/** A generic argument list will be created by a call of type 'raw'.
|
/** A generic argument list will be created by a call of type 'raw'.
|
||||||
* The values need to be reboxed for to match 'cooked'.
|
* The values need to be reboxed for to match 'cooked'.
|
||||||
* Do this on the fly.
|
* Do this on the fly.
|
||||||
@ -171,7 +175,7 @@ class ToGeneric {
|
|||||||
invoker.type().generic(), invoker, 0, MethodHandle.class);
|
invoker.type().generic(), invoker, 0, MethodHandle.class);
|
||||||
if (filteredInvoker == null) throw new UnsupportedOperationException("NYI");
|
if (filteredInvoker == null) throw new UnsupportedOperationException("NYI");
|
||||||
}
|
}
|
||||||
MethodHandle reboxer = ValueConversions.rebox(dst, false);
|
MethodHandle reboxer = ValueConversions.rebox(dst);
|
||||||
filteredInvoker = FilterGeneric.makeArgumentFilter(1+i, reboxer, filteredInvoker);
|
filteredInvoker = FilterGeneric.makeArgumentFilter(1+i, reboxer, filteredInvoker);
|
||||||
if (filteredInvoker == null) throw new InternalError();
|
if (filteredInvoker == null) throw new InternalError();
|
||||||
}
|
}
|
||||||
@ -199,13 +203,13 @@ class ToGeneric {
|
|||||||
assert(!rret.isPrimitive());
|
assert(!rret.isPrimitive());
|
||||||
if (rret == Object.class && !mustCast)
|
if (rret == Object.class && !mustCast)
|
||||||
return null;
|
return null;
|
||||||
return ValueConversions.cast(tret, false);
|
return ValueConversions.cast(tret);
|
||||||
} else if (tret == rret) {
|
} else if (tret == rret) {
|
||||||
return ValueConversions.unbox(tret, false);
|
return ValueConversions.unbox(tret);
|
||||||
} else {
|
} else {
|
||||||
assert(rret.isPrimitive());
|
assert(rret.isPrimitive());
|
||||||
assert(tret == double.class ? rret == long.class : rret == int.class);
|
assert(tret == double.class ? rret == long.class : rret == int.class);
|
||||||
return ValueConversions.unboxRaw(tret, false);
|
return ValueConversions.unboxRaw(tret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,7 +315,7 @@ class ToGeneric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Adapter buildAdapterFromBytecodes(MethodType entryPointType) {
|
static Adapter buildAdapterFromBytecodes(MethodType entryPointType) {
|
||||||
throw new UnsupportedOperationException("NYI");
|
throw new UnsupportedOperationException("NYI: "+entryPointType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -185,7 +185,7 @@
|
|||||||
* The method handle constant produced for such a method behaves as if
|
* The method handle constant produced for such a method behaves as if
|
||||||
* it were created by {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector}.
|
* it were created by {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector}.
|
||||||
* In other words, the constant method handle will exhibit variable arity,
|
* In other words, the constant method handle will exhibit variable arity,
|
||||||
* when invoked via {@code invokeGeneric}.
|
* when invoked via {@code MethodHandle.invoke}.
|
||||||
* On the other hand, its behavior with respect to {@code invokeExact} will be the same
|
* On the other hand, its behavior with respect to {@code invokeExact} will be the same
|
||||||
* as if the {@code varargs} bit were not set.
|
* as if the {@code varargs} bit were not set.
|
||||||
* <p>
|
* <p>
|
||||||
@ -243,7 +243,7 @@
|
|||||||
* <li>optionally, one or more <a href="#args">additional static arguments</a> </li>
|
* <li>optionally, one or more <a href="#args">additional static arguments</a> </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* The method handle is then applied to the other values as if by
|
* The method handle is then applied to the other values as if by
|
||||||
* {@link java.lang.invoke.MethodHandle#invokeGeneric invokeGeneric}.
|
* {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}.
|
||||||
* The returned result must be a {@link java.lang.invoke.CallSite CallSite} (or a subclass).
|
* The returned result must be a {@link java.lang.invoke.CallSite CallSite} (or a subclass).
|
||||||
* The type of the call site's target must be exactly equal to the type
|
* The type of the call site's target must be exactly equal to the type
|
||||||
* derived from the dynamic call site's type descriptor and passed to
|
* derived from the dynamic call site's type descriptor and passed to
|
||||||
@ -251,7 +251,7 @@
|
|||||||
* The call site then becomes permanently linked to the dynamic call site.
|
* The call site then becomes permanently linked to the dynamic call site.
|
||||||
* <p>
|
* <p>
|
||||||
* As long as each bootstrap method can be correctly invoked
|
* As long as each bootstrap method can be correctly invoked
|
||||||
* by <code>invokeGeneric</code>, its detailed type is arbitrary.
|
* by <code>MethodHandle.invoke</code>, its detailed type is arbitrary.
|
||||||
* For example, the first argument could be {@code Object}
|
* For example, the first argument could be {@code Object}
|
||||||
* instead of {@code MethodHandles.Lookup}, and the return type
|
* instead of {@code MethodHandles.Lookup}, and the return type
|
||||||
* could also be {@code Object} instead of {@code CallSite}.
|
* could also be {@code Object} instead of {@code CallSite}.
|
||||||
@ -272,7 +272,7 @@
|
|||||||
* (i.e., a {@code CONSTANT_Class}, {@code CONSTANT_MethodType},
|
* (i.e., a {@code CONSTANT_Class}, {@code CONSTANT_MethodType},
|
||||||
* or {@code CONSTANT_MethodHandle} argument cannot be linked) </li>
|
* or {@code CONSTANT_MethodHandle} argument cannot be linked) </li>
|
||||||
* <li>the bootstrap method has the wrong arity,
|
* <li>the bootstrap method has the wrong arity,
|
||||||
* causing {@code invokeGeneric} to throw {@code WrongMethodTypeException} </li>
|
* causing {@code MethodHandle.invoke} to throw {@code WrongMethodTypeException} </li>
|
||||||
* <li>the bootstrap method has a wrong argument or return type </li>
|
* <li>the bootstrap method has a wrong argument or return type </li>
|
||||||
* <li>the bootstrap method invocation completes abnormally </li>
|
* <li>the bootstrap method invocation completes abnormally </li>
|
||||||
* <li>the result from the bootstrap invocation is not a reference to
|
* <li>the result from the bootstrap invocation is not a reference to
|
||||||
@ -381,10 +381,10 @@
|
|||||||
* those values will be passed as additional arguments to the method handle.
|
* those values will be passed as additional arguments to the method handle.
|
||||||
* (Note that because there is a limit of 255 arguments to any method,
|
* (Note that because there is a limit of 255 arguments to any method,
|
||||||
* at most 252 extra arguments can be supplied.)
|
* at most 252 extra arguments can be supplied.)
|
||||||
* The bootstrap method will be invoked as if by either {@code invokeGeneric}
|
* The bootstrap method will be invoked as if by either {@code MethodHandle.invoke}
|
||||||
* or {@code invokeWithArguments}. (There is no way to tell the difference.)
|
* or {@code invokeWithArguments}. (There is no way to tell the difference.)
|
||||||
* <p>
|
* <p>
|
||||||
* The normal argument conversion rules for {@code invokeGeneric} apply to all stacked arguments.
|
* The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments.
|
||||||
* For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
|
* For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
|
||||||
* If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
|
* If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
|
||||||
* then some or all of the arguments specified here may be collected into a trailing array parameter.
|
* then some or all of the arguments specified here may be collected into a trailing array parameter.
|
||||||
@ -419,8 +419,8 @@
|
|||||||
* For example, the fourth argument could be {@code MethodHandle},
|
* For example, the fourth argument could be {@code MethodHandle},
|
||||||
* if that is the type of the corresponding constant in
|
* if that is the type of the corresponding constant in
|
||||||
* the {@code CONSTANT_InvokeDynamic} entry.
|
* the {@code CONSTANT_InvokeDynamic} entry.
|
||||||
* In that case, the {@code invokeGeneric} call will pass the extra method handle
|
* In that case, the {@code MethodHandle.invoke} call will pass the extra method handle
|
||||||
* constant as an {@code Object}, but the type matching machinery of {@code invokeGeneric}
|
* constant as an {@code Object}, but the type matching machinery of {@code MethodHandle.invoke}
|
||||||
* will cast the reference back to {@code MethodHandle} before invoking the bootstrap method.
|
* will cast the reference back to {@code MethodHandle} before invoking the bootstrap method.
|
||||||
* (If a string constant were passed instead, by badly generated code, that cast would then fail,
|
* (If a string constant were passed instead, by badly generated code, that cast would then fail,
|
||||||
* resulting in a {@code BootstrapMethodError}.)
|
* resulting in a {@code BootstrapMethodError}.)
|
||||||
|
@ -31,10 +31,15 @@ import java.lang.invoke.MethodHandles.Lookup;
|
|||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ValueConversions {
|
public class ValueConversions {
|
||||||
|
private static final Class<?> THIS_CLASS = ValueConversions.class;
|
||||||
|
// Do not adjust this except for special platforms:
|
||||||
|
private static final int MAX_ARITY = Integer.getInteger(THIS_CLASS.getName()+".MAX_ARITY", 255);
|
||||||
|
|
||||||
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
|
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
|
||||||
|
|
||||||
private static EnumMap<Wrapper, MethodHandle>[] newWrapperCaches(int n) {
|
private static EnumMap<Wrapper, MethodHandle>[] newWrapperCaches(int n) {
|
||||||
@ -42,88 +47,101 @@ public class ValueConversions {
|
|||||||
EnumMap<Wrapper, MethodHandle>[] caches
|
EnumMap<Wrapper, MethodHandle>[] caches
|
||||||
= (EnumMap<Wrapper, MethodHandle>[]) new EnumMap[n]; // unchecked warning expected here
|
= (EnumMap<Wrapper, MethodHandle>[]) new EnumMap[n]; // unchecked warning expected here
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
caches[i] = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
|
caches[i] = new EnumMap<>(Wrapper.class);
|
||||||
return caches;
|
return caches;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converting references to values.
|
/// Converting references to values.
|
||||||
|
|
||||||
static int unboxInteger(Object x) {
|
// There are several levels of this unboxing conversions:
|
||||||
if (x == null) return 0; // never NPE
|
// no conversions: exactly Integer.valueOf, etc.
|
||||||
return ((Integer) x).intValue();
|
// implicit conversions sanctioned by JLS 5.1.2, etc.
|
||||||
|
// explicit conversions as allowed by explicitCastArguments
|
||||||
|
|
||||||
|
static int unboxInteger(Object x, boolean cast) {
|
||||||
|
if (x instanceof Integer)
|
||||||
|
return ((Integer) x).intValue();
|
||||||
|
return primitiveConversion(Wrapper.INT, x, cast).intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte unboxByte(Object x) {
|
static byte unboxByte(Object x, boolean cast) {
|
||||||
if (x == null) return 0; // never NPE
|
if (x instanceof Byte)
|
||||||
return ((Byte) x).byteValue();
|
return ((Byte) x).byteValue();
|
||||||
|
return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
static short unboxShort(Object x) {
|
static short unboxShort(Object x, boolean cast) {
|
||||||
if (x == null) return 0; // never NPE
|
if (x instanceof Short)
|
||||||
return ((Short) x).shortValue();
|
return ((Short) x).shortValue();
|
||||||
|
return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean unboxBoolean(Object x) {
|
static boolean unboxBoolean(Object x, boolean cast) {
|
||||||
if (x == null) return false; // never NPE
|
if (x instanceof Boolean)
|
||||||
return ((Boolean) x).booleanValue();
|
return ((Boolean) x).booleanValue();
|
||||||
|
return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char unboxCharacter(Object x) {
|
static char unboxCharacter(Object x, boolean cast) {
|
||||||
if (x == null) return 0; // never NPE
|
if (x instanceof Character)
|
||||||
return ((Character) x).charValue();
|
return ((Character) x).charValue();
|
||||||
|
return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
static long unboxLong(Object x) {
|
static long unboxLong(Object x, boolean cast) {
|
||||||
if (x == null) return 0; // never NPE
|
if (x instanceof Long)
|
||||||
return ((Long) x).longValue();
|
return ((Long) x).longValue();
|
||||||
|
return primitiveConversion(Wrapper.LONG, x, cast).longValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
static float unboxFloat(Object x) {
|
static float unboxFloat(Object x, boolean cast) {
|
||||||
if (x == null) return 0; // never NPE
|
if (x instanceof Float)
|
||||||
return ((Float) x).floatValue();
|
return ((Float) x).floatValue();
|
||||||
|
return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
static double unboxDouble(Object x) {
|
static double unboxDouble(Object x, boolean cast) {
|
||||||
if (x == null) return 0; // never NPE
|
if (x instanceof Double)
|
||||||
return ((Double) x).doubleValue();
|
return ((Double) x).doubleValue();
|
||||||
|
return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converting references to "raw" values.
|
/// Converting references to "raw" values.
|
||||||
/// A raw primitive value is always an int or long.
|
/// A raw primitive value is always an int or long.
|
||||||
|
|
||||||
static int unboxByteRaw(Object x) {
|
static int unboxByteRaw(Object x, boolean cast) {
|
||||||
return unboxByte(x);
|
return unboxByte(x, cast);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unboxShortRaw(Object x) {
|
static int unboxShortRaw(Object x, boolean cast) {
|
||||||
return unboxShort(x);
|
return unboxShort(x, cast);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unboxBooleanRaw(Object x) {
|
static int unboxBooleanRaw(Object x, boolean cast) {
|
||||||
return unboxBoolean(x) ? 1 : 0;
|
return unboxBoolean(x, cast) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unboxCharacterRaw(Object x) {
|
static int unboxCharacterRaw(Object x, boolean cast) {
|
||||||
return unboxCharacter(x);
|
return unboxCharacter(x, cast);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unboxFloatRaw(Object x) {
|
static int unboxFloatRaw(Object x, boolean cast) {
|
||||||
return Float.floatToIntBits(unboxFloat(x));
|
return Float.floatToIntBits(unboxFloat(x, cast));
|
||||||
}
|
}
|
||||||
|
|
||||||
static long unboxDoubleRaw(Object x) {
|
static long unboxDoubleRaw(Object x, boolean cast) {
|
||||||
return Double.doubleToRawLongBits(unboxDouble(x));
|
return Double.doubleToRawLongBits(unboxDouble(x, cast));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodType unboxType(Wrapper wrap, boolean raw) {
|
private static MethodType unboxType(Wrapper wrap, boolean raw) {
|
||||||
return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), wrap.wrapperType());
|
return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), Object.class, boolean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final EnumMap<Wrapper, MethodHandle>[]
|
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||||
UNBOX_CONVERSIONS = newWrapperCaches(4);
|
UNBOX_CONVERSIONS = newWrapperCaches(4);
|
||||||
|
|
||||||
private static MethodHandle unbox(Wrapper wrap, boolean exact, boolean raw) {
|
private static MethodHandle unbox(Wrapper wrap, boolean raw, boolean cast) {
|
||||||
EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
|
EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)+(raw?2:0)];
|
||||||
MethodHandle mh = cache.get(wrap);
|
MethodHandle mh = cache.get(wrap);
|
||||||
if (mh != null) {
|
if (mh != null) {
|
||||||
return mh;
|
return mh;
|
||||||
@ -136,7 +154,7 @@ public class ValueConversions {
|
|||||||
mh = raw ? ALWAYS_ZERO : IGNORE; break;
|
mh = raw ? ALWAYS_ZERO : IGNORE; break;
|
||||||
case INT: case LONG:
|
case INT: case LONG:
|
||||||
// these guys don't need separate raw channels
|
// these guys don't need separate raw channels
|
||||||
if (raw) mh = unbox(wrap, exact, false);
|
if (raw) mh = unbox(wrap, false, cast);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (mh != null) {
|
if (mh != null) {
|
||||||
@ -146,37 +164,62 @@ public class ValueConversions {
|
|||||||
// look up the method
|
// look up the method
|
||||||
String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
|
String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
|
||||||
MethodType type = unboxType(wrap, raw);
|
MethodType type = unboxType(wrap, raw);
|
||||||
if (!exact) {
|
try {
|
||||||
try {
|
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
|
||||||
// actually, type is wrong; the Java method takes Object
|
} catch (ReflectiveOperationException ex) {
|
||||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
|
mh = null;
|
||||||
} catch (ReflectiveOperationException ex) {
|
|
||||||
mh = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mh = unbox(wrap, !exact, raw).asType(type);
|
|
||||||
}
|
}
|
||||||
if (mh != null) {
|
if (mh != null) {
|
||||||
|
mh = MethodHandles.insertArguments(mh, 1, cast);
|
||||||
cache.put(wrap, mh);
|
cache.put(wrap, mh);
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + (raw ? " (raw)" : ""));
|
throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
|
||||||
|
+ (cast ? " (cast)" : "") + (raw ? " (raw)" : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodHandle unbox(Wrapper type, boolean exact) {
|
public static MethodHandle unboxCast(Wrapper type) {
|
||||||
return unbox(type, exact, false);
|
return unbox(type, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodHandle unboxRaw(Wrapper type, boolean exact) {
|
public static MethodHandle unboxRaw(Wrapper type) {
|
||||||
return unbox(type, exact, true);
|
return unbox(type, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodHandle unbox(Class<?> type, boolean exact) {
|
public static MethodHandle unbox(Class<?> type) {
|
||||||
return unbox(Wrapper.forPrimitiveType(type), exact, false);
|
return unbox(Wrapper.forPrimitiveType(type), false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodHandle unboxRaw(Class<?> type, boolean exact) {
|
public static MethodHandle unboxCast(Class<?> type) {
|
||||||
return unbox(Wrapper.forPrimitiveType(type), exact, true);
|
return unbox(Wrapper.forPrimitiveType(type), false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodHandle unboxRaw(Class<?> type) {
|
||||||
|
return unbox(Wrapper.forPrimitiveType(type), true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Primitive conversions
|
||||||
|
public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
|
||||||
|
// Maybe merge this code with Wrapper.convert/cast.
|
||||||
|
Number res = null;
|
||||||
|
if (x == null) {
|
||||||
|
if (!cast) return null;
|
||||||
|
x = wrap.zero();
|
||||||
|
}
|
||||||
|
if (x instanceof Number) {
|
||||||
|
res = (Number) x;
|
||||||
|
} else if (x instanceof Boolean) {
|
||||||
|
res = ((boolean)x ? 1 : 0);
|
||||||
|
} else if (x instanceof Character) {
|
||||||
|
res = (int)(char)x;
|
||||||
|
} else {
|
||||||
|
// this will fail with the required ClassCastException:
|
||||||
|
res = (Number) x;
|
||||||
|
}
|
||||||
|
if (!cast && !wrap.isConvertibleFrom(Wrapper.forWrapperType(x.getClass())))
|
||||||
|
// this will fail with the required ClassCastException:
|
||||||
|
res = (Number) wrap.wrapperType().cast(x);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converting primitives to references
|
/// Converting primitives to references
|
||||||
@ -285,7 +328,7 @@ public class ValueConversions {
|
|||||||
MethodType type = boxType(wrap, raw);
|
MethodType type = boxType(wrap, raw);
|
||||||
if (exact) {
|
if (exact) {
|
||||||
try {
|
try {
|
||||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
|
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
|
||||||
} catch (ReflectiveOperationException ex) {
|
} catch (ReflectiveOperationException ex) {
|
||||||
mh = null;
|
mh = null;
|
||||||
}
|
}
|
||||||
@ -296,22 +339,31 @@ public class ValueConversions {
|
|||||||
cache.put(wrap, mh);
|
cache.put(wrap, mh);
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("cannot find box adapter for " + wrap + (raw ? " (raw)" : ""));
|
throw new IllegalArgumentException("cannot find box adapter for "
|
||||||
|
+ wrap + (exact ? " (exact)" : "") + (raw ? " (raw)" : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodHandle box(Class<?> type, boolean 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, false);
|
return box(Wrapper.forPrimitiveType(type), exact, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodHandle boxRaw(Class<?> type, boolean exact) {
|
public static MethodHandle boxRaw(Class<?> type) {
|
||||||
|
boolean exact = false;
|
||||||
|
// e.g., boxShortRaw(int)Short if exact
|
||||||
|
// e.g., boxShortRaw(int)Object if !exact
|
||||||
return box(Wrapper.forPrimitiveType(type), exact, true);
|
return box(Wrapper.forPrimitiveType(type), exact, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodHandle box(Wrapper type, boolean exact) {
|
public static MethodHandle box(Wrapper type) {
|
||||||
|
boolean exact = false;
|
||||||
return box(type, exact, false);
|
return box(type, exact, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodHandle boxRaw(Wrapper type, boolean exact) {
|
public static MethodHandle boxRaw(Wrapper type) {
|
||||||
|
boolean exact = false;
|
||||||
return box(type, exact, true);
|
return box(type, exact, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,16 +371,16 @@ public class ValueConversions {
|
|||||||
|
|
||||||
static int unboxRawInteger(Object x) {
|
static int unboxRawInteger(Object x) {
|
||||||
if (x instanceof Integer)
|
if (x instanceof Integer)
|
||||||
return unboxInteger(x);
|
return (int) x;
|
||||||
else
|
else
|
||||||
return (int) unboxLong(x);
|
return (int) unboxLong(x, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Integer reboxRawInteger(Object x) {
|
static Integer reboxRawInteger(Object x) {
|
||||||
if (x instanceof Integer)
|
if (x instanceof Integer)
|
||||||
return (Integer) x;
|
return (Integer) x;
|
||||||
else
|
else
|
||||||
return (int) unboxLong(x);
|
return (int) unboxLong(x, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Byte reboxRawByte(Object x) {
|
static Byte reboxRawByte(Object x) {
|
||||||
@ -362,7 +414,7 @@ public class ValueConversions {
|
|||||||
|
|
||||||
static Double reboxRawDouble(Object x) {
|
static Double reboxRawDouble(Object x) {
|
||||||
if (x instanceof Double) return (Double) x;
|
if (x instanceof Double) return (Double) x;
|
||||||
return boxDoubleRaw(unboxLong(x));
|
return boxDoubleRaw(unboxLong(x, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodType reboxType(Wrapper wrap) {
|
private static MethodType reboxType(Wrapper wrap) {
|
||||||
@ -371,7 +423,7 @@ public class ValueConversions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final EnumMap<Wrapper, MethodHandle>[]
|
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||||
REBOX_CONVERSIONS = newWrapperCaches(2);
|
REBOX_CONVERSIONS = newWrapperCaches(1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because we normalize primitive types to reduce the number of signatures,
|
* Because we normalize primitive types to reduce the number of signatures,
|
||||||
@ -380,10 +432,10 @@ public class ValueConversions {
|
|||||||
* When the erased primitive value is then boxed into an Integer or Long,
|
* When the erased primitive value is then boxed into an Integer or Long,
|
||||||
* the final boxed primitive is sometimes required. This transformation
|
* the final boxed primitive is sometimes required. This transformation
|
||||||
* is called a "rebox". It takes an Integer or Long and produces some
|
* is called a "rebox". It takes an Integer or Long and produces some
|
||||||
* other boxed value.
|
* other boxed value, typed (inexactly) as an Object
|
||||||
*/
|
*/
|
||||||
public static MethodHandle rebox(Wrapper wrap, boolean exact) {
|
public static MethodHandle rebox(Wrapper wrap) {
|
||||||
EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[exact?1:0];
|
EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[0];
|
||||||
MethodHandle mh = cache.get(wrap);
|
MethodHandle mh = cache.get(wrap);
|
||||||
if (mh != null) {
|
if (mh != null) {
|
||||||
return mh;
|
return mh;
|
||||||
@ -402,14 +454,11 @@ public class ValueConversions {
|
|||||||
// look up the method
|
// look up the method
|
||||||
String name = "reboxRaw" + wrap.simpleName();
|
String name = "reboxRaw" + wrap.simpleName();
|
||||||
MethodType type = reboxType(wrap);
|
MethodType type = reboxType(wrap);
|
||||||
if (exact) {
|
try {
|
||||||
try {
|
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
|
||||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
|
mh = mh.asType(IDENTITY.type());
|
||||||
} catch (ReflectiveOperationException ex) {
|
} catch (ReflectiveOperationException ex) {
|
||||||
mh = null;
|
mh = null;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mh = rebox(wrap, !exact).asType(IDENTITY.type());
|
|
||||||
}
|
}
|
||||||
if (mh != null) {
|
if (mh != null) {
|
||||||
cache.put(wrap, mh);
|
cache.put(wrap, mh);
|
||||||
@ -418,8 +467,8 @@ public class ValueConversions {
|
|||||||
throw new IllegalArgumentException("cannot find rebox adapter for " + wrap);
|
throw new IllegalArgumentException("cannot find rebox adapter for " + wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodHandle rebox(Class<?> type, boolean exact) {
|
public static MethodHandle rebox(Class<?> type) {
|
||||||
return rebox(Wrapper.forPrimitiveType(type), exact);
|
return rebox(Wrapper.forPrimitiveType(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Width-changing conversions between int and long.
|
/// Width-changing conversions between int and long.
|
||||||
@ -486,9 +535,10 @@ public class ValueConversions {
|
|||||||
case VOID:
|
case VOID:
|
||||||
mh = EMPTY;
|
mh = EMPTY;
|
||||||
break;
|
break;
|
||||||
|
case OBJECT:
|
||||||
case INT: case LONG: case FLOAT: case DOUBLE:
|
case INT: case LONG: case FLOAT: case DOUBLE:
|
||||||
try {
|
try {
|
||||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
|
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.simpleName(), type);
|
||||||
} catch (ReflectiveOperationException ex) {
|
} catch (ReflectiveOperationException ex) {
|
||||||
mh = null;
|
mh = null;
|
||||||
}
|
}
|
||||||
@ -592,7 +642,7 @@ public class ValueConversions {
|
|||||||
return t.cast(x);
|
return t.cast(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY;
|
private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY, NEW_ARRAY;
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
MethodType idType = MethodType.genericMethodType(1);
|
MethodType idType = MethodType.genericMethodType(1);
|
||||||
@ -600,40 +650,56 @@ public class ValueConversions {
|
|||||||
MethodType alwaysZeroType = idType.changeReturnType(int.class);
|
MethodType alwaysZeroType = idType.changeReturnType(int.class);
|
||||||
MethodType ignoreType = idType.changeReturnType(void.class);
|
MethodType ignoreType = idType.changeReturnType(void.class);
|
||||||
MethodType zeroObjectType = MethodType.genericMethodType(0);
|
MethodType zeroObjectType = MethodType.genericMethodType(0);
|
||||||
IDENTITY = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", idType);
|
IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
|
||||||
IDENTITY_I = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(int.class, int.class));
|
IDENTITY_I = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(int.class, int.class));
|
||||||
IDENTITY_J = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(long.class, long.class));
|
IDENTITY_J = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(long.class, long.class));
|
||||||
//CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
|
//CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
|
||||||
CAST_REFERENCE = IMPL_LOOKUP.findStatic(ValueConversions.class, "castReference", castType);
|
CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType);
|
||||||
ALWAYS_NULL = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysNull", idType);
|
ALWAYS_NULL = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysNull", idType);
|
||||||
ALWAYS_ZERO = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysZero", alwaysZeroType);
|
ALWAYS_ZERO = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysZero", alwaysZeroType);
|
||||||
ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType);
|
ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType);
|
||||||
IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType);
|
IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
|
||||||
EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1));
|
EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
|
||||||
} catch (Exception ex) {
|
NEW_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "newArray", MethodType.methodType(Object[].class, int.class));
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException ex) {
|
||||||
Error err = new InternalError("uncaught exception");
|
Error err = new InternalError("uncaught exception");
|
||||||
err.initCause(ex);
|
err.initCause(ex);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final EnumMap<Wrapper, MethodHandle> WRAPPER_CASTS
|
// Varargs methods need to be in a separately initialized class, to bootstrapping problems.
|
||||||
= new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
|
static class LazyStatics {
|
||||||
|
private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST;
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
//MAKE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeArray", MethodType.methodType(Object[].class, Object[].class));
|
||||||
|
COPY_AS_REFERENCE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsReferenceArray", MethodType.methodType(Object[].class, Class.class, Object[].class));
|
||||||
|
COPY_AS_PRIMITIVE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class));
|
||||||
|
MAKE_LIST = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeList", MethodType.methodType(List.class, Object[].class));
|
||||||
|
} catch (ReflectiveOperationException ex) {
|
||||||
|
Error err = new InternalError("uncaught exception");
|
||||||
|
err.initCause(ex);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final EnumMap<Wrapper, MethodHandle> EXACT_WRAPPER_CASTS
|
private static final EnumMap<Wrapper, MethodHandle>[] WRAPPER_CASTS
|
||||||
= new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
|
= newWrapperCaches(2);
|
||||||
|
|
||||||
/** Return a method that casts its sole argument (an Object) to the given type
|
/** Return a method that casts its sole argument (an Object) to the given type
|
||||||
* and returns it as the given type (if exact is true), or as plain Object (if erase is true).
|
* and returns it as the given type (if exact is true), or as plain Object (if erase is true).
|
||||||
*/
|
*/
|
||||||
public static MethodHandle cast(Class<?> type, boolean exact) {
|
public static MethodHandle cast(Class<?> type) {
|
||||||
|
boolean exact = false;
|
||||||
if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type);
|
if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type);
|
||||||
MethodHandle mh = null;
|
MethodHandle mh = null;
|
||||||
Wrapper wrap = null;
|
Wrapper wrap = null;
|
||||||
EnumMap<Wrapper, MethodHandle> cache = null;
|
EnumMap<Wrapper, MethodHandle> cache = null;
|
||||||
if (Wrapper.isWrapperType(type)) {
|
if (Wrapper.isWrapperType(type)) {
|
||||||
wrap = Wrapper.forWrapperType(type);
|
wrap = Wrapper.forWrapperType(type);
|
||||||
cache = (exact ? EXACT_WRAPPER_CASTS : WRAPPER_CASTS);
|
cache = WRAPPER_CASTS[exact?1:0];
|
||||||
mh = cache.get(wrap);
|
mh = cache.get(wrap);
|
||||||
if (mh != null) return mh;
|
if (mh != null) return mh;
|
||||||
}
|
}
|
||||||
@ -673,7 +739,7 @@ public class ValueConversions {
|
|||||||
if (wrap != Wrapper.VOID)
|
if (wrap != Wrapper.VOID)
|
||||||
type = type.appendParameterTypes(wrap.primitiveType());
|
type = type.appendParameterTypes(wrap.primitiveType());
|
||||||
try {
|
try {
|
||||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type);
|
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type);
|
||||||
} catch (ReflectiveOperationException ex) {
|
} catch (ReflectiveOperationException ex) {
|
||||||
mh = null;
|
mh = null;
|
||||||
}
|
}
|
||||||
@ -692,6 +758,210 @@ public class ValueConversions {
|
|||||||
throw new IllegalArgumentException("cannot find identity for " + wrap);
|
throw new IllegalArgumentException("cannot find identity for " + wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Float/non-float conversions.
|
||||||
|
|
||||||
|
static float doubleToFloat(double x) {
|
||||||
|
return (float) x;
|
||||||
|
}
|
||||||
|
static double floatToDouble(float x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// narrow double to integral type
|
||||||
|
static long doubleToLong(double x) {
|
||||||
|
return (long) x;
|
||||||
|
}
|
||||||
|
static int doubleToInt(double x) {
|
||||||
|
return (int) x;
|
||||||
|
}
|
||||||
|
static short doubleToShort(double x) {
|
||||||
|
return (short) x;
|
||||||
|
}
|
||||||
|
static char doubleToChar(double x) {
|
||||||
|
return (char) x;
|
||||||
|
}
|
||||||
|
static byte doubleToByte(double x) {
|
||||||
|
return (byte) x;
|
||||||
|
}
|
||||||
|
static boolean doubleToBoolean(double x) {
|
||||||
|
return toBoolean((byte) x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// narrow float to integral type
|
||||||
|
static long floatToLong(float x) {
|
||||||
|
return (long) x;
|
||||||
|
}
|
||||||
|
static int floatToInt(float x) {
|
||||||
|
return (int) x;
|
||||||
|
}
|
||||||
|
static short floatToShort(float x) {
|
||||||
|
return (short) x;
|
||||||
|
}
|
||||||
|
static char floatToChar(float x) {
|
||||||
|
return (char) x;
|
||||||
|
}
|
||||||
|
static byte floatToByte(float x) {
|
||||||
|
return (byte) x;
|
||||||
|
}
|
||||||
|
static boolean floatToBoolean(float x) {
|
||||||
|
return toBoolean((byte) x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// widen integral type to double
|
||||||
|
static double longToDouble(long x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
static double intToDouble(int x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
static double shortToDouble(short x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
static double charToDouble(char x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
static double byteToDouble(byte x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
static double booleanToDouble(boolean x) {
|
||||||
|
return fromBoolean(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// widen integral type to float
|
||||||
|
static float longToFloat(long x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
static float intToFloat(int x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
static float shortToFloat(short x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
static float charToFloat(char x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
static float byteToFloat(byte x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
static float booleanToFloat(boolean x) {
|
||||||
|
return fromBoolean(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean toBoolean(byte x) {
|
||||||
|
// see javadoc for MethodHandles.explicitCastArguments
|
||||||
|
return ((x & 1) != 0);
|
||||||
|
}
|
||||||
|
static byte fromBoolean(boolean x) {
|
||||||
|
// see javadoc for MethodHandles.explicitCastArguments
|
||||||
|
return (x ? (byte)1 : (byte)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||||
|
CONVERT_FLOAT_FUNCTIONS = newWrapperCaches(4);
|
||||||
|
|
||||||
|
static MethodHandle convertFloatFunction(Wrapper wrap, boolean toFloat, boolean doubleSize) {
|
||||||
|
EnumMap<Wrapper, MethodHandle> cache = CONVERT_FLOAT_FUNCTIONS[(toFloat?1:0)+(doubleSize?2:0)];
|
||||||
|
MethodHandle mh = cache.get(wrap);
|
||||||
|
if (mh != null) {
|
||||||
|
return mh;
|
||||||
|
}
|
||||||
|
// slow path
|
||||||
|
Wrapper fwrap = (doubleSize ? Wrapper.DOUBLE : Wrapper.FLOAT);
|
||||||
|
Class<?> fix = wrap.primitiveType();
|
||||||
|
Class<?> flt = (doubleSize ? double.class : float.class);
|
||||||
|
Class<?> src = toFloat ? fix : flt;
|
||||||
|
Class<?> dst = toFloat ? flt : fix;
|
||||||
|
if (src == dst) return identity(wrap);
|
||||||
|
MethodType type = MethodType.methodType(dst, src);
|
||||||
|
switch (wrap) {
|
||||||
|
case VOID:
|
||||||
|
mh = toFloat ? zeroConstantFunction(fwrap) : MethodHandles.dropArguments(EMPTY, 0, flt);
|
||||||
|
break;
|
||||||
|
case OBJECT:
|
||||||
|
mh = toFloat ? unbox(flt) : box(flt);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
try {
|
||||||
|
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type);
|
||||||
|
} catch (ReflectiveOperationException ex) {
|
||||||
|
mh = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mh != null) {
|
||||||
|
assert(mh.type() == type) : mh;
|
||||||
|
cache.put(wrap, mh);
|
||||||
|
return mh;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("cannot find float conversion constant for " +
|
||||||
|
src.getSimpleName()+" -> "+dst.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodHandle convertFromFloat(Class<?> fixType) {
|
||||||
|
Wrapper wrap = Wrapper.forPrimitiveType(fixType);
|
||||||
|
return convertFloatFunction(wrap, false, false);
|
||||||
|
}
|
||||||
|
public static MethodHandle convertFromDouble(Class<?> fixType) {
|
||||||
|
Wrapper wrap = Wrapper.forPrimitiveType(fixType);
|
||||||
|
return convertFloatFunction(wrap, false, true);
|
||||||
|
}
|
||||||
|
public static MethodHandle convertToFloat(Class<?> fixType) {
|
||||||
|
Wrapper wrap = Wrapper.forPrimitiveType(fixType);
|
||||||
|
return convertFloatFunction(wrap, true, false);
|
||||||
|
}
|
||||||
|
public static MethodHandle convertToDouble(Class<?> fixType) {
|
||||||
|
Wrapper wrap = Wrapper.forPrimitiveType(fixType);
|
||||||
|
return convertFloatFunction(wrap, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String capitalize(String x) {
|
||||||
|
return Character.toUpperCase(x.charAt(0))+x.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection of multiple arguments.
|
||||||
|
|
||||||
|
public static Object convertArrayElements(Class<?> arrayType, Object array) {
|
||||||
|
Class<?> src = array.getClass().getComponentType();
|
||||||
|
Class<?> dst = arrayType.getComponentType();
|
||||||
|
if (src == null || dst == null) throw new IllegalArgumentException("not array type");
|
||||||
|
Wrapper sw = (src.isPrimitive() ? Wrapper.forPrimitiveType(src) : null);
|
||||||
|
Wrapper dw = (dst.isPrimitive() ? Wrapper.forPrimitiveType(dst) : null);
|
||||||
|
int length;
|
||||||
|
if (sw == null) {
|
||||||
|
Object[] a = (Object[]) array;
|
||||||
|
length = a.length;
|
||||||
|
if (dw == null)
|
||||||
|
return Arrays.copyOf(a, length, arrayType.asSubclass(Object[].class));
|
||||||
|
Object res = dw.makeArray(length);
|
||||||
|
dw.copyArrayUnboxing(a, 0, res, 0, length);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
length = java.lang.reflect.Array.getLength(array);
|
||||||
|
Object[] res;
|
||||||
|
if (dw == null) {
|
||||||
|
res = Arrays.copyOf(NO_ARGS_ARRAY, length, arrayType.asSubclass(Object[].class));
|
||||||
|
} else {
|
||||||
|
res = new Object[length];
|
||||||
|
}
|
||||||
|
sw.copyArrayBoxing(array, 0, res, 0, length);
|
||||||
|
if (dw == null) return res;
|
||||||
|
Object a = dw.makeArray(length);
|
||||||
|
dw.copyArrayUnboxing(res, 0, a, 0, length);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle findCollector(String name, int nargs, Class<?> rtype, Class<?>... ptypes) {
|
||||||
|
MethodType type = MethodType.genericMethodType(nargs)
|
||||||
|
.changeReturnType(rtype)
|
||||||
|
.insertParameterTypes(0, ptypes);
|
||||||
|
try {
|
||||||
|
return IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
|
||||||
|
} catch (ReflectiveOperationException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final Object[] NO_ARGS_ARRAY = {};
|
private static final Object[] NO_ARGS_ARRAY = {};
|
||||||
private static Object[] makeArray(Object... args) { return args; }
|
private static Object[] makeArray(Object... args) { return args; }
|
||||||
private static Object[] array() { return NO_ARGS_ARRAY; }
|
private static Object[] array() { return NO_ARGS_ARRAY; }
|
||||||
@ -723,36 +993,176 @@ public class ValueConversions {
|
|||||||
Object a4, Object a5, Object a6, Object a7,
|
Object a4, Object a5, Object a6, Object a7,
|
||||||
Object a8, Object a9)
|
Object a8, Object a9)
|
||||||
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
|
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
|
||||||
static MethodHandle[] makeArrays() {
|
private static MethodHandle[] makeArrays() {
|
||||||
ArrayList<MethodHandle> arrays = new ArrayList<MethodHandle>();
|
ArrayList<MethodHandle> mhs = new ArrayList<>();
|
||||||
MethodHandles.Lookup lookup = IMPL_LOOKUP;
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int nargs = arrays.size();
|
MethodHandle mh = findCollector("array", mhs.size(), Object[].class);
|
||||||
MethodType type = MethodType.genericMethodType(nargs).changeReturnType(Object[].class);
|
if (mh == null) break;
|
||||||
String name = "array";
|
mhs.add(mh);
|
||||||
MethodHandle array = null;
|
|
||||||
try {
|
|
||||||
array = lookup.findStatic(ValueConversions.class, name, type);
|
|
||||||
} catch (ReflectiveOperationException ex) {
|
|
||||||
}
|
|
||||||
if (array == null) break;
|
|
||||||
arrays.add(array);
|
|
||||||
}
|
}
|
||||||
assert(arrays.size() == 11); // current number of methods
|
assert(mhs.size() == 11); // current number of methods
|
||||||
return arrays.toArray(new MethodHandle[0]);
|
return mhs.toArray(new MethodHandle[MAX_ARITY+1]);
|
||||||
|
}
|
||||||
|
private static final MethodHandle[] ARRAYS = makeArrays();
|
||||||
|
|
||||||
|
// mh-fill versions of the above:
|
||||||
|
private static Object[] newArray(int len) { return new Object[len]; }
|
||||||
|
private static void fillWithArguments(Object[] a, int pos, Object... args) {
|
||||||
|
System.arraycopy(args, 0, a, pos, args.length);
|
||||||
|
}
|
||||||
|
// using Integer pos instead of int pos to avoid bootstrapping problems
|
||||||
|
private static Object[] fillArray(Object[] a, Integer pos, Object a0)
|
||||||
|
{ fillWithArguments(a, pos, a0); return a; }
|
||||||
|
private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1)
|
||||||
|
{ fillWithArguments(a, pos, a0, a1); return a; }
|
||||||
|
private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2)
|
||||||
|
{ fillWithArguments(a, pos, a0, a1, a2); return a; }
|
||||||
|
private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3)
|
||||||
|
{ fillWithArguments(a, pos, a0, a1, a2, a3); return a; }
|
||||||
|
private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
|
||||||
|
Object a4)
|
||||||
|
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; }
|
||||||
|
private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
|
||||||
|
Object a4, Object a5)
|
||||||
|
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; }
|
||||||
|
private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
|
||||||
|
Object a4, Object a5, Object a6)
|
||||||
|
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; }
|
||||||
|
private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
|
||||||
|
Object a4, Object a5, Object a6, Object a7)
|
||||||
|
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; }
|
||||||
|
private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
|
||||||
|
Object a4, Object a5, Object a6, Object a7,
|
||||||
|
Object a8)
|
||||||
|
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; }
|
||||||
|
private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
|
||||||
|
Object a4, Object a5, Object a6, Object a7,
|
||||||
|
Object a8, Object a9)
|
||||||
|
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; }
|
||||||
|
private static MethodHandle[] makeFillArrays() {
|
||||||
|
ArrayList<MethodHandle> mhs = new ArrayList<>();
|
||||||
|
mhs.add(null); // there is no empty fill; at least a0 is required
|
||||||
|
for (;;) {
|
||||||
|
MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Object[].class, Integer.class);
|
||||||
|
if (mh == null) break;
|
||||||
|
mhs.add(mh);
|
||||||
|
}
|
||||||
|
assert(mhs.size() == 11); // current number of methods
|
||||||
|
return mhs.toArray(new MethodHandle[0]);
|
||||||
|
}
|
||||||
|
private static final MethodHandle[] FILL_ARRAYS = makeFillArrays();
|
||||||
|
|
||||||
|
private static Object[] copyAsReferenceArray(Class<? extends Object[]> arrayType, Object... a) {
|
||||||
|
return Arrays.copyOf(a, a.length, arrayType);
|
||||||
|
}
|
||||||
|
private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
|
||||||
|
Object a = w.makeArray(boxes.length);
|
||||||
|
w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length);
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
static final MethodHandle[] ARRAYS = makeArrays();
|
|
||||||
|
|
||||||
/** Return a method handle that takes the indicated number of Object
|
/** Return a method handle that takes the indicated number of Object
|
||||||
* arguments and returns an Object array of them, as if for varargs.
|
* arguments and returns an Object array of them, as if for varargs.
|
||||||
*/
|
*/
|
||||||
public static MethodHandle varargsArray(int nargs) {
|
public static MethodHandle varargsArray(int nargs) {
|
||||||
if (nargs < ARRAYS.length)
|
MethodHandle mh = ARRAYS[nargs];
|
||||||
return ARRAYS[nargs];
|
if (mh != null) return mh;
|
||||||
// else need to spin bytecode or do something else fancy
|
mh = findCollector("array", nargs, Object[].class);
|
||||||
throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs);
|
if (mh != null) return ARRAYS[nargs] = mh;
|
||||||
|
MethodHandle producer = filler(0); // identity function produces result
|
||||||
|
return ARRAYS[nargs] = buildVarargsArray(producer, nargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MethodHandle buildVarargsArray(MethodHandle producer, int nargs) {
|
||||||
|
// Build up the result mh as a sequence of fills like this:
|
||||||
|
// producer(fill(fill(fill(newArray(23),0,x1..x10),10,x11..x20),20,x21..x23))
|
||||||
|
// The various fill(_,10*I,___*[J]) are reusable.
|
||||||
|
MethodHandle filler = filler(nargs);
|
||||||
|
MethodHandle mh = producer;
|
||||||
|
mh = MethodHandles.dropArguments(mh, 1, filler.type().parameterList());
|
||||||
|
mh = MethodHandles.foldArguments(mh, filler);
|
||||||
|
mh = MethodHandles.foldArguments(mh, buildNewArray(nargs));
|
||||||
|
return mh;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle buildNewArray(int nargs) {
|
||||||
|
return MethodHandles.insertArguments(NEW_ARRAY, 0, (int) nargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final MethodHandle[] FILLERS = new MethodHandle[MAX_ARITY+1];
|
||||||
|
// filler(N).invoke(a, arg0..arg[N-1]) fills a[0]..a[N-1]
|
||||||
|
private static MethodHandle filler(int nargs) {
|
||||||
|
MethodHandle filler = FILLERS[nargs];
|
||||||
|
if (filler != null) return filler;
|
||||||
|
return FILLERS[nargs] = buildFiller(nargs);
|
||||||
|
}
|
||||||
|
private static MethodHandle buildFiller(int nargs) {
|
||||||
|
if (nargs == 0)
|
||||||
|
return MethodHandles.identity(Object[].class);
|
||||||
|
final int CHUNK = (FILL_ARRAYS.length - 1);
|
||||||
|
int rightLen = nargs % CHUNK;
|
||||||
|
int leftLen = nargs - rightLen;
|
||||||
|
if (rightLen == 0) {
|
||||||
|
leftLen = nargs - (rightLen = CHUNK);
|
||||||
|
if (FILLERS[leftLen] == null) {
|
||||||
|
// build some precursors from left to right
|
||||||
|
for (int j = 0; j < leftLen; j += CHUNK) filler(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MethodHandle leftFill = filler(leftLen); // recursive fill
|
||||||
|
MethodHandle rightFill = FILL_ARRAYS[rightLen];
|
||||||
|
rightFill = MethodHandles.insertArguments(rightFill, 1, (int) leftLen); // [leftLen..nargs-1]
|
||||||
|
|
||||||
|
// Combine the two fills: right(left(newArray(nargs), x1..x20), x21..x23)
|
||||||
|
MethodHandle mh = filler(0); // identity function produces result
|
||||||
|
mh = MethodHandles.dropArguments(mh, 1, rightFill.type().parameterList());
|
||||||
|
mh = MethodHandles.foldArguments(mh, rightFill);
|
||||||
|
if (leftLen > 0) {
|
||||||
|
mh = MethodHandles.dropArguments(mh, 1, leftFill.type().parameterList());
|
||||||
|
mh = MethodHandles.foldArguments(mh, leftFill);
|
||||||
|
}
|
||||||
|
return mh;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-polymorphic version of varargs maker.
|
||||||
|
private static final ClassValue<MethodHandle[]> TYPED_COLLECTORS
|
||||||
|
= new ClassValue<MethodHandle[]>() {
|
||||||
|
protected MethodHandle[] computeValue(Class<?> type) {
|
||||||
|
return new MethodHandle[256];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Return a method handle that takes the indicated number of
|
||||||
|
* typed arguments and returns an array of them.
|
||||||
|
* The type argument is the array type.
|
||||||
|
*/
|
||||||
|
public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
|
||||||
|
Class<?> elemType = arrayType.getComponentType();
|
||||||
|
if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType);
|
||||||
|
// FIXME: Need more special casing and caching here.
|
||||||
|
if (elemType == Object.class)
|
||||||
|
return varargsArray(nargs);
|
||||||
|
// other cases: primitive arrays, subtypes of Object[]
|
||||||
|
MethodHandle cache[] = TYPED_COLLECTORS.get(elemType);
|
||||||
|
MethodHandle mh = nargs < cache.length ? cache[nargs] : null;
|
||||||
|
if (mh != null) return mh;
|
||||||
|
MethodHandle producer = buildArrayProducer(arrayType);
|
||||||
|
mh = buildVarargsArray(producer, nargs);
|
||||||
|
mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
|
||||||
|
cache[nargs] = mh;
|
||||||
|
return mh;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle buildArrayProducer(Class<?> arrayType) {
|
||||||
|
Class<?> elemType = arrayType.getComponentType();
|
||||||
|
if (elemType.isPrimitive())
|
||||||
|
return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType));
|
||||||
|
else
|
||||||
|
return LazyStatics.COPY_AS_REFERENCE_ARRAY.bindTo(arrayType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// List version of varargs maker.
|
||||||
|
|
||||||
private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
|
private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
|
||||||
private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
|
private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
|
||||||
private static List<Object> list() { return NO_ARGS_LIST; }
|
private static List<Object> list() { return NO_ARGS_LIST; }
|
||||||
@ -784,34 +1194,29 @@ public class ValueConversions {
|
|||||||
Object a4, Object a5, Object a6, Object a7,
|
Object a4, Object a5, Object a6, Object a7,
|
||||||
Object a8, Object a9)
|
Object a8, Object a9)
|
||||||
{ return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
|
{ return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
|
||||||
static MethodHandle[] makeLists() {
|
private static MethodHandle[] makeLists() {
|
||||||
ArrayList<MethodHandle> arrays = new ArrayList<MethodHandle>();
|
ArrayList<MethodHandle> mhs = new ArrayList<>();
|
||||||
MethodHandles.Lookup lookup = IMPL_LOOKUP;
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int nargs = arrays.size();
|
MethodHandle mh = findCollector("list", mhs.size(), List.class);
|
||||||
MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class);
|
if (mh == null) break;
|
||||||
String name = "list";
|
mhs.add(mh);
|
||||||
MethodHandle array = null;
|
|
||||||
try {
|
|
||||||
array = lookup.findStatic(ValueConversions.class, name, type);
|
|
||||||
} catch (ReflectiveOperationException ex) {
|
|
||||||
}
|
|
||||||
if (array == null) break;
|
|
||||||
arrays.add(array);
|
|
||||||
}
|
}
|
||||||
assert(arrays.size() == 11); // current number of methods
|
assert(mhs.size() == 11); // current number of methods
|
||||||
return arrays.toArray(new MethodHandle[0]);
|
return mhs.toArray(new MethodHandle[MAX_ARITY+1]);
|
||||||
}
|
}
|
||||||
static final MethodHandle[] LISTS = makeLists();
|
private static final MethodHandle[] LISTS = makeLists();
|
||||||
|
|
||||||
/** Return a method handle that takes the indicated number of Object
|
/** Return a method handle that takes the indicated number of Object
|
||||||
* arguments and returns List.
|
* arguments and returns a List.
|
||||||
*/
|
*/
|
||||||
public static MethodHandle varargsList(int nargs) {
|
public static MethodHandle varargsList(int nargs) {
|
||||||
if (nargs < LISTS.length)
|
MethodHandle mh = LISTS[nargs];
|
||||||
return LISTS[nargs];
|
if (mh != null) return mh;
|
||||||
// else need to spin bytecode or do something else fancy
|
mh = findCollector("list", nargs, List.class);
|
||||||
throw new UnsupportedOperationException("NYI");
|
if (mh != null) return LISTS[nargs] = mh;
|
||||||
|
return LISTS[nargs] = buildVarargsList(nargs);
|
||||||
|
}
|
||||||
|
private static MethodHandle buildVarargsList(int nargs) {
|
||||||
|
return MethodHandles.filterReturnValue(varargsArray(nargs), LazyStatics.MAKE_LIST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,9 +54,15 @@ public class VerifyType {
|
|||||||
if (dst == void.class) return true; // drop any return value
|
if (dst == void.class) return true; // drop any return value
|
||||||
if (isNullType(src)) return !dst.isPrimitive();
|
if (isNullType(src)) return !dst.isPrimitive();
|
||||||
if (!src.isPrimitive()) return dst.isAssignableFrom(src);
|
if (!src.isPrimitive()) return dst.isAssignableFrom(src);
|
||||||
|
if (!dst.isPrimitive()) return false;
|
||||||
// Verifier allows an int to carry byte, short, char, or even boolean:
|
// Verifier allows an int to carry byte, short, char, or even boolean:
|
||||||
if (dst == int.class) return Wrapper.forPrimitiveType(src).isSubwordOrInt();
|
Wrapper sw = Wrapper.forPrimitiveType(src);
|
||||||
return false;
|
if (dst == int.class) return sw.isSubwordOrInt();
|
||||||
|
Wrapper dw = Wrapper.forPrimitiveType(dst);
|
||||||
|
if (!sw.isSubwordOrInt()) return false;
|
||||||
|
if (!dw.isSubwordOrInt()) return false;
|
||||||
|
if (!dw.isSigned() && sw.isSigned()) return false;
|
||||||
|
return dw.bitWidth() > sw.bitWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,6 +161,7 @@ public class VerifyType {
|
|||||||
return -1; // truncation may be required
|
return -1; // truncation may be required
|
||||||
if (!dw.isSigned() && sw.isSigned())
|
if (!dw.isSigned() && sw.isSigned())
|
||||||
return -1; // sign elimination may be required
|
return -1; // sign elimination may be required
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
if (src == float.class || dst == float.class) {
|
if (src == float.class || dst == float.class) {
|
||||||
if (src == double.class || dst == double.class)
|
if (src == double.class || dst == double.class)
|
||||||
|
@ -26,37 +26,47 @@
|
|||||||
package sun.invoke.util;
|
package sun.invoke.util;
|
||||||
|
|
||||||
public enum Wrapper {
|
public enum Wrapper {
|
||||||
BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)),
|
BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, new boolean[0], Format.unsigned(1)),
|
||||||
// These must be in the order defined for widening primitive conversions in JLS 5.1.2
|
// These must be in the order defined for widening primitive conversions in JLS 5.1.2
|
||||||
BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)),
|
BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed(8)),
|
||||||
SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)),
|
SHORT(Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed(16)),
|
||||||
CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)),
|
CHAR(Character.class, char.class, 'C', (Character)(char)0, new char[0], Format.unsigned(16)),
|
||||||
INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)),
|
INT(Integer.class, int.class, 'I', (Integer)(int)0, new int[0], Format.signed(32)),
|
||||||
LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)),
|
LONG(Long.class, long.class, 'J', (Long)(long)0, new long[0], Format.signed(64)),
|
||||||
FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)),
|
FLOAT(Float.class, float.class, 'F', (Float)(float)0, new float[0], Format.floating(32)),
|
||||||
DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)),
|
DOUBLE(Double.class, double.class, 'D', (Double)(double)0, new double[0], Format.floating(64)),
|
||||||
//NULL(Null.class, null.class, 'N', null, Format.other(1)),
|
//NULL(Null.class, null.class, 'N', null, null, Format.other(1)),
|
||||||
OBJECT(Object.class, Object.class, 'L', null, Format.other(1)),
|
OBJECT(Object.class, Object.class, 'L', null, new Object[0], Format.other(1)),
|
||||||
// VOID must be the last type, since it is "assignable" from any other type:
|
// VOID must be the last type, since it is "assignable" from any other type:
|
||||||
VOID(Void.class, void.class, 'V', null, Format.other(0)),
|
VOID(Void.class, void.class, 'V', null, null, Format.other(0)),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final Class<?> wrapperType;
|
private final Class<?> wrapperType;
|
||||||
private final Class<?> primitiveType;
|
private final Class<?> primitiveType;
|
||||||
private final char basicTypeChar;
|
private final char basicTypeChar;
|
||||||
private final Object zero;
|
private final Object zero;
|
||||||
|
private final Object emptyArray;
|
||||||
private final int format;
|
private final int format;
|
||||||
private final String simpleName;
|
private final String simpleName;
|
||||||
|
|
||||||
private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, int format) {
|
private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, Object emptyArray, int format) {
|
||||||
this.wrapperType = wtype;
|
this.wrapperType = wtype;
|
||||||
this.primitiveType = ptype;
|
this.primitiveType = ptype;
|
||||||
this.basicTypeChar = tchar;
|
this.basicTypeChar = tchar;
|
||||||
this.zero = zero;
|
this.zero = zero;
|
||||||
|
this.emptyArray = emptyArray;
|
||||||
this.format = format;
|
this.format = format;
|
||||||
this.simpleName = wtype.getSimpleName();
|
this.simpleName = wtype.getSimpleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** For debugging, give the details of this wrapper. */
|
||||||
|
public String detailString() {
|
||||||
|
return simpleName+
|
||||||
|
java.util.Arrays.asList(wrapperType, primitiveType,
|
||||||
|
basicTypeChar, zero,
|
||||||
|
"0x"+Integer.toHexString(format));
|
||||||
|
}
|
||||||
|
|
||||||
private static abstract class Format {
|
private static abstract class Format {
|
||||||
static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12;
|
static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12;
|
||||||
static final int
|
static final int
|
||||||
@ -114,16 +124,18 @@ public enum Wrapper {
|
|||||||
public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; }
|
public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; }
|
||||||
/** Is the wrapped type either float or double? */
|
/** Is the wrapped type either float or double? */
|
||||||
public boolean isFloating() { return format >= Format.FLOAT; }
|
public boolean isFloating() { return format >= Format.FLOAT; }
|
||||||
|
/** Is the wrapped type either void or a reference? */
|
||||||
|
public boolean isOther() { return (format & ~Format.SLOT_MASK) == 0; }
|
||||||
|
|
||||||
/** Does the JVM verifier allow a variable of this wrapper's
|
/** Does the JLS 5.1.2 allow a variable of this wrapper's
|
||||||
* primitive type to be assigned from a value of the given wrapper's primitive type?
|
* primitive type to be assigned from a value of the given wrapper's primitive type?
|
||||||
* Cases:
|
* Cases:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>unboxing followed by widening primitive conversion
|
* <li>unboxing followed by widening primitive conversion
|
||||||
* <li>any type converted to {@code void}
|
* <li>any type converted to {@code void} (i.e., dropping a method call's value)
|
||||||
* <li>boxing conversion followed by widening reference conversion to {@code Object}
|
* <li>boxing conversion followed by widening reference conversion to {@code Object}
|
||||||
* <li>conversion of {@code boolean} to any type
|
|
||||||
* </ul>
|
* </ul>
|
||||||
|
* These are the cases allowed by MethodHandle.asType and convertArguments.
|
||||||
*/
|
*/
|
||||||
public boolean isConvertibleFrom(Wrapper source) {
|
public boolean isConvertibleFrom(Wrapper source) {
|
||||||
if (this == source) return true;
|
if (this == source) return true;
|
||||||
@ -131,13 +143,75 @@ public enum Wrapper {
|
|||||||
// At best, this is a narrowing conversion.
|
// At best, this is a narrowing conversion.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((this.format ^ source.format) == (Format.SHORT ^ Format.CHAR)) {
|
// All conversions are allowed in the enum order between floats and signed ints.
|
||||||
assert (this == SHORT && source == CHAR) || (this == CHAR && source == SHORT);
|
// First detect non-signed non-float types (boolean, char, Object, void).
|
||||||
|
boolean floatOrSigned = (((this.format & source.format) & Format.SIGNED) != 0);
|
||||||
|
if (!floatOrSigned) {
|
||||||
|
if (this.isOther()) return true;
|
||||||
|
// can convert char to int or wider, but nothing else
|
||||||
|
if (source.format == Format.CHAR) return true;
|
||||||
|
// no other conversions are classified as widening
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// All signed and float conversions in the enum order are widening.
|
||||||
|
assert(this.isFloating() || this.isSigned());
|
||||||
|
assert(source.isFloating() || source.isSigned());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static { assert(checkConvertibleFrom()); }
|
||||||
|
private static boolean checkConvertibleFrom() {
|
||||||
|
// Check the matrix for correct classification of widening conversions.
|
||||||
|
for (Wrapper w : values()) {
|
||||||
|
assert(w.isConvertibleFrom(w));
|
||||||
|
assert(VOID.isConvertibleFrom(w));
|
||||||
|
if (w != VOID) {
|
||||||
|
assert(OBJECT.isConvertibleFrom(w));
|
||||||
|
assert(!w.isConvertibleFrom(VOID));
|
||||||
|
}
|
||||||
|
// check relations with unsigned integral types:
|
||||||
|
if (w != CHAR) {
|
||||||
|
assert(!CHAR.isConvertibleFrom(w));
|
||||||
|
if (!w.isConvertibleFrom(INT))
|
||||||
|
assert(!w.isConvertibleFrom(CHAR));
|
||||||
|
}
|
||||||
|
if (w != BOOLEAN) {
|
||||||
|
assert(!BOOLEAN.isConvertibleFrom(w));
|
||||||
|
if (w != VOID && w != OBJECT)
|
||||||
|
assert(!w.isConvertibleFrom(BOOLEAN));
|
||||||
|
}
|
||||||
|
// check relations with signed integral types:
|
||||||
|
if (w.isSigned()) {
|
||||||
|
for (Wrapper x : values()) {
|
||||||
|
if (w == x) continue;
|
||||||
|
if (x.isFloating())
|
||||||
|
assert(!w.isConvertibleFrom(x));
|
||||||
|
else if (x.isSigned()) {
|
||||||
|
if (w.compareTo(x) < 0)
|
||||||
|
assert(!w.isConvertibleFrom(x));
|
||||||
|
else
|
||||||
|
assert(w.isConvertibleFrom(x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check relations with floating types:
|
||||||
|
if (w.isFloating()) {
|
||||||
|
for (Wrapper x : values()) {
|
||||||
|
if (w == x) continue;
|
||||||
|
if (x.isSigned())
|
||||||
|
assert(w.isConvertibleFrom(x));
|
||||||
|
else if (x.isFloating()) {
|
||||||
|
if (w.compareTo(x) < 0)
|
||||||
|
assert(!w.isConvertibleFrom(x));
|
||||||
|
else
|
||||||
|
assert(w.isConvertibleFrom(x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true; // i.e., assert(true)
|
||||||
|
}
|
||||||
|
|
||||||
/** Produce a zero value for the given wrapper type.
|
/** Produce a zero value for the given wrapper type.
|
||||||
* This will be a numeric zero for a number or character,
|
* This will be a numeric zero for a number or character,
|
||||||
* false for a boolean, and null for a reference or void.
|
* false for a boolean, and null for a reference or void.
|
||||||
@ -549,7 +623,7 @@ public enum Wrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean boolValue(long bits) {
|
private static boolean boolValue(long bits) {
|
||||||
//bits &= 1; // simple 31-bit zero extension
|
bits &= 1; // simple 31-bit zero extension
|
||||||
return (bits != 0);
|
return (bits != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,4 +633,31 @@ public enum Wrapper {
|
|||||||
private static RuntimeException newIllegalArgumentException(String message) {
|
private static RuntimeException newIllegalArgumentException(String message) {
|
||||||
return new IllegalArgumentException(message);
|
return new IllegalArgumentException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// primitive array support
|
||||||
|
public Object makeArray(int len) {
|
||||||
|
return java.lang.reflect.Array.newInstance(primitiveType, len);
|
||||||
|
}
|
||||||
|
public Class<?> arrayType() {
|
||||||
|
return emptyArray.getClass();
|
||||||
|
}
|
||||||
|
public void copyArrayUnboxing(Object[] values, int vpos, Object a, int apos, int length) {
|
||||||
|
if (a.getClass() != arrayType())
|
||||||
|
arrayType().cast(a); // throw NPE or CCE if bad type
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
Object value = values[i+vpos];
|
||||||
|
value = convert(value, primitiveType);
|
||||||
|
java.lang.reflect.Array.set(a, i+apos, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void copyArrayBoxing(Object a, int apos, Object[] values, int vpos, int length) {
|
||||||
|
if (a.getClass() != arrayType())
|
||||||
|
arrayType().cast(a); // throw NPE or CCE if bad type
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
Object value = java.lang.reflect.Array.get(a, i+apos);
|
||||||
|
//Already done: value = convert(value, primitiveType);
|
||||||
|
assert(value.getClass() == wrapperType);
|
||||||
|
values[i+vpos] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
513
jdk/test/java/lang/invoke/6998541/Test6998541.java
Normal file
513
jdk/test/java/lang/invoke/6998541/Test6998541.java
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 6998541
|
||||||
|
* @summary JSR 292 implement missing return-type conversion for OP_RETYPE_RAW
|
||||||
|
*
|
||||||
|
* @run main/othervm -Xbatch
|
||||||
|
* -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2
|
||||||
|
* -DTest6998541.N=100000 -DTest6998541.KIND=cast Test6998541
|
||||||
|
* @run main/othervm -Xbatch
|
||||||
|
* -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2
|
||||||
|
* -DTest6998541.N=100000 -DTest6998541.KIND=normal Test6998541
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import java.lang.invoke.*;
|
||||||
|
import static java.lang.invoke.MethodHandles.*;
|
||||||
|
|
||||||
|
public class Test6998541 {
|
||||||
|
private static final Class CLASS = Test6998541.class;
|
||||||
|
private static final String NAME = "identity";
|
||||||
|
private static final int N = Math.max(2, Integer.getInteger(CLASS.getSimpleName()+".N", 10000));
|
||||||
|
private static final String KIND = System.getProperty(CLASS.getSimpleName()+".KIND", "cast");
|
||||||
|
private static final int BITS = 0x00000201;
|
||||||
|
|
||||||
|
private static final boolean DO_CASTS = !KIND.equals("normal");
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
System.out.println("KIND="+KIND+" DO_CASTS="+DO_CASTS+" N="+N);
|
||||||
|
doboolean();
|
||||||
|
dobyte();
|
||||||
|
dochar();
|
||||||
|
doshort();
|
||||||
|
doint();
|
||||||
|
dolong();
|
||||||
|
dofloat();
|
||||||
|
dodouble();
|
||||||
|
dovoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void doboolean() throws Throwable {
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
boolean2prim(false);
|
||||||
|
boolean2prim(true);
|
||||||
|
}
|
||||||
|
boolean2prim_invalid(true);
|
||||||
|
}
|
||||||
|
private static void dobyte() throws Throwable {
|
||||||
|
byte x = Byte.MIN_VALUE;
|
||||||
|
for (int i = 0; i < N; i++, x++)
|
||||||
|
byte2prim(x);
|
||||||
|
byte2prim_invalid(x);
|
||||||
|
}
|
||||||
|
private static void dochar() throws Throwable {
|
||||||
|
char x = Character.MIN_VALUE;
|
||||||
|
for (int i = 0; i < N; i++, x++)
|
||||||
|
char2prim(x);
|
||||||
|
char2prim_invalid(x);
|
||||||
|
}
|
||||||
|
private static void doshort() throws Throwable {
|
||||||
|
short x = Short.MIN_VALUE;
|
||||||
|
for (int i = 0; i < N; i++, x++)
|
||||||
|
short2prim(x);
|
||||||
|
short2prim_invalid(x);
|
||||||
|
}
|
||||||
|
private static void doint() throws Throwable {
|
||||||
|
int x = Integer.MIN_VALUE;
|
||||||
|
int D = Integer.MAX_VALUE / (N / 2) | BITS;
|
||||||
|
for (int i = 0; i < N; i++, x += D) {
|
||||||
|
int2prim(x);
|
||||||
|
}
|
||||||
|
int2prim_invalid(x);
|
||||||
|
}
|
||||||
|
private static void dolong() throws Throwable {
|
||||||
|
long x = Long.MIN_VALUE;
|
||||||
|
long D = Long.MAX_VALUE / ((long) (N / 2)) | BITS;
|
||||||
|
for (int i = 0; i < N; i++, x += D)
|
||||||
|
long2prim(x);
|
||||||
|
long2prim_invalid(x);
|
||||||
|
}
|
||||||
|
private static void dofloat() throws Throwable {
|
||||||
|
float x = Float.MIN_VALUE;
|
||||||
|
float D = Float.MAX_VALUE / ((float) (N / 2));
|
||||||
|
for (int i = 0; i < N; i++, x += D)
|
||||||
|
float2prim(x);
|
||||||
|
float2prim_invalid(x);
|
||||||
|
}
|
||||||
|
private static void dodouble() throws Throwable {
|
||||||
|
double x = Double.MIN_VALUE;
|
||||||
|
double D = Double.MAX_VALUE / ((double) (N / 2));
|
||||||
|
for (int i = 0; i < N; i++, x += D)
|
||||||
|
double2prim(x);
|
||||||
|
double2prim_invalid(x);
|
||||||
|
}
|
||||||
|
private static void dovoid() throws Throwable {
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
void2prim(i);
|
||||||
|
}
|
||||||
|
void2prim_invalid(0);
|
||||||
|
// do the other direction here also:
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
prim2void(i);
|
||||||
|
}
|
||||||
|
prim2void_invalid(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertEquals(Object o, Object o2) {
|
||||||
|
if (!o.equals(o2))
|
||||||
|
throw new AssertionError("expected: " + o + ", found: " + o2);
|
||||||
|
}
|
||||||
|
private static void fail() {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
|
|
||||||
|
private static MethodHandle mh(Class ret, Class... args) {
|
||||||
|
try {
|
||||||
|
MethodType mt = MethodType.methodType(ret, args);
|
||||||
|
Class lookupRet = (args.length == 0 ? void.class : args[0]);
|
||||||
|
MethodHandle mh = lookup.findStatic(CLASS, NAME, mt.changeReturnType(lookupRet));
|
||||||
|
if (DO_CASTS)
|
||||||
|
return MethodHandles.explicitCastArguments(mh, mt);
|
||||||
|
if (canDoAsType(mh.type(), mt))
|
||||||
|
return mh.asType(mt);
|
||||||
|
try {
|
||||||
|
mh.asType(mt);
|
||||||
|
throw new AssertionError("asType should not succeed: "+mh+" => "+mt);
|
||||||
|
} catch (WrongMethodTypeException ex) {
|
||||||
|
// this was a required WMTE
|
||||||
|
return mh.asType(mt.generic()).asType(mt);
|
||||||
|
}
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static final Class<?>[] NUMERIC_TYPE_WIDENING_ORDER = {
|
||||||
|
byte.class, short.class, int.class, long.class, float.class, double.class
|
||||||
|
};
|
||||||
|
private static boolean canDoAsType(Class<?> src, Class<?> dst) {
|
||||||
|
if (src == dst) return true;
|
||||||
|
if (dst == void.class) return true;
|
||||||
|
if (!src.isPrimitive() || !dst.isPrimitive()) return true;
|
||||||
|
// primitive conversion works for asType only when it's widening
|
||||||
|
if (src == boolean.class || dst == boolean.class) return false;
|
||||||
|
if (dst == char.class) return false;
|
||||||
|
if (src == char.class) src = int.class; // can widen char to int
|
||||||
|
for (Class<?> ntype : NUMERIC_TYPE_WIDENING_ORDER) {
|
||||||
|
if (src == ntype) return true;
|
||||||
|
if (dst == ntype) return false;
|
||||||
|
}
|
||||||
|
throw new AssertionError("should not reach here: "+src+", "+dst);
|
||||||
|
}
|
||||||
|
private static boolean canDoAsType(MethodType mt0, MethodType mt1) {
|
||||||
|
Class<?> rt0 = mt0.returnType();
|
||||||
|
Class<?> rt1 = mt1.returnType();
|
||||||
|
if (!canDoAsType(rt0, rt1)) return false;
|
||||||
|
int argc = mt0.parameterCount();
|
||||||
|
if (argc != mt1.parameterCount()) return false;
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
if (!canDoAsType(mt1.parameterType(i), mt0.parameterType(i)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle mh_z(Class ret) { return mh(ret, boolean.class); }
|
||||||
|
|
||||||
|
private final static MethodHandle mh_zz = mh_z(boolean.class);
|
||||||
|
private final static MethodHandle mh_bz = mh_z(byte.class );
|
||||||
|
private final static MethodHandle mh_cz = mh_z(char.class );
|
||||||
|
private final static MethodHandle mh_sz = mh_z(short.class );
|
||||||
|
private final static MethodHandle mh_iz = mh_z(int.class );
|
||||||
|
private final static MethodHandle mh_jz = mh_z(long.class );
|
||||||
|
private final static MethodHandle mh_fz = mh_z(float.class );
|
||||||
|
private final static MethodHandle mh_dz = mh_z(double.class );
|
||||||
|
|
||||||
|
private static void boolean2prim(boolean x) throws Throwable {
|
||||||
|
int i = x ? 1 : 0;
|
||||||
|
assertEquals( x, (boolean) mh_zz.invokeExact(x)); // boolean -> boolean
|
||||||
|
if (!DO_CASTS) return;
|
||||||
|
assertEquals((byte) i, (byte) mh_bz.invokeExact(x)); // boolean -> byte
|
||||||
|
assertEquals((char) i, (char) mh_cz.invokeExact(x)); // boolean -> char
|
||||||
|
assertEquals((short) i, (short) mh_sz.invokeExact(x)); // boolean -> short
|
||||||
|
assertEquals((int) i, (int) mh_iz.invokeExact(x)); // boolean -> int
|
||||||
|
assertEquals((long) i, (long) mh_jz.invokeExact(x)); // boolean -> long
|
||||||
|
assertEquals((float) i, (float) mh_fz.invokeExact(x)); // boolean -> float
|
||||||
|
assertEquals((double) i, (double) mh_dz.invokeExact(x)); // boolean -> double
|
||||||
|
}
|
||||||
|
private static void boolean2prim_invalid(boolean x) throws Throwable {
|
||||||
|
if (DO_CASTS) return;
|
||||||
|
try { byte y = (byte) mh_bz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> byte
|
||||||
|
try { char y = (char) mh_cz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> char
|
||||||
|
try { short y = (short) mh_sz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> short
|
||||||
|
try { int y = (int) mh_iz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> int
|
||||||
|
try { long y = (long) mh_jz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> long
|
||||||
|
try { float y = (float) mh_fz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> float
|
||||||
|
try { double y = (double) mh_dz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> double
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle mh_b(Class ret) { return mh(ret, byte.class); }
|
||||||
|
|
||||||
|
private final static MethodHandle mh_zb = mh_b(boolean.class);
|
||||||
|
private final static MethodHandle mh_bb = mh_b(byte.class );
|
||||||
|
private final static MethodHandle mh_cb = mh_b(char.class );
|
||||||
|
private final static MethodHandle mh_sb = mh_b(short.class );
|
||||||
|
private final static MethodHandle mh_ib = mh_b(int.class );
|
||||||
|
private final static MethodHandle mh_jb = mh_b(long.class );
|
||||||
|
private final static MethodHandle mh_fb = mh_b(float.class );
|
||||||
|
private final static MethodHandle mh_db = mh_b(double.class );
|
||||||
|
|
||||||
|
private static void byte2prim(byte x) throws Throwable {
|
||||||
|
assertEquals((byte) x, (byte) mh_bb.invokeExact(x)); // byte -> byte
|
||||||
|
assertEquals((short) x, (short) mh_sb.invokeExact(x)); // byte -> short
|
||||||
|
assertEquals((int) x, (int) mh_ib.invokeExact(x)); // byte -> int
|
||||||
|
assertEquals((long) x, (long) mh_jb.invokeExact(x)); // byte -> long
|
||||||
|
assertEquals((float) x, (float) mh_fb.invokeExact(x)); // byte -> float
|
||||||
|
assertEquals((double) x, (double) mh_db.invokeExact(x)); // byte -> double
|
||||||
|
if (!DO_CASTS) return;
|
||||||
|
boolean z = ((x & 1) != 0);
|
||||||
|
assertEquals((char) x, (char) mh_cb.invokeExact(x)); // byte -> char
|
||||||
|
assertEquals((boolean) z, (boolean) mh_zb.invokeExact(x)); // byte -> boolean
|
||||||
|
}
|
||||||
|
private static void byte2prim_invalid(byte x) throws Throwable {
|
||||||
|
if (DO_CASTS) return;
|
||||||
|
try { char y = (char) mh_cb.invokeExact(x); fail(); } catch (ClassCastException _) {} // byte -> char
|
||||||
|
try { boolean y = (boolean) mh_zb.invokeExact(x); fail(); } catch (ClassCastException _) {} // byte -> boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle mh_c(Class ret) { return mh(ret, char.class); }
|
||||||
|
|
||||||
|
private final static MethodHandle mh_zc = mh_c(boolean.class);
|
||||||
|
private final static MethodHandle mh_bc = mh_c(byte.class );
|
||||||
|
private final static MethodHandle mh_cc = mh_c(char.class );
|
||||||
|
private final static MethodHandle mh_sc = mh_c(short.class );
|
||||||
|
private final static MethodHandle mh_ic = mh_c(int.class );
|
||||||
|
private final static MethodHandle mh_jc = mh_c(long.class );
|
||||||
|
private final static MethodHandle mh_fc = mh_c(float.class );
|
||||||
|
private final static MethodHandle mh_dc = mh_c(double.class );
|
||||||
|
|
||||||
|
private static void char2prim(char x) throws Throwable {
|
||||||
|
assertEquals((char) x, (char) mh_cc.invokeExact(x)); // char -> char
|
||||||
|
assertEquals((int) x, (int) mh_ic.invokeExact(x)); // char -> int
|
||||||
|
assertEquals((long) x, (long) mh_jc.invokeExact(x)); // char -> long
|
||||||
|
assertEquals((float) x, (float) mh_fc.invokeExact(x)); // char -> float
|
||||||
|
assertEquals((double) x, (double) mh_dc.invokeExact(x)); // char -> double
|
||||||
|
if (!DO_CASTS) return;
|
||||||
|
boolean z = ((x & 1) != 0);
|
||||||
|
assertEquals((boolean) z, (boolean) mh_zc.invokeExact(x)); // char -> boolean
|
||||||
|
assertEquals((byte) x, (byte) mh_bc.invokeExact(x)); // char -> byte
|
||||||
|
assertEquals((short) x, (short) mh_sc.invokeExact(x)); // char -> short
|
||||||
|
}
|
||||||
|
private static void char2prim_invalid(char x) throws Throwable {
|
||||||
|
if (DO_CASTS) return;
|
||||||
|
try { boolean y = (boolean) mh_zc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> boolean
|
||||||
|
try { byte y = (byte) mh_bc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> byte
|
||||||
|
try { short y = (short) mh_sc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> short
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle mh_s(Class ret) { return mh(ret, short.class); }
|
||||||
|
|
||||||
|
private final static MethodHandle mh_zs = mh_s(boolean.class);
|
||||||
|
private final static MethodHandle mh_bs = mh_s(byte.class );
|
||||||
|
private final static MethodHandle mh_cs = mh_s(char.class );
|
||||||
|
private final static MethodHandle mh_ss = mh_s(short.class );
|
||||||
|
private final static MethodHandle mh_is = mh_s(int.class );
|
||||||
|
private final static MethodHandle mh_js = mh_s(long.class );
|
||||||
|
private final static MethodHandle mh_fs = mh_s(float.class );
|
||||||
|
private final static MethodHandle mh_ds = mh_s(double.class );
|
||||||
|
|
||||||
|
private static void short2prim(short x) throws Throwable {
|
||||||
|
assertEquals((short) x, (short) mh_ss.invokeExact(x)); // short -> short
|
||||||
|
assertEquals((int) x, (int) mh_is.invokeExact(x)); // short -> int
|
||||||
|
assertEquals((long) x, (long) mh_js.invokeExact(x)); // short -> long
|
||||||
|
assertEquals((float) x, (float) mh_fs.invokeExact(x)); // short -> float
|
||||||
|
assertEquals((double) x, (double) mh_ds.invokeExact(x)); // short -> double
|
||||||
|
if (!DO_CASTS) return;
|
||||||
|
boolean z = ((x & 1) != 0);
|
||||||
|
assertEquals((boolean) z, (boolean) mh_zs.invokeExact(x)); // short -> boolean
|
||||||
|
assertEquals((byte) x, (byte) mh_bs.invokeExact(x)); // short -> byte
|
||||||
|
assertEquals((char) x, (char) mh_cs.invokeExact(x)); // short -> char
|
||||||
|
}
|
||||||
|
private static void short2prim_invalid(short x) throws Throwable {
|
||||||
|
if (DO_CASTS) return;
|
||||||
|
try { boolean y = (boolean) mh_zs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> boolean
|
||||||
|
try { byte y = (byte) mh_bs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> byte
|
||||||
|
try { char y = (char) mh_cs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> char
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle mh_i(Class ret) { return mh(ret, int.class); }
|
||||||
|
|
||||||
|
private final static MethodHandle mh_zi = mh_i(boolean.class);
|
||||||
|
private final static MethodHandle mh_bi = mh_i(byte.class );
|
||||||
|
private final static MethodHandle mh_ci = mh_i(char.class );
|
||||||
|
private final static MethodHandle mh_si = mh_i(short.class );
|
||||||
|
private final static MethodHandle mh_ii = mh_i(int.class );
|
||||||
|
private final static MethodHandle mh_ji = mh_i(long.class );
|
||||||
|
private final static MethodHandle mh_fi = mh_i(float.class );
|
||||||
|
private final static MethodHandle mh_di = mh_i(double.class );
|
||||||
|
|
||||||
|
private static void int2prim(int x) throws Throwable {
|
||||||
|
assertEquals((int) x, (int) mh_ii.invokeExact(x)); // int -> int
|
||||||
|
assertEquals((long) x, (long) mh_ji.invokeExact(x)); // int -> long
|
||||||
|
assertEquals((float) x, (float) mh_fi.invokeExact(x)); // int -> float
|
||||||
|
assertEquals((double) x, (double) mh_di.invokeExact(x)); // int -> double
|
||||||
|
if (!DO_CASTS) return;
|
||||||
|
boolean z = ((x & 1) != 0);
|
||||||
|
assertEquals((boolean) z, (boolean) mh_zi.invokeExact(x)); // int -> boolean
|
||||||
|
assertEquals((byte) x, (byte) mh_bi.invokeExact(x)); // int -> byte
|
||||||
|
assertEquals((char) x, (char) mh_ci.invokeExact(x)); // int -> char
|
||||||
|
assertEquals((short) x, (short) mh_si.invokeExact(x)); // int -> short
|
||||||
|
}
|
||||||
|
private static void int2prim_invalid(int x) throws Throwable {
|
||||||
|
if (DO_CASTS) return;
|
||||||
|
try { boolean y = (boolean) mh_zi.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> boolean
|
||||||
|
try { byte y = (byte) mh_bi.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> byte
|
||||||
|
try { char y = (char) mh_ci.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> char
|
||||||
|
try { short y = (short) mh_si.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> short
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle mh_j(Class ret) { return mh(ret, long.class); }
|
||||||
|
|
||||||
|
private final static MethodHandle mh_zj = mh_j(boolean.class);
|
||||||
|
private final static MethodHandle mh_bj = mh_j(byte.class );
|
||||||
|
private final static MethodHandle mh_cj = mh_j(char.class );
|
||||||
|
private final static MethodHandle mh_sj = mh_j(short.class );
|
||||||
|
private final static MethodHandle mh_ij = mh_j(int.class );
|
||||||
|
private final static MethodHandle mh_jj = mh_j(long.class );
|
||||||
|
private final static MethodHandle mh_fj = mh_j(float.class );
|
||||||
|
private final static MethodHandle mh_dj = mh_j(double.class );
|
||||||
|
|
||||||
|
private static void long2prim(long x) throws Throwable {
|
||||||
|
assertEquals((long) x, (long) mh_jj.invokeExact(x)); // long -> long
|
||||||
|
assertEquals((float) x, (float) mh_fj.invokeExact(x)); // long -> float
|
||||||
|
assertEquals((double) x, (double) mh_dj.invokeExact(x)); // long -> double
|
||||||
|
if (!DO_CASTS) return;
|
||||||
|
boolean z = ((x & 1) != 0);
|
||||||
|
assertEquals((boolean)z, (boolean) mh_zj.invokeExact(x)); // long -> boolean
|
||||||
|
assertEquals((byte) x, (byte) mh_bj.invokeExact(x)); // long -> byte
|
||||||
|
assertEquals((char) x, (char) mh_cj.invokeExact(x)); // long -> char
|
||||||
|
assertEquals((short) x, (short) mh_sj.invokeExact(x)); // long -> short
|
||||||
|
assertEquals((int) x, (int) mh_ij.invokeExact(x)); // long -> int
|
||||||
|
}
|
||||||
|
private static void long2prim_invalid(long x) throws Throwable {
|
||||||
|
if (DO_CASTS) return;
|
||||||
|
try { boolean y = (boolean) mh_zj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> boolean
|
||||||
|
try { byte y = (byte) mh_bj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> byte
|
||||||
|
try { char y = (char) mh_cj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> char
|
||||||
|
try { short y = (short) mh_sj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> short
|
||||||
|
try { int y = (int) mh_ij.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle mh_f(Class ret) { return mh(ret, float.class); }
|
||||||
|
|
||||||
|
private final static MethodHandle mh_zf = mh_f(boolean.class);
|
||||||
|
private final static MethodHandle mh_bf = mh_f(byte.class );
|
||||||
|
private final static MethodHandle mh_cf = mh_f(char.class );
|
||||||
|
private final static MethodHandle mh_sf = mh_f(short.class );
|
||||||
|
private final static MethodHandle mh_if = mh_f(int.class );
|
||||||
|
private final static MethodHandle mh_jf = mh_f(long.class );
|
||||||
|
private final static MethodHandle mh_ff = mh_f(float.class );
|
||||||
|
private final static MethodHandle mh_df = mh_f(double.class );
|
||||||
|
|
||||||
|
private static void float2prim(float x) throws Throwable {
|
||||||
|
assertEquals((float) x, (float) mh_ff.invokeExact(x)); // float -> float
|
||||||
|
assertEquals((double) x, (double) mh_df.invokeExact(x)); // float -> double
|
||||||
|
if (!DO_CASTS) return;
|
||||||
|
boolean z = (((byte) x & 1) != 0);
|
||||||
|
assertEquals((boolean) z, (boolean) mh_zf.invokeExact(x)); // float -> boolean
|
||||||
|
assertEquals((byte) x, (byte) mh_bf.invokeExact(x)); // float -> byte
|
||||||
|
assertEquals((char) x, (char) mh_cf.invokeExact(x)); // float -> char
|
||||||
|
assertEquals((short) x, (short) mh_sf.invokeExact(x)); // float -> short
|
||||||
|
assertEquals((int) x, (int) mh_if.invokeExact(x)); // float -> int
|
||||||
|
assertEquals((long) x, (long) mh_jf.invokeExact(x)); // float -> long
|
||||||
|
}
|
||||||
|
private static void float2prim_invalid(float x) throws Throwable {
|
||||||
|
if (DO_CASTS) return;
|
||||||
|
try { boolean y = (boolean) mh_zf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> boolean
|
||||||
|
try { byte y = (byte) mh_bf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> byte
|
||||||
|
try { char y = (char) mh_cf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> char
|
||||||
|
try { short y = (short) mh_sf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> short
|
||||||
|
try { int y = (int) mh_if.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> int
|
||||||
|
try { long y = (long) mh_jf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> long
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle mh_d(Class ret) { return mh(ret, double.class); }
|
||||||
|
|
||||||
|
private final static MethodHandle mh_zd = mh_d(boolean.class);
|
||||||
|
private final static MethodHandle mh_bd = mh_d(byte.class );
|
||||||
|
private final static MethodHandle mh_cd = mh_d(char.class );
|
||||||
|
private final static MethodHandle mh_sd = mh_d(short.class );
|
||||||
|
private final static MethodHandle mh_id = mh_d(int.class );
|
||||||
|
private final static MethodHandle mh_jd = mh_d(long.class );
|
||||||
|
private final static MethodHandle mh_fd = mh_d(float.class );
|
||||||
|
private final static MethodHandle mh_dd = mh_d(double.class );
|
||||||
|
|
||||||
|
private static void double2prim(double x) throws Throwable {
|
||||||
|
assertEquals((double) x, (double) mh_dd.invokeExact(x)); // double -> double
|
||||||
|
if (!DO_CASTS) return;
|
||||||
|
boolean z = (((byte) x & 1) != 0);
|
||||||
|
assertEquals((boolean) z, (boolean) mh_zd.invokeExact(x)); // double -> boolean
|
||||||
|
assertEquals((byte) x, (byte) mh_bd.invokeExact(x)); // double -> byte
|
||||||
|
assertEquals((char) x, (char) mh_cd.invokeExact(x)); // double -> char
|
||||||
|
assertEquals((short) x, (short) mh_sd.invokeExact(x)); // double -> short
|
||||||
|
assertEquals((int) x, (int) mh_id.invokeExact(x)); // double -> int
|
||||||
|
assertEquals((long) x, (long) mh_jd.invokeExact(x)); // double -> long
|
||||||
|
assertEquals((float) x, (float) mh_fd.invokeExact(x)); // double -> float
|
||||||
|
}
|
||||||
|
private static void double2prim_invalid(double x) throws Throwable {
|
||||||
|
if (DO_CASTS) return;
|
||||||
|
try { boolean y = (boolean) mh_zd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> boolean
|
||||||
|
try { byte y = (byte) mh_bd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> byte
|
||||||
|
try { char y = (char) mh_cd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> char
|
||||||
|
try { short y = (short) mh_sd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> short
|
||||||
|
try { int y = (int) mh_id.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> int
|
||||||
|
try { long y = (long) mh_jd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> long
|
||||||
|
try { float y = (float) mh_fd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> float
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static MethodHandle mh_zv = mh(boolean.class);
|
||||||
|
private final static MethodHandle mh_bv = mh(byte.class );
|
||||||
|
private final static MethodHandle mh_cv = mh(char.class );
|
||||||
|
private final static MethodHandle mh_sv = mh(short.class );
|
||||||
|
private final static MethodHandle mh_iv = mh(int.class );
|
||||||
|
private final static MethodHandle mh_jv = mh(long.class );
|
||||||
|
private final static MethodHandle mh_fv = mh(float.class );
|
||||||
|
private final static MethodHandle mh_dv = mh(double.class );
|
||||||
|
|
||||||
|
private static void void2prim(int i) throws Throwable {
|
||||||
|
if (!DO_CASTS) return;
|
||||||
|
assertEquals( false, (boolean) mh_zv.invokeExact()); // void -> boolean
|
||||||
|
assertEquals((byte) 0, (byte) mh_bv.invokeExact()); // void -> byte
|
||||||
|
assertEquals((char) 0, (char) mh_cv.invokeExact()); // void -> char
|
||||||
|
assertEquals((short) 0, (short) mh_sv.invokeExact()); // void -> short
|
||||||
|
assertEquals( 0, (int) mh_iv.invokeExact()); // void -> int
|
||||||
|
assertEquals( 0L, (long) mh_jv.invokeExact()); // void -> long
|
||||||
|
assertEquals( 0.0f, (float) mh_fv.invokeExact()); // void -> float
|
||||||
|
assertEquals( 0.0d, (double) mh_dv.invokeExact()); // void -> double
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void void2prim_invalid(double x) throws Throwable {
|
||||||
|
if (DO_CASTS) return;
|
||||||
|
try { assertEquals( false, (boolean) mh_zv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> boolean
|
||||||
|
try { assertEquals((byte) 0, (byte) mh_bv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> byte
|
||||||
|
try { assertEquals((char) 0, (char) mh_cv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> char
|
||||||
|
try { assertEquals((short) 0, (short) mh_sv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> short
|
||||||
|
try { assertEquals( 0, (int) mh_iv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> int
|
||||||
|
try { assertEquals( 0L, (long) mh_jv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> long
|
||||||
|
try { assertEquals( 0.0f, (float) mh_fv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> float
|
||||||
|
try { assertEquals( 0.0d, (double) mh_dv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> double
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle mh_v(Class arg) { return mh(void.class, arg); }
|
||||||
|
|
||||||
|
private final static MethodHandle mh_vz = mh_v(boolean.class);
|
||||||
|
private final static MethodHandle mh_vb = mh_v(byte.class );
|
||||||
|
private final static MethodHandle mh_vc = mh_v(char.class );
|
||||||
|
private final static MethodHandle mh_vs = mh_v(short.class );
|
||||||
|
private final static MethodHandle mh_vi = mh_v(int.class );
|
||||||
|
private final static MethodHandle mh_vj = mh_v(long.class );
|
||||||
|
private final static MethodHandle mh_vf = mh_v(float.class );
|
||||||
|
private final static MethodHandle mh_vd = mh_v(double.class );
|
||||||
|
|
||||||
|
private static void prim2void(int x) throws Throwable {
|
||||||
|
boolean z = ((x & 1) != 0);
|
||||||
|
mh_vz.invokeExact( z); // boolean -> void
|
||||||
|
mh_vb.invokeExact((byte) x); // byte -> void
|
||||||
|
mh_vc.invokeExact((char) x); // char -> void
|
||||||
|
mh_vs.invokeExact((short) x); // short -> void
|
||||||
|
mh_vi.invokeExact((int) x); // int -> void
|
||||||
|
mh_vj.invokeExact((long) x); // long -> void
|
||||||
|
mh_vf.invokeExact((float) x); // float -> void
|
||||||
|
mh_vd.invokeExact((double) x); // double -> void
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void prim2void_invalid(int x) throws Throwable {
|
||||||
|
// no cases
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean identity(boolean v) { return v; }
|
||||||
|
private static byte identity(byte v) { return v; }
|
||||||
|
private static char identity(char v) { return v; }
|
||||||
|
private static short identity(short v) { return v; }
|
||||||
|
private static int identity(int v) { return v; }
|
||||||
|
private static long identity(long v) { return v; }
|
||||||
|
private static float identity(float v) { return v; }
|
||||||
|
private static double identity(double v) { return v; }
|
||||||
|
private static void identity() {}
|
||||||
|
}
|
@ -24,7 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* @test
|
/* @test
|
||||||
* @summary unit tests for java.lang.invoke.MethodHandle.invokeGeneric
|
* @summary unit tests for java.lang.invoke.MethodHandle.invoke
|
||||||
* @compile -target 7 InvokeGenericTest.java
|
* @compile -target 7 InvokeGenericTest.java
|
||||||
* @run junit/othervm test.java.lang.invoke.InvokeGenericTest
|
* @run junit/othervm test.java.lang.invoke.InvokeGenericTest
|
||||||
*/
|
*/
|
||||||
@ -53,6 +53,10 @@ public class InvokeGenericTest {
|
|||||||
if (vstr != null) verbosity = Integer.parseInt(vstr);
|
if (vstr != null) verbosity = Integer.parseInt(vstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String... av) throws Throwable {
|
||||||
|
new InvokeGenericTest().testFirst();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFirst() throws Throwable {
|
public void testFirst() throws Throwable {
|
||||||
verbosity += 9; try {
|
verbosity += 9; try {
|
||||||
@ -103,7 +107,7 @@ public class InvokeGenericTest {
|
|||||||
void startTest(String name) {
|
void startTest(String name) {
|
||||||
if (testName != null) printCounts();
|
if (testName != null) printCounts();
|
||||||
if (verbosity >= 1)
|
if (verbosity >= 1)
|
||||||
System.out.println(name);
|
System.out.println("["+name+"]");
|
||||||
posTests = negTests = 0;
|
posTests = negTests = 0;
|
||||||
testName = name;
|
testName = name;
|
||||||
}
|
}
|
||||||
@ -350,6 +354,30 @@ public class InvokeGenericTest {
|
|||||||
String[] args = { "one", "two" };
|
String[] args = { "one", "two" };
|
||||||
MethodHandle mh = callable(Object.class, String.class);
|
MethodHandle mh = callable(Object.class, String.class);
|
||||||
Object res; List resl;
|
Object res; List resl;
|
||||||
|
res = resl = (List) mh.invoke((String)args[0], (Object)args[1]);
|
||||||
|
//System.out.println(res);
|
||||||
|
assertEquals(Arrays.asList(args), res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimplePrims() throws Throwable {
|
||||||
|
startTest("testSimplePrims");
|
||||||
|
countTest();
|
||||||
|
int[] args = { 1, 2 };
|
||||||
|
MethodHandle mh = callable(Object.class, Object.class);
|
||||||
|
Object res; List resl;
|
||||||
|
res = resl = (List) mh.invoke(args[0], args[1]);
|
||||||
|
//System.out.println(res);
|
||||||
|
assertEquals(Arrays.toString(args), res.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAlternateName() throws Throwable {
|
||||||
|
startTest("testAlternateName");
|
||||||
|
countTest();
|
||||||
|
String[] args = { "one", "two" };
|
||||||
|
MethodHandle mh = callable(Object.class, String.class);
|
||||||
|
Object res; List resl;
|
||||||
res = resl = (List) mh.invokeGeneric((String)args[0], (Object)args[1]);
|
res = resl = (List) mh.invokeGeneric((String)args[0], (Object)args[1]);
|
||||||
//System.out.println(res);
|
//System.out.println(res);
|
||||||
assertEquals(Arrays.asList(args), res);
|
assertEquals(Arrays.asList(args), res);
|
||||||
@ -388,24 +416,24 @@ public class InvokeGenericTest {
|
|||||||
try {
|
try {
|
||||||
switch (args.length) {
|
switch (args.length) {
|
||||||
case 0:
|
case 0:
|
||||||
junk = target.invokeGeneric(); break;
|
junk = target.invoke(); break;
|
||||||
case 1:
|
case 1:
|
||||||
junk = target.invokeGeneric(args[0]); break;
|
junk = target.invoke(args[0]); break;
|
||||||
case 2:
|
case 2:
|
||||||
junk = target.invokeGeneric(args[0], args[1]); break;
|
junk = target.invoke(args[0], args[1]); break;
|
||||||
case 3:
|
case 3:
|
||||||
junk = target.invokeGeneric(args[0], args[1], args[2]); break;
|
junk = target.invoke(args[0], args[1], args[2]); break;
|
||||||
case 4:
|
case 4:
|
||||||
junk = target.invokeGeneric(args[0], args[1], args[2], args[3]); break;
|
junk = target.invoke(args[0], args[1], args[2], args[3]); break;
|
||||||
default:
|
default:
|
||||||
junk = target.invokeWithArguments(args); break;
|
junk = target.invokeWithArguments(args); break;
|
||||||
}
|
}
|
||||||
} catch (WrongMethodTypeException ex) {
|
} catch (WrongMethodTypeException ex) {
|
||||||
return;
|
return;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new RuntimeException("wrong exception calling "+target+target.type()+" on "+Arrays.asList(args)+" : "+ex);
|
throw new RuntimeException("wrong exception calling "+target+" on "+Arrays.asList(args), ex);
|
||||||
}
|
}
|
||||||
throw new RuntimeException("bad success calling "+target+target.type()+" on "+Arrays.asList(args));
|
throw new RuntimeException("bad success calling "+target+" on "+Arrays.asList(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Make a list of all combinations of the given types, with the given arities.
|
/** Make a list of all combinations of the given types, with the given arities.
|
||||||
@ -451,7 +479,7 @@ public class InvokeGenericTest {
|
|||||||
startTest("testReferenceConversions");
|
startTest("testReferenceConversions");
|
||||||
toString_MH = LOOKUP.
|
toString_MH = LOOKUP.
|
||||||
findVirtual(Object.class, "toString", MethodType.methodType(String.class));
|
findVirtual(Object.class, "toString", MethodType.methodType(String.class));
|
||||||
String[] args = { "one", "two" };
|
Object[] args = { "one", "two" };
|
||||||
for (MethodType type : allMethodTypes(2, Object.class, String.class, RandomInterface.class)) {
|
for (MethodType type : allMethodTypes(2, Object.class, String.class, RandomInterface.class)) {
|
||||||
testReferenceConversions(type, args);
|
testReferenceConversions(type, args);
|
||||||
}
|
}
|
||||||
@ -463,7 +491,7 @@ public class InvokeGenericTest {
|
|||||||
MethodHandle tsdrop = MethodHandles.dropArguments(toString_MH, 1, type.parameterList());
|
MethodHandle tsdrop = MethodHandles.dropArguments(toString_MH, 1, type.parameterList());
|
||||||
mh = MethodHandles.foldArguments(tsdrop, mh);
|
mh = MethodHandles.foldArguments(tsdrop, mh);
|
||||||
mh = mh.asType(type);
|
mh = mh.asType(type);
|
||||||
Object res = mh.invokeGeneric((String)args[0], (Object)args[1]);
|
Object res = mh.invoke((String)args[0], (Object)args[1]);
|
||||||
//System.out.println(res);
|
//System.out.println(res);
|
||||||
assertEquals(Arrays.asList(args).toString(), res);
|
assertEquals(Arrays.asList(args).toString(), res);
|
||||||
}
|
}
|
||||||
@ -473,10 +501,10 @@ public class InvokeGenericTest {
|
|||||||
public void testBoxConversions() throws Throwable {
|
public void testBoxConversions() throws Throwable {
|
||||||
startTest("testBoxConversions");
|
startTest("testBoxConversions");
|
||||||
countTest();
|
countTest();
|
||||||
Integer[] args = { 1, 2 };
|
Object[] args = { 1, 2 };
|
||||||
MethodHandle mh = callable(Object.class, int.class);
|
MethodHandle mh = callable(Object.class, int.class);
|
||||||
Object res; List resl;
|
Object res; List resl;
|
||||||
res = resl = (List) mh.invokeGeneric((int)args[0], (Object)args[1]);
|
res = resl = (List) mh.invoke((int)args[0], (Object)args[1]);
|
||||||
//System.out.println(res);
|
//System.out.println(res);
|
||||||
assertEquals(Arrays.asList(args), res);
|
assertEquals(Arrays.asList(args), res);
|
||||||
}
|
}
|
||||||
|
@ -170,8 +170,8 @@ assert(s.equals("savvy"));
|
|||||||
mt = MethodType.methodType(java.util.List.class, Object[].class);
|
mt = MethodType.methodType(java.util.List.class, Object[].class);
|
||||||
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
|
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
|
||||||
assert(mh.isVarargsCollector());
|
assert(mh.isVarargsCollector());
|
||||||
x = mh.invokeGeneric("one", "two");
|
x = mh.invoke("one", "two");
|
||||||
// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
|
// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
|
||||||
assert(x.equals(java.util.Arrays.asList("one","two")));
|
assert(x.equals(java.util.Arrays.asList("one","two")));
|
||||||
// mt is (Object,Object,Object)Object
|
// mt is (Object,Object,Object)Object
|
||||||
mt = MethodType.genericMethodType(3);
|
mt = MethodType.genericMethodType(3);
|
||||||
@ -199,12 +199,12 @@ mh.invokeExact(System.out, "Hello, world.");
|
|||||||
MethodHandle asList = publicLookup()
|
MethodHandle asList = publicLookup()
|
||||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
||||||
.asVarargsCollector(Object[].class);
|
.asVarargsCollector(Object[].class);
|
||||||
assertEquals("[]", asList.invokeGeneric().toString());
|
assertEquals("[]", asList.invoke().toString());
|
||||||
assertEquals("[1]", asList.invokeGeneric(1).toString());
|
assertEquals("[1]", asList.invoke(1).toString());
|
||||||
assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
|
assertEquals("[two, too]", asList.invoke("two", "too").toString());
|
||||||
Object[] argv = { "three", "thee", "tee" };
|
Object[] argv = { "three", "thee", "tee" };
|
||||||
assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
|
assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
|
||||||
List ls = (List) asList.invokeGeneric((Object)argv);
|
List ls = (List) asList.invoke((Object)argv);
|
||||||
assertEquals(1, ls.size());
|
assertEquals(1, ls.size());
|
||||||
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||||
}}
|
}}
|
||||||
@ -218,9 +218,9 @@ MethodHandle vamh = publicLookup()
|
|||||||
.asVarargsCollector(Object[].class);
|
.asVarargsCollector(Object[].class);
|
||||||
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
|
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
|
||||||
assert(vamh.type().equals(mh.type()));
|
assert(vamh.type().equals(mh.type()));
|
||||||
assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
|
assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString());
|
||||||
boolean failed = false;
|
boolean failed = false;
|
||||||
try { mh.invokeGeneric(1,2,3); }
|
try { mh.invoke(1,2,3); }
|
||||||
catch (WrongMethodTypeException ex) { failed = true; }
|
catch (WrongMethodTypeException ex) { failed = true; }
|
||||||
assert(failed);
|
assert(failed);
|
||||||
{}
|
{}
|
||||||
|
@ -105,24 +105,6 @@ public class MethodHandlesTest {
|
|||||||
public MethodHandlesTest() {
|
public MethodHandlesTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
|
||||||
public void checkImplementedPlatform() {
|
|
||||||
boolean platformOK = false;
|
|
||||||
Properties properties = System.getProperties();
|
|
||||||
String vers = properties.getProperty("java.vm.version");
|
|
||||||
String name = properties.getProperty("java.vm.name");
|
|
||||||
String arch = properties.getProperty("os.arch");
|
|
||||||
if ((arch.equals("amd64") || arch.equals("i386") || arch.equals("x86") ||
|
|
||||||
arch.equals("sparc") || arch.equals("sparcv9")) &&
|
|
||||||
(name.contains("Client") || name.contains("Server"))
|
|
||||||
) {
|
|
||||||
platformOK = true;
|
|
||||||
} else {
|
|
||||||
System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch));
|
|
||||||
}
|
|
||||||
assumeTrue(platformOK);
|
|
||||||
}
|
|
||||||
|
|
||||||
String testName;
|
String testName;
|
||||||
static int allPosTests, allNegTests;
|
static int allPosTests, allNegTests;
|
||||||
int posTests, negTests;
|
int posTests, negTests;
|
||||||
@ -331,6 +313,9 @@ public class MethodHandlesTest {
|
|||||||
static MethodHandle varargsArray(int arity) {
|
static MethodHandle varargsArray(int arity) {
|
||||||
return ValueConversions.varargsArray(arity);
|
return ValueConversions.varargsArray(arity);
|
||||||
}
|
}
|
||||||
|
static MethodHandle varargsArray(Class<?> arrayType, int arity) {
|
||||||
|
return ValueConversions.varargsArray(arrayType, arity);
|
||||||
|
}
|
||||||
/** Variation of varargsList, but with the given rtype. */
|
/** Variation of varargsList, but with the given rtype. */
|
||||||
static MethodHandle varargsList(int arity, Class<?> rtype) {
|
static MethodHandle varargsList(int arity, Class<?> rtype) {
|
||||||
MethodHandle list = varargsList(arity);
|
MethodHandle list = varargsList(arity);
|
||||||
@ -865,7 +850,7 @@ public class MethodHandlesTest {
|
|||||||
Class<?> type = (Class<?>) t[1];
|
Class<?> type = (Class<?>) t[1];
|
||||||
Object value;
|
Object value;
|
||||||
Field field;
|
Field field;
|
||||||
try {
|
try {
|
||||||
field = HasFields.class.getDeclaredField(name);
|
field = HasFields.class.getDeclaredField(name);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new InternalError("no field HasFields."+name);
|
throw new InternalError("no field HasFields."+name);
|
||||||
@ -1144,16 +1129,9 @@ public class MethodHandlesTest {
|
|||||||
: MethodHandles.arrayElementSetter(arrayType);
|
: MethodHandles.arrayElementSetter(arrayType);
|
||||||
assertSame(mh.type(), expType);
|
assertSame(mh.type(), expType);
|
||||||
if (elemType != int.class && elemType != boolean.class) {
|
if (elemType != int.class && elemType != boolean.class) {
|
||||||
MethodType gtype;
|
// FIXME: change Integer.class and (Integer) below to int.class and (int) below.
|
||||||
if (true) { // FIXME: remove this path (and remove <void> below in the mh.invokes)
|
MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class);
|
||||||
gtype = mh.type().changeParameterType(0, Object.class);
|
if (testSetter) gtype = gtype.changeReturnType(void.class);
|
||||||
if (testSetter)
|
|
||||||
gtype = gtype.changeParameterType(2, Object.class);
|
|
||||||
else
|
|
||||||
gtype = gtype.changeReturnType(Object.class);
|
|
||||||
} else
|
|
||||||
// FIXME: This simpler path hits a bug in convertArguments => ToGeneric
|
|
||||||
gtype = mh.type().generic().changeParameterType(1, int.class);
|
|
||||||
mh = MethodHandles.convertArguments(mh, gtype);
|
mh = MethodHandles.convertArguments(mh, gtype);
|
||||||
}
|
}
|
||||||
Object sawValue, expValue;
|
Object sawValue, expValue;
|
||||||
@ -1169,7 +1147,7 @@ public class MethodHandlesTest {
|
|||||||
else if (elemType == boolean.class)
|
else if (elemType == boolean.class)
|
||||||
mh.invokeExact((boolean[]) array, i, (boolean)random);
|
mh.invokeExact((boolean[]) array, i, (boolean)random);
|
||||||
else
|
else
|
||||||
mh.invokeExact(array, i, random);
|
mh.invokeExact(array, (Integer)i, random);
|
||||||
assertEquals(model, array2list(array));
|
assertEquals(model, array2list(array));
|
||||||
} else {
|
} else {
|
||||||
Array.set(array, i, random);
|
Array.set(array, i, random);
|
||||||
@ -1189,7 +1167,7 @@ public class MethodHandlesTest {
|
|||||||
else if (elemType == boolean.class)
|
else if (elemType == boolean.class)
|
||||||
sawValue = (boolean) mh.invokeExact((boolean[]) array, i);
|
sawValue = (boolean) mh.invokeExact((boolean[]) array, i);
|
||||||
else
|
else
|
||||||
sawValue = mh.invokeExact(array, i);
|
sawValue = mh.invokeExact(array, (Integer)i);
|
||||||
assertEquals(sawValue, expValue);
|
assertEquals(sawValue, expValue);
|
||||||
assertEquals(model, array2list(array));
|
assertEquals(model, array2list(array));
|
||||||
}
|
}
|
||||||
@ -1341,21 +1319,15 @@ public class MethodHandlesTest {
|
|||||||
int numcases = 1;
|
int numcases = 1;
|
||||||
for (int outargs = 0; outargs <= max; outargs++) {
|
for (int outargs = 0; outargs <= max; outargs++) {
|
||||||
if (outargs - inargs >= MAX_ARG_INCREASE) continue;
|
if (outargs - inargs >= MAX_ARG_INCREASE) continue;
|
||||||
int[] reorder = new int[outargs];
|
|
||||||
int casStep = dilution + 1;
|
int casStep = dilution + 1;
|
||||||
// Avoid some common factors:
|
// Avoid some common factors:
|
||||||
while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) ||
|
while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) ||
|
||||||
(casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0))
|
(casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0))
|
||||||
casStep++;
|
casStep++;
|
||||||
for (int cas = 0; cas < numcases; cas += casStep) {
|
testPermuteArguments(args, types, outargs, numcases, casStep);
|
||||||
for (int i = 0, c = cas; i < outargs; i++) {
|
|
||||||
reorder[i] = c % inargs;
|
|
||||||
c /= inargs;
|
|
||||||
}
|
|
||||||
testPermuteArguments(args, types, reorder);
|
|
||||||
}
|
|
||||||
numcases *= inargs;
|
numcases *= inargs;
|
||||||
if (dilution > 10 && outargs >= 4) {
|
if (dilution > 10 && outargs >= 4) {
|
||||||
|
int[] reorder = new int[outargs];
|
||||||
// Do some special patterns, which we probably missed.
|
// Do some special patterns, which we probably missed.
|
||||||
// Replication of a single argument or argument pair.
|
// Replication of a single argument or argument pair.
|
||||||
for (int i = 0; i < inargs; i++) {
|
for (int i = 0; i < inargs; i++) {
|
||||||
@ -1383,6 +1355,19 @@ public class MethodHandlesTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPermuteArguments(Object[] args, Class<?>[] types,
|
||||||
|
int outargs, int numcases, int casStep) throws Throwable {
|
||||||
|
int inargs = args.length;
|
||||||
|
int[] reorder = new int[outargs];
|
||||||
|
for (int cas = 0; cas < numcases; cas += casStep) {
|
||||||
|
for (int i = 0, c = cas; i < outargs; i++) {
|
||||||
|
reorder[i] = c % inargs;
|
||||||
|
c /= inargs;
|
||||||
|
}
|
||||||
|
testPermuteArguments(args, types, reorder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int[] reverse(int[] reorder) {
|
static int[] reverse(int[] reorder) {
|
||||||
reorder = reorder.clone();
|
reorder = reorder.clone();
|
||||||
for (int i = 0, imax = reorder.length / 2; i < imax; i++) {
|
for (int i = 0, imax = reorder.length / 2; i < imax; i++) {
|
||||||
@ -1433,6 +1418,12 @@ public class MethodHandlesTest {
|
|||||||
MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
|
MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
|
||||||
Object result = newTarget.invokeWithArguments(args);
|
Object result = newTarget.invokeWithArguments(args);
|
||||||
Object expected = Arrays.asList(permArgs);
|
Object expected = Arrays.asList(permArgs);
|
||||||
|
if (!expected.equals(result)) {
|
||||||
|
System.out.println("*** failed permuteArguments "+Arrays.toString(reorder)+" types="+Arrays.asList(types));
|
||||||
|
System.out.println("in args: "+Arrays.asList(args));
|
||||||
|
System.out.println("out args: "+expected);
|
||||||
|
System.out.println("bad args: "+result);
|
||||||
|
}
|
||||||
assertEquals(expected, result);
|
assertEquals(expected, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1456,26 +1447,27 @@ public class MethodHandlesTest {
|
|||||||
}
|
}
|
||||||
public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
|
public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
|
||||||
countTest();
|
countTest();
|
||||||
MethodHandle target = varargsArray(nargs);
|
Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
|
||||||
MethodHandle target2 = changeArgTypes(target, argType);
|
MethodHandle target2 = varargsArray(arrayType, nargs);
|
||||||
|
MethodHandle target = target2.asType(target2.type().generic());
|
||||||
if (verbosity >= 3)
|
if (verbosity >= 3)
|
||||||
System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
|
System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
|
||||||
Object[] args = randomArgs(target2.type().parameterArray());
|
Object[] args = randomArgs(target2.type().parameterArray());
|
||||||
// make sure the target does what we think it does:
|
// make sure the target does what we think it does:
|
||||||
if (pos == 0 && nargs < 5) {
|
if (pos == 0 && nargs < 5 && !argType.isPrimitive()) {
|
||||||
Object[] check = (Object[]) target.invokeWithArguments(args);
|
Object[] check = (Object[]) (Object) target.invokeWithArguments(args);
|
||||||
assertArrayEquals(args, check);
|
assertArrayEquals(args, check);
|
||||||
switch (nargs) {
|
switch (nargs) {
|
||||||
case 0:
|
case 0:
|
||||||
check = (Object[]) target.invokeExact();
|
check = (Object[]) (Object) target.invokeExact();
|
||||||
assertArrayEquals(args, check);
|
assertArrayEquals(args, check);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
check = (Object[]) target.invokeExact(args[0]);
|
check = (Object[]) (Object) target.invokeExact(args[0]);
|
||||||
assertArrayEquals(args, check);
|
assertArrayEquals(args, check);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
check = (Object[]) target.invokeExact(args[0], args[1]);
|
check = (Object[]) (Object) target.invokeExact(args[0], args[1]);
|
||||||
assertArrayEquals(args, check);
|
assertArrayEquals(args, check);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1483,23 +1475,50 @@ public class MethodHandlesTest {
|
|||||||
List<Class<?>> newParams = new ArrayList<Class<?>>(target2.type().parameterList());
|
List<Class<?>> newParams = new ArrayList<Class<?>>(target2.type().parameterList());
|
||||||
{ // modify newParams in place
|
{ // modify newParams in place
|
||||||
List<Class<?>> spreadParams = newParams.subList(pos, nargs);
|
List<Class<?>> spreadParams = newParams.subList(pos, nargs);
|
||||||
spreadParams.clear(); spreadParams.add(Object[].class);
|
spreadParams.clear(); spreadParams.add(arrayType);
|
||||||
}
|
}
|
||||||
MethodType newType = MethodType.methodType(Object.class, newParams);
|
MethodType newType = MethodType.methodType(arrayType, newParams);
|
||||||
MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType);
|
MethodHandle result = target2.asSpreader(arrayType, nargs-pos);
|
||||||
Object[] returnValue;
|
assert(result.type() == newType) : Arrays.asList(result, newType);
|
||||||
|
result = result.asType(newType.generic());
|
||||||
|
Object returnValue;
|
||||||
if (pos == 0) {
|
if (pos == 0) {
|
||||||
// In the following line, the first cast implies
|
Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
|
||||||
// normal Object return value for the MH call (Object[])->Object,
|
returnValue = result.invokeExact(args2);
|
||||||
// while the second cast dynamically converts to an Object array.
|
|
||||||
// Such a double cast is typical of MH.invokeExact.
|
|
||||||
returnValue = (Object[]) (Object) result.invokeExact(args);
|
|
||||||
} else {
|
} else {
|
||||||
Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
|
Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
|
||||||
args1[pos] = Arrays.copyOfRange(args, pos, args.length);
|
args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
|
||||||
returnValue = (Object[]) result.invokeWithArguments(args1);
|
returnValue = result.invokeWithArguments(args1);
|
||||||
|
}
|
||||||
|
String argstr = Arrays.toString(args);
|
||||||
|
if (!argType.isPrimitive()) {
|
||||||
|
Object[] rv = (Object[]) returnValue;
|
||||||
|
String rvs = Arrays.toString(rv);
|
||||||
|
if (!Arrays.equals(args, rv)) {
|
||||||
|
System.out.println("method: "+result);
|
||||||
|
System.out.println("expected: "+argstr);
|
||||||
|
System.out.println("returned: "+rvs);
|
||||||
|
assertArrayEquals(args, rv);
|
||||||
|
}
|
||||||
|
} else if (argType == int.class) {
|
||||||
|
String rvs = Arrays.toString((int[]) returnValue);
|
||||||
|
if (!argstr.equals(rvs)) {
|
||||||
|
System.out.println("method: "+result);
|
||||||
|
System.out.println("expected: "+argstr);
|
||||||
|
System.out.println("returned: "+rvs);
|
||||||
|
assertEquals(argstr, rvs);
|
||||||
|
}
|
||||||
|
} else if (argType == long.class) {
|
||||||
|
String rvs = Arrays.toString((long[]) returnValue);
|
||||||
|
if (!argstr.equals(rvs)) {
|
||||||
|
System.out.println("method: "+result);
|
||||||
|
System.out.println("expected: "+argstr);
|
||||||
|
System.out.println("returned: "+rvs);
|
||||||
|
assertEquals(argstr, rvs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// cannot test...
|
||||||
}
|
}
|
||||||
assertArrayEquals(args, returnValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -1780,7 +1799,7 @@ public class MethodHandlesTest {
|
|||||||
assertCalled("invokee", args);
|
assertCalled("invokee", args);
|
||||||
// generic invoker
|
// generic invoker
|
||||||
countTest();
|
countTest();
|
||||||
inv = MethodHandles.genericInvoker(type);
|
inv = MethodHandles.invoker(type);
|
||||||
if (nargs <= 3) {
|
if (nargs <= 3) {
|
||||||
calledLog.clear();
|
calledLog.clear();
|
||||||
switch (nargs) {
|
switch (nargs) {
|
||||||
@ -2130,15 +2149,12 @@ public class MethodHandlesTest {
|
|||||||
Object z = surprise.invokeExact(x);
|
Object z = surprise.invokeExact(x);
|
||||||
System.out.println("Failed to throw; got z="+z);
|
System.out.println("Failed to throw; got z="+z);
|
||||||
assertTrue(false);
|
assertTrue(false);
|
||||||
} catch (Exception ex) {
|
} catch (ClassCastException ex) {
|
||||||
if (verbosity > 2)
|
if (verbosity > 2)
|
||||||
System.out.println("caught "+ex);
|
System.out.println("caught "+ex);
|
||||||
if (verbosity > 3)
|
if (verbosity > 3)
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
assertTrue(ex instanceof ClassCastException
|
assertTrue(true); // all is well
|
||||||
// FIXME: accept only one of the two for any given unit test
|
|
||||||
|| ex instanceof WrongMethodTypeException
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2328,6 +2344,34 @@ class ValueConversions {
|
|||||||
// else need to spin bytecode or do something else fancy
|
// else need to spin bytecode or do something else fancy
|
||||||
throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs);
|
throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs);
|
||||||
}
|
}
|
||||||
|
public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
|
||||||
|
Class<?> elemType = arrayType.getComponentType();
|
||||||
|
MethodType vaType = MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType));
|
||||||
|
MethodHandle mh = varargsArray(nargs);
|
||||||
|
if (arrayType != Object[].class)
|
||||||
|
mh = MethodHandles.filterReturnValue(mh, CHANGE_ARRAY_TYPE.bindTo(arrayType));
|
||||||
|
return mh.asType(vaType);
|
||||||
|
}
|
||||||
|
static Object changeArrayType(Class<?> arrayType, Object[] a) {
|
||||||
|
Class<?> elemType = arrayType.getComponentType();
|
||||||
|
if (!elemType.isPrimitive())
|
||||||
|
return Arrays.copyOf(a, a.length, arrayType.asSubclass(Object[].class));
|
||||||
|
Object b = java.lang.reflect.Array.newInstance(elemType, a.length);
|
||||||
|
for (int i = 0; i < a.length; i++)
|
||||||
|
java.lang.reflect.Array.set(b, i, a[i]);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
private static final MethodHandle CHANGE_ARRAY_TYPE;
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
CHANGE_ARRAY_TYPE = IMPL_LOOKUP.findStatic(ValueConversions.class, "changeArrayType",
|
||||||
|
MethodType.methodType(Object.class, Class.class, Object[].class));
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException ex) {
|
||||||
|
Error err = new InternalError("uncaught exception");
|
||||||
|
err.initCause(ex);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
|
private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
|
||||||
private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
|
private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
|
||||||
|
582
jdk/test/java/lang/invoke/RicochetTest.java
Normal file
582
jdk/test/java/lang/invoke/RicochetTest.java
Normal file
@ -0,0 +1,582 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @test
|
||||||
|
* @summary unit tests for recursive method handles
|
||||||
|
* @run junit/othervm -DRicochetTest.MAX_ARITY=255 test.java.lang.invoke.RicochetTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
package test.java.lang.invoke;
|
||||||
|
|
||||||
|
import java.lang.invoke.*;
|
||||||
|
import java.util.*;
|
||||||
|
import org.junit.*;
|
||||||
|
import static java.lang.invoke.MethodType.*;
|
||||||
|
import static java.lang.invoke.MethodHandles.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.junit.Assume.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jrose
|
||||||
|
*/
|
||||||
|
public class RicochetTest {
|
||||||
|
private static final Class CLASS = RicochetTest.class;
|
||||||
|
private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40);
|
||||||
|
|
||||||
|
public static void main(String... av) throws Throwable {
|
||||||
|
RicochetTest test = new RicochetTest();
|
||||||
|
if (av.length > 0) test.testOnly = Arrays.asList(av).toString();
|
||||||
|
if (REPEAT == 1 || test.testOnly != null) {
|
||||||
|
test.testAll();
|
||||||
|
if (test.testOnlyTests == null) throw new RuntimeException("no matching test: "+test.testOnly);
|
||||||
|
} else if (REPEAT == 0) {
|
||||||
|
org.junit.runner.JUnitCore.runClasses(RicochetTest.class);
|
||||||
|
} else {
|
||||||
|
verbose(1, "REPEAT="+REPEAT);
|
||||||
|
for (int i = 0; i < REPEAT; i++) {
|
||||||
|
test.testRepetition = (i+1);
|
||||||
|
verbose(0, "[#"+test.testRepetition+"]");
|
||||||
|
test.testAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int testRepetition;
|
||||||
|
|
||||||
|
public void testAll() throws Throwable {
|
||||||
|
testNull();
|
||||||
|
testBoxInteger();
|
||||||
|
testFilterReturnValue();
|
||||||
|
testFilterObject();
|
||||||
|
testBoxLong();
|
||||||
|
testFilterInteger();
|
||||||
|
testIntSpreads();
|
||||||
|
testByteSpreads();
|
||||||
|
testLongSpreads();
|
||||||
|
testIntCollects();
|
||||||
|
testReturns();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNull() throws Throwable {
|
||||||
|
if (testRepetition > (1+REPEAT/100)) return; // trivial test
|
||||||
|
if (!startTest("testNull")) return;
|
||||||
|
assertEquals(opI(37), opI.invokeWithArguments(37));
|
||||||
|
assertEqualFunction(opI, opI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBoxInteger() throws Throwable {
|
||||||
|
if (!startTest("testBoxInteger")) return;
|
||||||
|
assertEqualFunction(opI, opI.asType(opL_I.type()).asType(opI.type()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterReturnValue() throws Throwable {
|
||||||
|
if (!startTest("testFilterReturnValue")) return;
|
||||||
|
int[] ints = { 12, 23, 34, 45, 56, 67, 78, 89 };
|
||||||
|
Object res = list8ints.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
|
||||||
|
assertEquals(Arrays.toString(ints), res.toString());
|
||||||
|
MethodHandle idreturn = filterReturnValue(list8ints, identity(Object.class));
|
||||||
|
res = idreturn.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
|
||||||
|
assertEquals(Arrays.toString(ints), res.toString());
|
||||||
|
MethodHandle add0 = addL.bindTo(0);
|
||||||
|
assertEqualFunction(filterReturnValue(opL2, add0), opL2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterObject() throws Throwable {
|
||||||
|
if (!startTest("testFilterObject")) return;
|
||||||
|
MethodHandle add0 = addL.bindTo(0);
|
||||||
|
assertEqualFunction(sequence(opL2, add0), opL2);
|
||||||
|
int bump13 = -13; // value near 20 works as long as test values are near [-80..80]
|
||||||
|
MethodHandle add13 = addL.bindTo(bump13);
|
||||||
|
MethodHandle add13_0 = addL.bindTo(opI2(bump13, 0));
|
||||||
|
MethodHandle add13_1 = addL.bindTo(opI2(0, bump13));
|
||||||
|
assertEqualFunction(sequence(opL2, add13_0),
|
||||||
|
filterArguments(opL2, 0, add13));
|
||||||
|
assertEqualFunction(sequence(opL2, add13_1),
|
||||||
|
filterArguments(opL2, 1, add13));
|
||||||
|
System.out.println("[testFilterObject done]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBoxLong() throws Throwable {
|
||||||
|
if (!startTest("testBoxLong")) return;
|
||||||
|
assertEqualFunction(opJ, opJ.asType(opL_J.type()).asType(opJ.type()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterInteger() throws Throwable {
|
||||||
|
if (!startTest("testFilterInteger")) return;
|
||||||
|
assertEqualFunction(opI, sequence(convI_L, opL_I));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntSpreads() throws Throwable {
|
||||||
|
if (!startTest("testIntSpreads")) return;
|
||||||
|
MethodHandle id = identity(int[].class);
|
||||||
|
final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
|
||||||
|
for (int nargs = 0; nargs <= MAX; nargs++) {
|
||||||
|
if (nargs > 30 && nargs < MAX-20) nargs += 10;
|
||||||
|
int[] args = new int[nargs];
|
||||||
|
for (int j = 0; j < args.length; j++) args[j] = (int)(j + 11);
|
||||||
|
//System.out.println("testIntSpreads "+Arrays.toString(args));
|
||||||
|
int[] args1 = (int[]) id.invokeExact(args);
|
||||||
|
assertArrayEquals(args, args1);
|
||||||
|
MethodHandle coll = id.asCollector(int[].class, nargs);
|
||||||
|
int[] args2 = args;
|
||||||
|
switch (nargs) {
|
||||||
|
case 0: args2 = (int[]) coll.invokeExact(); break;
|
||||||
|
case 1: args2 = (int[]) coll.invokeExact(args[0]); break;
|
||||||
|
case 2: args2 = (int[]) coll.invokeExact(args[0], args[1]); break;
|
||||||
|
case 3: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2]); break;
|
||||||
|
case 4: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
|
||||||
|
case 5: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
|
||||||
|
}
|
||||||
|
assertArrayEquals(args, args2);
|
||||||
|
MethodHandle mh = coll.asSpreader(int[].class, nargs);
|
||||||
|
int[] args3 = (int[]) mh.invokeExact(args);
|
||||||
|
assertArrayEquals(args, args3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testByteSpreads() throws Throwable {
|
||||||
|
if (!startTest("testByteSpreads")) return;
|
||||||
|
MethodHandle id = identity(byte[].class);
|
||||||
|
final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
|
||||||
|
for (int nargs = 0; nargs <= MAX; nargs++) {
|
||||||
|
if (nargs > 30 && nargs < MAX-20) nargs += 10;
|
||||||
|
byte[] args = new byte[nargs];
|
||||||
|
for (int j = 0; j < args.length; j++) args[j] = (byte)(j + 11);
|
||||||
|
//System.out.println("testByteSpreads "+Arrays.toString(args));
|
||||||
|
byte[] args1 = (byte[]) id.invokeExact(args);
|
||||||
|
assertArrayEquals(args, args1);
|
||||||
|
MethodHandle coll = id.asCollector(byte[].class, nargs);
|
||||||
|
byte[] args2 = args;
|
||||||
|
switch (nargs) {
|
||||||
|
case 0: args2 = (byte[]) coll.invokeExact(); break;
|
||||||
|
case 1: args2 = (byte[]) coll.invokeExact(args[0]); break;
|
||||||
|
case 2: args2 = (byte[]) coll.invokeExact(args[0], args[1]); break;
|
||||||
|
case 3: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2]); break;
|
||||||
|
case 4: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
|
||||||
|
case 5: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
|
||||||
|
}
|
||||||
|
assertArrayEquals(args, args2);
|
||||||
|
MethodHandle mh = coll.asSpreader(byte[].class, nargs);
|
||||||
|
byte[] args3 = (byte[]) mh.invokeExact(args);
|
||||||
|
assertArrayEquals(args, args3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLongSpreads() throws Throwable {
|
||||||
|
if (!startTest("testLongSpreads")) return;
|
||||||
|
MethodHandle id = identity(long[].class);
|
||||||
|
final int MAX = (MAX_ARITY - 2) / 2; // 253/2+1 would cause parameter overflow with 'this' added
|
||||||
|
for (int nargs = 0; nargs <= MAX; nargs++) {
|
||||||
|
if (nargs > 30 && nargs < MAX-20) nargs += 10;
|
||||||
|
long[] args = new long[nargs];
|
||||||
|
for (int j = 0; j < args.length; j++) args[j] = (long)(j + 11);
|
||||||
|
//System.out.println("testLongSpreads "+Arrays.toString(args));
|
||||||
|
long[] args1 = (long[]) id.invokeExact(args);
|
||||||
|
assertArrayEquals(args, args1);
|
||||||
|
MethodHandle coll = id.asCollector(long[].class, nargs);
|
||||||
|
long[] args2 = args;
|
||||||
|
switch (nargs) {
|
||||||
|
case 0: args2 = (long[]) coll.invokeExact(); break;
|
||||||
|
case 1: args2 = (long[]) coll.invokeExact(args[0]); break;
|
||||||
|
case 2: args2 = (long[]) coll.invokeExact(args[0], args[1]); break;
|
||||||
|
case 3: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2]); break;
|
||||||
|
case 4: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
|
||||||
|
case 5: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
|
||||||
|
}
|
||||||
|
assertArrayEquals(args, args2);
|
||||||
|
MethodHandle mh = coll.asSpreader(long[].class, nargs);
|
||||||
|
long[] args3 = (long[]) mh.invokeExact(args);
|
||||||
|
assertArrayEquals(args, args3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntCollects() throws Throwable {
|
||||||
|
if (!startTest("testIntCollects")) return;
|
||||||
|
for (MethodHandle lister : INT_LISTERS) {
|
||||||
|
int outputs = lister.type().parameterCount();
|
||||||
|
for (int collects = 0; collects <= Math.min(outputs, INT_COLLECTORS.length-1); collects++) {
|
||||||
|
int inputs = outputs - 1 + collects;
|
||||||
|
if (inputs < 0) continue;
|
||||||
|
for (int pos = 0; pos + collects <= inputs; pos++) {
|
||||||
|
MethodHandle collector = INT_COLLECTORS[collects];
|
||||||
|
int[] args = new int[inputs];
|
||||||
|
int ap = 0, arg = 31;
|
||||||
|
for (int i = 0; i < pos; i++)
|
||||||
|
args[ap++] = arg++ + 0;
|
||||||
|
for (int i = 0; i < collects; i++)
|
||||||
|
args[ap++] = arg++ + 10;
|
||||||
|
while (ap < args.length)
|
||||||
|
args[ap++] = arg++ + 20;
|
||||||
|
// calculate piecemeal:
|
||||||
|
//System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
|
||||||
|
int[] collargs = Arrays.copyOfRange(args, pos, pos+collects);
|
||||||
|
int coll = (int) collector.asSpreader(int[].class, collargs.length).invokeExact(collargs);
|
||||||
|
int[] listargs = Arrays.copyOfRange(args, 0, outputs);
|
||||||
|
System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1));
|
||||||
|
listargs[pos] = coll;
|
||||||
|
//System.out.println(" coll="+coll+" listargs="+Arrays.toString(listargs));
|
||||||
|
Object expect = lister.asSpreader(int[].class, listargs.length).invokeExact(listargs);
|
||||||
|
//System.out.println(" expect="+expect);
|
||||||
|
|
||||||
|
// now use the combined MH, and test the output:
|
||||||
|
MethodHandle mh = collectArguments(lister, pos, INT_COLLECTORS[collects]);
|
||||||
|
if (mh == null) continue; // no infix collection, yet
|
||||||
|
assert(mh.type().parameterCount() == inputs);
|
||||||
|
Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args);
|
||||||
|
assertEquals(expect, observe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle collectArguments(MethodHandle lister, int pos, MethodHandle collector) {
|
||||||
|
int collects = collector.type().parameterCount();
|
||||||
|
int outputs = lister.type().parameterCount();
|
||||||
|
if (pos == outputs - 1)
|
||||||
|
return MethodHandles.filterArguments(lister, pos,
|
||||||
|
collector.asSpreader(int[].class, collects))
|
||||||
|
.asCollector(int[].class, collects);
|
||||||
|
//return MethodHandles.collectArguments(lister, pos, collector); //no such animal
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?>[] RETURN_TYPES = {
|
||||||
|
Object.class, String.class, Integer.class,
|
||||||
|
int.class, long.class,
|
||||||
|
boolean.class, byte.class, char.class, short.class,
|
||||||
|
float.class, double.class,
|
||||||
|
void.class,
|
||||||
|
};
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReturns() throws Throwable {
|
||||||
|
if (!startTest("testReturns")) return;
|
||||||
|
// fault injection:
|
||||||
|
int faultCount = 0; // total of 1296 tests
|
||||||
|
faultCount = Integer.getInteger("testReturns.faultCount", 0);
|
||||||
|
for (Class<?> ret : RETURN_TYPES) {
|
||||||
|
// make a complicated identity function and pass something through it
|
||||||
|
System.out.println(ret.getSimpleName());
|
||||||
|
Class<?> vret = (ret == void.class) ? Void.class : ret;
|
||||||
|
MethodHandle id = // (vret)->ret
|
||||||
|
identity(vret).asType(methodType(ret, vret));
|
||||||
|
final int LENGTH = 4;
|
||||||
|
int[] index = {0};
|
||||||
|
Object vals = java.lang.reflect.Array.newInstance(vret, LENGTH);
|
||||||
|
MethodHandle indexGetter = //()->int
|
||||||
|
insertArguments(arrayElementGetter(index.getClass()), 0, index, 0);
|
||||||
|
MethodHandle valSelector = // (int)->vret
|
||||||
|
arrayElementGetter(vals.getClass()).bindTo(vals);
|
||||||
|
MethodHandle valGetter = // ()->vret
|
||||||
|
foldArguments(valSelector, indexGetter);
|
||||||
|
if (ret != void.class) {
|
||||||
|
for (int i = 0; i < LENGTH; i++) {
|
||||||
|
Object val = (i + 50);
|
||||||
|
if (ret == boolean.class) val = (i % 3 == 0);
|
||||||
|
if (ret == String.class) val = "#"+i;
|
||||||
|
if (ret == char.class) val = (char)('a'+i);
|
||||||
|
if (ret == byte.class) val = (byte)~i;
|
||||||
|
if (ret == short.class) val = (short)(1<<i);
|
||||||
|
java.lang.reflect.Array.set(vals, i, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < LENGTH; i++) {
|
||||||
|
Object val = java.lang.reflect.Array.get(vals, i);
|
||||||
|
System.out.println(i+" => "+val);
|
||||||
|
index[0] = i;
|
||||||
|
if (--faultCount == 0) index[0] ^= 1;
|
||||||
|
Object x = valGetter.invokeWithArguments();
|
||||||
|
assertEquals(val, x);
|
||||||
|
// make a return-filter call: x = id(valGetter())
|
||||||
|
if (--faultCount == 0) index[0] ^= 1;
|
||||||
|
x = filterReturnValue(valGetter, id).invokeWithArguments();
|
||||||
|
assertEquals(val, x);
|
||||||
|
// make a filter call: x = id(*,valGetter(),*)
|
||||||
|
for (int len = 1; len <= 4; len++) {
|
||||||
|
for (int pos = 0; pos < len; pos++) {
|
||||||
|
MethodHandle proj = id; // lambda(..., vret x,...){x}
|
||||||
|
for (int j = 0; j < len; j++) {
|
||||||
|
if (j == pos) continue;
|
||||||
|
proj = dropArguments(proj, j, Object.class);
|
||||||
|
}
|
||||||
|
assert(proj.type().parameterCount() == len);
|
||||||
|
// proj: (Object*, pos: vret, Object*)->ret
|
||||||
|
assertEquals(vret, proj.type().parameterType(pos));
|
||||||
|
MethodHandle vgFilter = dropArguments(valGetter, 0, Object.class);
|
||||||
|
if (--faultCount == 0) index[0] ^= 1;
|
||||||
|
x = filterArguments(proj, pos, vgFilter).invokeWithArguments(new Object[len]);
|
||||||
|
assertEquals(val, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// make a fold call:
|
||||||
|
for (int len = 0; len <= 4; len++) {
|
||||||
|
for (int fold = 0; fold <= len; fold++) {
|
||||||
|
MethodHandle proj = id; // lambda(ret x, ...){x}
|
||||||
|
if (ret == void.class) proj = constant(Object.class, null);
|
||||||
|
int arg0 = (ret == void.class ? 0 : 1);
|
||||||
|
for (int j = 0; j < len; j++) {
|
||||||
|
proj = dropArguments(proj, arg0, Object.class);
|
||||||
|
}
|
||||||
|
assert(proj.type().parameterCount() == arg0 + len);
|
||||||
|
// proj: (Object*, pos: vret, Object*)->ret
|
||||||
|
if (arg0 != 0) assertEquals(vret, proj.type().parameterType(0));
|
||||||
|
MethodHandle vgFilter = valGetter.asType(methodType(ret));
|
||||||
|
for (int j = 0; j < fold; j++) {
|
||||||
|
vgFilter = dropArguments(vgFilter, j, Object.class);
|
||||||
|
}
|
||||||
|
x = foldArguments(proj, vgFilter).invokeWithArguments(new Object[len]);
|
||||||
|
if (--faultCount == 0) index[0] ^= 1;
|
||||||
|
assertEquals(val, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//System.out.println("faultCount="+faultCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle sequence(MethodHandle mh1, MethodHandle... mhs) {
|
||||||
|
MethodHandle res = mh1;
|
||||||
|
for (MethodHandle mh2 : mhs)
|
||||||
|
res = filterReturnValue(res, mh2);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private static void assertEqualFunction(MethodHandle x, MethodHandle y) throws Throwable {
|
||||||
|
assertEquals(x.type(), y.type()); //??
|
||||||
|
MethodType t = x.type();
|
||||||
|
if (t.parameterCount() == 0) {
|
||||||
|
assertEqualFunctionAt(null, x, y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Class<?> ptype = t.parameterType(0);
|
||||||
|
if (ptype == long.class || ptype == Long.class) {
|
||||||
|
for (long i = -10; i <= 10; i++) {
|
||||||
|
assertEqualFunctionAt(i, x, y);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = -10; i <= 10; i++) {
|
||||||
|
assertEqualFunctionAt(i, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void assertEqualFunctionAt(Object v, MethodHandle x, MethodHandle y) throws Throwable {
|
||||||
|
Object[] args = new Object[x.type().parameterCount()];
|
||||||
|
Arrays.fill(args, v);
|
||||||
|
Object xval = invokeWithCatch(x, args);
|
||||||
|
Object yval = invokeWithCatch(y, args);
|
||||||
|
String msg = "ok";
|
||||||
|
if (!Objects.equals(xval, yval)) {
|
||||||
|
msg = ("applying "+x+" & "+y+" to "+v);
|
||||||
|
}
|
||||||
|
assertEquals(msg, xval, yval);
|
||||||
|
}
|
||||||
|
private static Object invokeWithCatch(MethodHandle mh, Object... args) throws Throwable {
|
||||||
|
try {
|
||||||
|
return mh.invokeWithArguments(args);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
System.out.println("threw: "+mh+Arrays.asList(args));
|
||||||
|
ex.printStackTrace();
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Lookup LOOKUP = lookup();
|
||||||
|
private static MethodHandle findStatic(String name,
|
||||||
|
Class<?> rtype,
|
||||||
|
Class<?>... ptypes) {
|
||||||
|
try {
|
||||||
|
return LOOKUP.findStatic(LOOKUP.lookupClass(), name, methodType(rtype, ptypes));
|
||||||
|
} catch (ReflectiveOperationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static MethodHandle findStatic(String name,
|
||||||
|
Class<?> rtype,
|
||||||
|
List<?> ptypes) {
|
||||||
|
return findStatic(name, rtype, ptypes.toArray(new Class<?>[ptypes.size()]));
|
||||||
|
}
|
||||||
|
static int getProperty(String name, int dflt) {
|
||||||
|
String qual = LOOKUP.lookupClass().getName();
|
||||||
|
String prop = System.getProperty(qual+"."+name);
|
||||||
|
if (prop == null) prop = System.getProperty(name);
|
||||||
|
if (prop == null) return dflt;
|
||||||
|
return Integer.parseInt(prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int opI(int... xs) {
|
||||||
|
stress();
|
||||||
|
int base = 100;
|
||||||
|
int z = 0;
|
||||||
|
for (int x : xs) {
|
||||||
|
z = (z * base) + (x % base);
|
||||||
|
}
|
||||||
|
verbose("opI", xs.length, xs, z);
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
private static int opI2(int x, int y) { return opI(x, y); } // x*100 + y%100
|
||||||
|
private static int opI3(int x, int y, int z) { return opI(x, y, z); }
|
||||||
|
private static int opI4(int w, int x, int y, int z) { return opI(w, x, y, z); }
|
||||||
|
private static int opI(int x) { return opI2(x, 37); }
|
||||||
|
private static Object opI_L(int x) { return (Object) opI(x); }
|
||||||
|
private static long opJ3(long x, long y, long z) { return (long) opI3((int)x, (int)y, (int)z); }
|
||||||
|
private static long opJ2(long x, long y) { return (long) opI2((int)x, (int)y); }
|
||||||
|
private static long opJ(long x) { return (long) opI((int)x); }
|
||||||
|
private static Object opL2(Object x, Object y) { return (Object) opI2((int)x, (int)y); }
|
||||||
|
private static Object opL(Object x) { return (Object) opI((int)x); }
|
||||||
|
private static int opL2_I(Object x, Object y) { return (int) opI2((int)x, (int)y); }
|
||||||
|
private static int opL_I(Object x) { return (int) opI((int)x); }
|
||||||
|
private static long opL_J(Object x) { return (long) opI((int)x); }
|
||||||
|
private static final MethodHandle opI, opI2, opI3, opI4, opI_L, opJ, opJ2, opJ3, opL2, opL, opL2_I, opL_I, opL_J;
|
||||||
|
static {
|
||||||
|
opI4 = findStatic("opI4", int.class, int.class, int.class, int.class, int.class);
|
||||||
|
opI3 = findStatic("opI3", int.class, int.class, int.class, int.class);
|
||||||
|
opI2 = findStatic("opI2", int.class, int.class, int.class);
|
||||||
|
opI = findStatic("opI", int.class, int.class);
|
||||||
|
opI_L = findStatic("opI_L", Object.class, int.class);
|
||||||
|
opJ = findStatic("opJ", long.class, long.class);
|
||||||
|
opJ2 = findStatic("opJ2", long.class, long.class, long.class);
|
||||||
|
opJ3 = findStatic("opJ3", long.class, long.class, long.class, long.class);
|
||||||
|
opL2 = findStatic("opL2", Object.class, Object.class, Object.class);
|
||||||
|
opL = findStatic("opL", Object.class, Object.class);
|
||||||
|
opL2_I = findStatic("opL2_I", int.class, Object.class, Object.class);
|
||||||
|
opL_I = findStatic("opL_I", int.class, Object.class);
|
||||||
|
opL_J = findStatic("opL_J", long.class, Object.class);
|
||||||
|
}
|
||||||
|
private static final MethodHandle[] INT_COLLECTORS = {
|
||||||
|
constant(int.class, 42), opI, opI2, opI3, opI4
|
||||||
|
};
|
||||||
|
private static final MethodHandle[] LONG_COLLECTORS = {
|
||||||
|
constant(long.class, 42), opJ, opJ2, opJ3
|
||||||
|
};
|
||||||
|
|
||||||
|
private static int addI(int x, int y) { stress(); return x+y; }
|
||||||
|
private static Object addL(Object x, Object y) { return addI((int)x, (int)y); }
|
||||||
|
private static final MethodHandle addI, addL;
|
||||||
|
static {
|
||||||
|
addI = findStatic("addI", int.class, int.class, int.class);
|
||||||
|
addL = findStatic("addL", Object.class, Object.class, Object.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object list8ints(int a, int b, int c, int d, int e, int f, int g, int h) {
|
||||||
|
return Arrays.asList(a, b, c, d, e, f, g, h);
|
||||||
|
}
|
||||||
|
private static Object list8longs(long a, long b, long c, long d, long e, long f, long g, long h) {
|
||||||
|
return Arrays.asList(a, b, c, d, e, f, g, h);
|
||||||
|
}
|
||||||
|
private static final MethodHandle list8ints = findStatic("list8ints", Object.class,
|
||||||
|
Collections.nCopies(8, int.class));
|
||||||
|
private static final MethodHandle list8longs = findStatic("list8longs", Object.class,
|
||||||
|
Collections.nCopies(8, long.class));
|
||||||
|
private static final MethodHandle[] INT_LISTERS, LONG_LISTERS;
|
||||||
|
static {
|
||||||
|
int listerCount = list8ints.type().parameterCount() + 1;
|
||||||
|
INT_LISTERS = new MethodHandle[listerCount];
|
||||||
|
LONG_LISTERS = new MethodHandle[listerCount];
|
||||||
|
MethodHandle lister = list8ints;
|
||||||
|
MethodHandle llister = list8longs;
|
||||||
|
for (int i = listerCount - 1; ; i--) {
|
||||||
|
INT_LISTERS[i] = lister;
|
||||||
|
LONG_LISTERS[i] = llister;
|
||||||
|
if (i == 0) break;
|
||||||
|
lister = insertArguments(lister, i-1, (int)0);
|
||||||
|
llister = insertArguments(llister, i-1, (long)0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object convI_L(int x) { stress(); return (Object) x; }
|
||||||
|
private static int convL_I(Object x) { stress(); return (int) x; }
|
||||||
|
private static Object convJ_L(long x) { stress(); return (Object) x; }
|
||||||
|
private static long convL_J(Object x) { stress(); return (long) x; }
|
||||||
|
private static int convJ_I(long x) { stress(); return (int) x; }
|
||||||
|
private static long convI_J(int x) { stress(); return (long) x; }
|
||||||
|
private static final MethodHandle convI_L, convL_I, convJ_L, convL_J, convJ_I, convI_J;
|
||||||
|
static {
|
||||||
|
convI_L = findStatic("convI_L", Object.class, int.class);
|
||||||
|
convL_I = findStatic("convL_I", int.class, Object.class);
|
||||||
|
convJ_L = findStatic("convJ_L", Object.class, long.class);
|
||||||
|
convL_J = findStatic("convL_J", long.class, Object.class);
|
||||||
|
convJ_I = findStatic("convJ_I", int.class, long.class);
|
||||||
|
convI_J = findStatic("convI_J", long.class, int.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stress modes:
|
||||||
|
private static final int REPEAT = getProperty("REPEAT", 0);
|
||||||
|
private static final int STRESS = getProperty("STRESS", 0);
|
||||||
|
private static /*v*/ int STRESS_COUNT;
|
||||||
|
private static final Object[] SINK = new Object[4];
|
||||||
|
private static void stress() {
|
||||||
|
if (STRESS <= 0) return;
|
||||||
|
int count = STRESS + (STRESS_COUNT++ & 0x1); // non-constant value
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
SINK[i % SINK.length] = new Object[STRESS + i % (SINK.length + 1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// verbosity:
|
||||||
|
private static final int VERBOSITY = getProperty("VERBOSITY", 0) + (REPEAT == 0 ? 0 : -1);
|
||||||
|
private static void verbose(Object a, Object b, Object c, Object d) {
|
||||||
|
if (VERBOSITY <= 0) return;
|
||||||
|
verbose(1, a, b, c, d);
|
||||||
|
}
|
||||||
|
private static void verbose(Object a, Object b, Object c) {
|
||||||
|
if (VERBOSITY <= 0) return;
|
||||||
|
verbose(1, a, b, c);
|
||||||
|
}
|
||||||
|
private static void verbose(int level, Object a, Object... bcd) {
|
||||||
|
if (level > VERBOSITY) return;
|
||||||
|
String m = a.toString();
|
||||||
|
if (bcd != null && bcd.length > 0) {
|
||||||
|
List<Object> l = new ArrayList<>(bcd.length);
|
||||||
|
for (Object x : bcd) {
|
||||||
|
if (x instanceof Object[]) x = Arrays.asList((Object[])x);
|
||||||
|
if (x instanceof int[]) x = Arrays.toString((int[])x);
|
||||||
|
if (x instanceof long[]) x = Arrays.toString((long[])x);
|
||||||
|
l.add(x);
|
||||||
|
}
|
||||||
|
m = m+Arrays.asList(bcd);
|
||||||
|
}
|
||||||
|
System.out.println(m);
|
||||||
|
}
|
||||||
|
String testOnly;
|
||||||
|
String testOnlyTests;
|
||||||
|
private boolean startTest(String name) {
|
||||||
|
if (testOnly != null && !testOnly.contains(name))
|
||||||
|
return false;
|
||||||
|
verbose(0, "["+name+"]");
|
||||||
|
testOnlyTests = (testOnlyTests == null) ? name : testOnlyTests+" "+name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
448
jdk/test/sun/invoke/util/ValueConversionsTest.java
Normal file
448
jdk/test/sun/invoke/util/ValueConversionsTest.java
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package test.sun.invoke.util;
|
||||||
|
|
||||||
|
import sun.invoke.util.ValueConversions;
|
||||||
|
import sun.invoke.util.Wrapper;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/* @test
|
||||||
|
* @summary unit tests for value-type conversion utilities
|
||||||
|
* @ignore This test requires a special compilation environment to access sun.inovke.util. Run by hand.
|
||||||
|
* @run junit/othervm test.sun.invoke.util.ValueConversionsTest
|
||||||
|
* @run junit/othervm
|
||||||
|
* -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.START_ARITY=250
|
||||||
|
* test.sun.invoke.util.ValueConversionsTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This might take a while and burn lots of metadata:
|
||||||
|
// @run junit/othervm -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.EXHAUSTIVE=true test.sun.invoke.util.ValueConversionsTest
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jrose
|
||||||
|
*/
|
||||||
|
public class ValueConversionsTest {
|
||||||
|
private static final Class CLASS = ValueConversionsTest.class;
|
||||||
|
private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40);
|
||||||
|
private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0);
|
||||||
|
private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnbox() throws Throwable {
|
||||||
|
testUnbox(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnboxCast() throws Throwable {
|
||||||
|
testUnbox(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testUnbox(boolean doCast) throws Throwable {
|
||||||
|
//System.out.println("unbox");
|
||||||
|
for (Wrapper dst : Wrapper.values()) {
|
||||||
|
//System.out.println(dst);
|
||||||
|
for (Wrapper src : Wrapper.values()) {
|
||||||
|
testUnbox(doCast, dst, src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.OBJECT)
|
||||||
|
expectThrow = false; // everything (even VOID==null here) converts to OBJECT
|
||||||
|
try {
|
||||||
|
for (int n = -5; n < 10; n++) {
|
||||||
|
Object box = src.wrap(n);
|
||||||
|
switch (src) {
|
||||||
|
case VOID: assertEquals(box, null); break;
|
||||||
|
case OBJECT: box = box.toString(); break;
|
||||||
|
case SHORT: assertEquals(box.getClass(), Short.class); break;
|
||||||
|
default: assertEquals(box.getClass(), src.wrapperType()); break;
|
||||||
|
}
|
||||||
|
MethodHandle unboxer;
|
||||||
|
if (doCast)
|
||||||
|
unboxer = ValueConversions.unboxCast(dst.primitiveType());
|
||||||
|
else
|
||||||
|
unboxer = ValueConversions.unbox(dst.primitiveType());
|
||||||
|
Object expResult = (box == null) ? dst.zero() : dst.wrap(box);
|
||||||
|
Object result = null;
|
||||||
|
switch (dst) {
|
||||||
|
case INT: result = (int) unboxer.invokeExact(box); break;
|
||||||
|
case LONG: result = (long) unboxer.invokeExact(box); break;
|
||||||
|
case FLOAT: result = (float) unboxer.invokeExact(box); break;
|
||||||
|
case DOUBLE: result = (double) unboxer.invokeExact(box); break;
|
||||||
|
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)";
|
||||||
|
}
|
||||||
|
assertEquals("(doCast,expectThrow,dst,src,n,box)="+Arrays.asList(doCast,expectThrow,dst,src,n,box),
|
||||||
|
expResult, result);
|
||||||
|
}
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
if (expectThrow) return;
|
||||||
|
System.out.println("Unexpected throw for (doCast,expectThrow,dst,src)="+Arrays.asList(doCast,expectThrow,dst,src));
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnboxRaw() throws Throwable {
|
||||||
|
//System.out.println("unboxRaw");
|
||||||
|
for (Wrapper w : Wrapper.values()) {
|
||||||
|
if (w == Wrapper.OBJECT) continue; // skip this; no raw form
|
||||||
|
//System.out.println(w);
|
||||||
|
for (int n = -5; n < 10; n++) {
|
||||||
|
Object box = w.wrap(n);
|
||||||
|
long expResult = w.unwrapRaw(box);
|
||||||
|
Object box2 = w.wrapRaw(expResult);
|
||||||
|
assertEquals(box, box2);
|
||||||
|
MethodHandle unboxer = ValueConversions.unboxRaw(w.primitiveType());
|
||||||
|
long result = -1;
|
||||||
|
switch (w) {
|
||||||
|
case INT: result = (int) unboxer.invokeExact(box); break;
|
||||||
|
case LONG: result = (long) unboxer.invokeExact(box); break;
|
||||||
|
case FLOAT: result = (int) unboxer.invokeExact(box); break;
|
||||||
|
case DOUBLE: result = (long) unboxer.invokeExact(box); break;
|
||||||
|
case CHAR: result = (int) unboxer.invokeExact(box); break;
|
||||||
|
case BYTE: result = (int) unboxer.invokeExact(box); break;
|
||||||
|
case SHORT: result = (int) unboxer.invokeExact(box); break;
|
||||||
|
case BOOLEAN: result = (int) unboxer.invokeExact(box); break;
|
||||||
|
case VOID: result = (int) unboxer.invokeExact(box); break;
|
||||||
|
}
|
||||||
|
assertEquals("(w,n,box)="+Arrays.asList(w,n,box),
|
||||||
|
expResult, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBox() throws Throwable {
|
||||||
|
//System.out.println("box");
|
||||||
|
for (Wrapper w : Wrapper.values()) {
|
||||||
|
if (w == Wrapper.VOID) continue; // skip this; no unboxed form
|
||||||
|
//System.out.println(w);
|
||||||
|
for (int n = -5; n < 10; n++) {
|
||||||
|
Object box = w.wrap(n);
|
||||||
|
MethodHandle boxer = ValueConversions.box(w.primitiveType());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
|
||||||
|
expResult, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBoxRaw() throws Throwable {
|
||||||
|
//System.out.println("boxRaw");
|
||||||
|
for (Wrapper w : Wrapper.values()) {
|
||||||
|
if (w == Wrapper.VOID) continue; // skip this; no unboxed form
|
||||||
|
if (w == Wrapper.OBJECT) continue; // skip this; no raw form
|
||||||
|
//System.out.println(w);
|
||||||
|
for (int n = -5; n < 10; n++) {
|
||||||
|
Object box = w.wrap(n);
|
||||||
|
long raw = w.unwrapRaw(box);
|
||||||
|
Object expResult = box;
|
||||||
|
MethodHandle boxer = ValueConversions.boxRaw(w.primitiveType());
|
||||||
|
Object result = null;
|
||||||
|
switch (w) {
|
||||||
|
case INT: result = boxer.invokeExact((int)raw); break;
|
||||||
|
case LONG: result = boxer.invokeExact(raw); break;
|
||||||
|
case FLOAT: result = boxer.invokeExact((int)raw); break;
|
||||||
|
case DOUBLE: result = boxer.invokeExact(raw); break;
|
||||||
|
case CHAR: result = boxer.invokeExact((int)raw); break;
|
||||||
|
case BYTE: result = boxer.invokeExact((int)raw); break;
|
||||||
|
case SHORT: result = boxer.invokeExact((int)raw); break;
|
||||||
|
case BOOLEAN: result = boxer.invokeExact((int)raw); break;
|
||||||
|
}
|
||||||
|
assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
|
||||||
|
expResult, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReboxRaw() throws Throwable {
|
||||||
|
//System.out.println("reboxRaw");
|
||||||
|
for (Wrapper w : Wrapper.values()) {
|
||||||
|
Wrapper pw = Wrapper.forPrimitiveType(w.rawPrimitiveType());
|
||||||
|
if (w == Wrapper.VOID) continue; // skip this; no unboxed form
|
||||||
|
if (w == Wrapper.OBJECT) continue; // skip this; no raw form
|
||||||
|
//System.out.println(w);
|
||||||
|
for (int n = -5; n < 10; n++) {
|
||||||
|
Object box = w.wrap(n);
|
||||||
|
Object raw = pw.wrap(w.unwrapRaw(box));
|
||||||
|
Object expResult = box;
|
||||||
|
MethodHandle boxer = ValueConversions.rebox(w.primitiveType());
|
||||||
|
Object result = null;
|
||||||
|
switch (w) {
|
||||||
|
case INT: result = boxer.invokeExact(raw); break;
|
||||||
|
case LONG: result = boxer.invokeExact(raw); break;
|
||||||
|
case FLOAT: result = boxer.invokeExact(raw); break;
|
||||||
|
case DOUBLE: result = boxer.invokeExact(raw); break;
|
||||||
|
case CHAR: result = boxer.invokeExact(raw); break;
|
||||||
|
case BYTE: result = boxer.invokeExact(raw); break;
|
||||||
|
case SHORT: result = boxer.invokeExact(raw); break;
|
||||||
|
case BOOLEAN: result = boxer.invokeExact(raw); break;
|
||||||
|
}
|
||||||
|
assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
|
||||||
|
expResult, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCast() throws Throwable {
|
||||||
|
//System.out.println("cast");
|
||||||
|
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());
|
||||||
|
for (Object obj : objects) {
|
||||||
|
Class<?> src = obj.getClass();
|
||||||
|
boolean canCast;
|
||||||
|
if (dst.isInterface()) {
|
||||||
|
canCast = true;
|
||||||
|
} else {
|
||||||
|
canCast = dst.isAssignableFrom(src);
|
||||||
|
assertEquals(canCast, dst.isInstance(obj));
|
||||||
|
}
|
||||||
|
//System.out.println("obj="+obj+" <: dst="+dst);
|
||||||
|
try {
|
||||||
|
Object result = caster.invokeExact(obj);
|
||||||
|
if (canCast)
|
||||||
|
assertEquals(obj, result);
|
||||||
|
else
|
||||||
|
assertEquals("cast should not have succeeded", dst, obj);
|
||||||
|
} catch (ClassCastException ex) {
|
||||||
|
if (canCast)
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIdentity() throws Throwable {
|
||||||
|
//System.out.println("identity");
|
||||||
|
MethodHandle id = ValueConversions.identity();
|
||||||
|
Object expResult = "foo";
|
||||||
|
Object result = id.invokeExact(expResult);
|
||||||
|
// compiler bug: ValueConversions.identity().invokeExact("bar");
|
||||||
|
assertEquals(expResult, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVarargsArray() throws Throwable {
|
||||||
|
//System.out.println("varargsArray");
|
||||||
|
final int MIN = START_ARITY;
|
||||||
|
final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
|
||||||
|
for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) {
|
||||||
|
MethodHandle target = ValueConversions.varargsArray(nargs);
|
||||||
|
Object[] args = new Object[nargs];
|
||||||
|
for (int i = 0; i < nargs; i++)
|
||||||
|
args[i] = "#"+i;
|
||||||
|
Object res = target.invokeWithArguments(args);
|
||||||
|
assertArrayEquals(args, (Object[])res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVarargsReferenceArray() throws Throwable {
|
||||||
|
//System.out.println("varargsReferenceArray");
|
||||||
|
testTypedVarargsArray(Object[].class);
|
||||||
|
testTypedVarargsArray(String[].class);
|
||||||
|
testTypedVarargsArray(Number[].class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVarargsPrimitiveArray() throws Throwable {
|
||||||
|
//System.out.println("varargsPrimitiveArray");
|
||||||
|
testTypedVarargsArray(int[].class);
|
||||||
|
testTypedVarargsArray(long[].class);
|
||||||
|
testTypedVarargsArray(byte[].class);
|
||||||
|
testTypedVarargsArray(boolean[].class);
|
||||||
|
testTypedVarargsArray(short[].class);
|
||||||
|
testTypedVarargsArray(char[].class);
|
||||||
|
testTypedVarargsArray(float[].class);
|
||||||
|
testTypedVarargsArray(double[].class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int nextArgCount(int nargs, int density, int MAX) {
|
||||||
|
if (EXHAUSTIVE) return nargs + 1;
|
||||||
|
if (nargs >= MAX) return Integer.MAX_VALUE;
|
||||||
|
int BOT = 20, TOP = MAX-5;
|
||||||
|
if (density < 10) { BOT = 10; MAX = TOP-2; }
|
||||||
|
if (nargs <= BOT || nargs >= TOP) {
|
||||||
|
++nargs;
|
||||||
|
} else {
|
||||||
|
int bump = Math.max(1, 100 / density);
|
||||||
|
nargs += bump;
|
||||||
|
if (nargs > TOP) nargs = TOP;
|
||||||
|
}
|
||||||
|
return nargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testTypedVarargsArray(Class<?> arrayType) throws Throwable {
|
||||||
|
System.out.println(arrayType.getSimpleName());
|
||||||
|
Class<?> elemType = arrayType.getComponentType();
|
||||||
|
int MIN = START_ARITY;
|
||||||
|
int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
|
||||||
|
int density = 3;
|
||||||
|
if (elemType == int.class || elemType == long.class) density = 7;
|
||||||
|
if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; }
|
||||||
|
for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) {
|
||||||
|
Object[] args = makeTestArray(elemType, nargs);
|
||||||
|
MethodHandle varargsArray = ValueConversions.varargsArray(arrayType, nargs);
|
||||||
|
MethodType vaType = varargsArray.type();
|
||||||
|
assertEquals(arrayType, vaType.returnType());
|
||||||
|
if (nargs != 0) {
|
||||||
|
assertEquals(elemType, vaType.parameterType(0));
|
||||||
|
assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1));
|
||||||
|
}
|
||||||
|
assertEquals(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)),
|
||||||
|
vaType);
|
||||||
|
Object res = varargsArray.invokeWithArguments(args);
|
||||||
|
String resString = toArrayString(res);
|
||||||
|
assertEquals(Arrays.toString(args), resString);
|
||||||
|
|
||||||
|
MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs);
|
||||||
|
MethodType stype = spreader.type();
|
||||||
|
assert(stype == MethodType.methodType(arrayType, arrayType));
|
||||||
|
if (nargs <= 5) {
|
||||||
|
// invoke target as a spreader also:
|
||||||
|
Object res2 = spreader.invokeWithArguments((Object)res);
|
||||||
|
String res2String = toArrayString(res2);
|
||||||
|
assertEquals(Arrays.toString(args), res2String);
|
||||||
|
// invoke the spreader on a generic Object[] array; check for error
|
||||||
|
try {
|
||||||
|
Object res3 = spreader.invokeWithArguments((Object)args);
|
||||||
|
String res3String = toArrayString(res3);
|
||||||
|
assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class));
|
||||||
|
assertEquals(Arrays.toString(args), res3String);
|
||||||
|
} catch (ClassCastException ex) {
|
||||||
|
assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nargs == 0) {
|
||||||
|
// invoke spreader on null arglist
|
||||||
|
Object res3 = spreader.invokeWithArguments((Object)null);
|
||||||
|
String res3String = toArrayString(res3);
|
||||||
|
assertEquals(Arrays.toString(args), res3String);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object[] makeTestArray(Class<?> elemType, int len) {
|
||||||
|
Wrapper elem = null;
|
||||||
|
if (elemType.isPrimitive())
|
||||||
|
elem = Wrapper.forPrimitiveType(elemType);
|
||||||
|
else if (Wrapper.isWrapperType(elemType))
|
||||||
|
elem = Wrapper.forWrapperType(elemType);
|
||||||
|
Object[] args = new Object[len];
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
Object arg = i * 100;
|
||||||
|
if (elem == null) {
|
||||||
|
if (elemType == String.class)
|
||||||
|
arg = "#"+arg;
|
||||||
|
arg = elemType.cast(arg); // just to make sure
|
||||||
|
} else {
|
||||||
|
switch (elem) {
|
||||||
|
case BOOLEAN: arg = (i % 3 == 0); break;
|
||||||
|
case CHAR: arg = 'a' + i; break;
|
||||||
|
case LONG: arg = (long)i * 1000_000_000; break;
|
||||||
|
case FLOAT: arg = (float)i / 100; break;
|
||||||
|
case DOUBLE: arg = (double)i / 1000_000; break;
|
||||||
|
}
|
||||||
|
arg = elem.cast(arg, elemType);
|
||||||
|
}
|
||||||
|
args[i] = arg;
|
||||||
|
}
|
||||||
|
//System.out.println(elemType.getName()+Arrays.toString(args));
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toArrayString(Object a) {
|
||||||
|
if (a == null) return "null";
|
||||||
|
Class<?> elemType = a.getClass().getComponentType();
|
||||||
|
if (elemType == null) return a.toString();
|
||||||
|
if (elemType.isPrimitive()) {
|
||||||
|
switch (Wrapper.forPrimitiveType(elemType)) {
|
||||||
|
case INT: return Arrays.toString((int[])a);
|
||||||
|
case BYTE: return Arrays.toString((byte[])a);
|
||||||
|
case BOOLEAN: return Arrays.toString((boolean[])a);
|
||||||
|
case SHORT: return Arrays.toString((short[])a);
|
||||||
|
case CHAR: return Arrays.toString((char[])a);
|
||||||
|
case FLOAT: return Arrays.toString((float[])a);
|
||||||
|
case LONG: return Arrays.toString((long[])a);
|
||||||
|
case DOUBLE: return Arrays.toString((double[])a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Arrays.toString((Object[])a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVarargsList() throws Throwable {
|
||||||
|
//System.out.println("varargsList");
|
||||||
|
final int MIN = START_ARITY;
|
||||||
|
final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
|
||||||
|
for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 7, MAX)) {
|
||||||
|
MethodHandle target = ValueConversions.varargsList(nargs);
|
||||||
|
Object[] args = new Object[nargs];
|
||||||
|
for (int i = 0; i < nargs; i++)
|
||||||
|
args[i] = "#"+i;
|
||||||
|
Object res = target.invokeWithArguments(args);
|
||||||
|
assertEquals(Arrays.asList(args), res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user