# HG changeset patch
# User trims
# Date 1305667799 25200
# Node ID 9fc3d991dc634ac5d48e1f954e0296734822c30f
# Parent 04f88d98efe35a1e0362788519ab6ac3a0d9a79f# Parent 5ebbe5ab084f44e76ce0edd7248852da0dcd8114
Merge
diff -r 04f88d98efe3 -r 9fc3d991dc63 jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java
--- a/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java Thu May 12 17:17:36 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java Tue May 17 14:29:59 2011 -0700
@@ -27,6 +27,7 @@
import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
+import sun.invoke.util.ValueConversions;
import java.util.Arrays;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.*;
@@ -55,29 +56,35 @@
this(target, newType, conv, null);
}
+ int getConversion() { return conversion; }
+
// TO DO: When adapting another MH with a null conversion, clone
// the target and change its type, instead of adding another layer.
/** Can a JVM-level adapter directly implement the proposed
* 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
int len = newType.parameterCount();
if (len != oldType.parameterCount())
return false;
- // Check return type. (Not much can be done with it.)
+ // Check return type.
Class> exp = newType.returnType();
Class> ret = oldType.returnType();
- if (!VerifyType.isNullConversion(ret, exp))
- return false;
+ if (!VerifyType.isNullConversion(ret, exp)) {
+ if (!convOpSupported(OP_COLLECT_ARGS))
+ return false;
+ if (!canConvertArgument(ret, exp, level))
+ return false;
+ }
// Check args pairwise.
for (int i = 0; i < len; i++) {
Class> src = newType.parameterType(i); // source type
Class> dst = oldType.parameterType(i); // destination type
- if (!canConvertArgument(src, dst))
+ if (!canConvertArgument(src, dst, level))
return false;
}
@@ -87,11 +94,14 @@
/** Can a JVM-level adapter directly implement the proposed
* 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,
// so we don't need to repeat so much decision making.
if (VerifyType.isNullConversion(src, dst)) {
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()) {
if (dst.isPrimitive())
return canPrimCast(src, dst);
@@ -99,7 +109,7 @@
return canBoxArgument(src, dst);
} else {
if (dst.isPrimitive())
- return canUnboxArgument(src, dst);
+ return canUnboxArgument(src, dst, level);
else
return true; // any two refs can be interconverted
}
@@ -109,21 +119,20 @@
* Create a JVM-level adapter method handle to conform the given method
* handle to the similar newType, using only pairwise argument conversions.
* For each argument, convert incoming argument to the exact type needed.
- * Only null conversions are allowed on the return value (until
- * the JVM supports ricochet adapters).
- * The argument conversions allowed are casting, unboxing,
+ * The argument conversions allowed are casting, boxing and unboxing,
* integral widening or narrowing, and floating point widening or narrowing.
* @param newType required call type
* @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,
* or the original target if the types are already identical
* 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();
if (newType == oldType) return target;
- if (!canPairwiseConvert(newType, oldType))
+ if (!canPairwiseConvert(newType, oldType, level))
return null;
// (after this point, it is an assertion error to fail to convert)
@@ -138,9 +147,14 @@
break;
}
}
+
+ Class> needReturn = newType.returnType();
+ Class> haveReturn = oldType.returnType();
+ boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
+
// Now build a chain of one or more adapters.
- MethodHandle adapter = target;
- MethodType midType = oldType.changeReturnType(newType.returnType());
+ MethodHandle adapter = target, adapter2;
+ MethodType midType = oldType;
for (int i = 0; i <= lastConv; i++) {
Class> src = newType.parameterType(i); // source type
Class> dst = midType.parameterType(i); // destination type
@@ -149,22 +163,23 @@
continue;
}
// Work the current type backward toward the desired caller type:
- if (i != lastConv) {
- midType = midType.changeParameterType(i, src);
- } else {
+ midType = midType.changeParameterType(i, src);
+ if (i == lastConv) {
// When doing the last (or only) real conversion,
// force all remaining null conversions to happen also.
- assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src)));
- midType = newType;
+ MethodType lastMidType = newType;
+ if (retConv) lastMidType = lastMidType.changeReturnType(haveReturn);
+ assert(VerifyType.isNullConversion(lastMidType, midType));
+ midType = lastMidType;
}
// Tricky case analysis follows.
// It parallels canConvertArgument() above.
if (src.isPrimitive()) {
if (dst.isPrimitive()) {
- adapter = makePrimCast(midType, adapter, i, dst);
+ adapter2 = makePrimCast(midType, adapter, i, dst);
} else {
- adapter = makeBoxArgument(midType, adapter, i, dst);
+ adapter2 = makeBoxArgument(midType, adapter, i, src);
}
} else {
if (dst.isPrimitive()) {
@@ -174,29 +189,53 @@
// conversions supported by reflect.Method.invoke.
// Those conversions require a big nest of if/then/else logic,
// which we prefer to make a user responsibility.
- adapter = makeUnboxArgument(midType, adapter, i, dst);
+ adapter2 = makeUnboxArgument(midType, adapter, i, dst, level);
} else {
// Simple reference conversion.
// Note: Do not check for a class hierarchy relation
// between src and dst. In all cases a 'null' argument
// will pass the cast conversion.
- adapter = makeCheckCast(midType, adapter, i, dst);
+ adapter2 = makeCheckCast(midType, adapter, i, dst);
}
}
- assert(adapter != null);
- assert(adapter.type() == midType);
+ assert(adapter2 != null) : Arrays.asList(src, dst, midType, adapter, i, target, newType);
+ assert(adapter2.type() == midType);
+ adapter = adapter2;
+ }
+ if (retConv) {
+ adapter2 = makeReturnConversion(adapter, haveReturn, needReturn);
+ assert(adapter2 != null);
+ adapter = adapter2;
}
if (adapter.type() != newType) {
// Only trivial conversions remain.
- adapter = makeRetypeOnly(newType, adapter);
- assert(adapter != null);
+ adapter2 = makeRetypeOnly(newType, adapter);
+ assert(adapter2 != null);
+ adapter = adapter2;
// Actually, that's because there were no non-trivial ones:
- assert(lastConv == -1);
+ assert(lastConv == -1 || retConv);
}
assert(adapter.type() == newType);
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
* of the given method.
@@ -224,7 +263,7 @@
if (argumentMap.length != oldType.parameterCount())
throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
if (nullPermutation) {
- MethodHandle res = makePairwiseConvert(newType, target);
+ MethodHandle res = makePairwiseConvert(newType, target, 0);
// well, that was easy
if (res == null)
throw newIllegalArgumentException("cannot convert pairwise: "+newType);
@@ -310,11 +349,25 @@
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. */
private static long makeConv(int convOp, int argnum, int src, int dest) {
- assert(src == (src & 0xF));
- assert(dest == (dest & 0xF));
- assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF);
+ assert(src == (src & CONV_TYPE_MASK));
+ assert(dest == (dest & CONV_TYPE_MASK));
+ assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF || convOp == OP_COLLECT_ARGS);
int stackMove = type2size(dest) - type2size(src);
return ((long) argnum << 32 |
(long) convOp << CONV_OP_SHIFT |
@@ -323,11 +376,10 @@
insertStackMove(stackMove)
);
}
- private static long makeConv(int convOp, int argnum, int stackMove) {
- assert(convOp >= OP_DUP_ARGS && convOp <= OP_SPREAD_ARGS);
+ private static long makeDupConv(int convOp, int argnum, int stackMove) {
+ // simple argument motion, requiring one slot to specify
+ assert(convOp == OP_DUP_ARGS || convOp == OP_DROP_ARGS);
byte src = 0, dest = 0;
- if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS)
- src = dest = T_OBJECT;
return ((long) argnum << 32 |
(long) convOp << CONV_OP_SHIFT |
(int) src << CONV_SRC_TYPE_SHIFT |
@@ -336,7 +388,8 @@
);
}
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 |
(long) convOp << CONV_OP_SHIFT |
(int) type << CONV_SRC_TYPE_SHIFT |
@@ -344,6 +397,18 @@
(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) {
assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW);
return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT); // stackMove, src, dst all zero
@@ -570,14 +635,10 @@
static boolean canPrimCast(Class> src, Class> dst) {
if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
return false;
- } else if (Wrapper.forPrimitiveType(dst).isFloating()) {
- // both must be floating types
- return Wrapper.forPrimitiveType(src).isFloating();
} else {
- // both are integral, and all combinations work fine
- assert(Wrapper.forPrimitiveType(src).isIntegral() &&
- Wrapper.forPrimitiveType(dst).isIntegral());
- return true;
+ boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
+ boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
+ return !(sflt | dflt); // no float support at present
}
}
@@ -589,6 +650,29 @@
*/
static MethodHandle makePrimCast(MethodType newType, MethodHandle target,
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();
if (!canPrimCast(newType, oldType, arg, convType))
return null;
@@ -602,7 +686,7 @@
* The convType is the unboxed type; it can be either a primitive or wrapper.
*/
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;
Class> src = newType.parameterType(arg);
Class> dst = targetType.parameterType(arg);
@@ -616,21 +700,31 @@
return (diff == arg+1); // arg is sole non-trivial diff
}
/** Can an primitive unboxing adapter validly convert src to dst? */
- static boolean canUnboxArgument(Class> src, Class> dst) {
- return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive());
+ static boolean canUnboxArgument(Class> src, Class> dst, int level) {
+ 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.
* Return null if this cannot be done.
*/
static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target,
- int arg, Class> convType) {
+ int arg, Class> convType, int level) {
MethodType oldType = target.type();
Class> src = newType.parameterType(arg);
Class> dst = oldType.parameterType(arg);
Class> boxType = Wrapper.asWrapperType(convType);
Class> primType = Wrapper.asPrimitiveType(convType);
- if (!canUnboxArgument(newType, oldType, arg, convType))
+ if (!canUnboxArgument(newType, oldType, arg, convType, level))
return null;
MethodType castDone = newType;
if (!VerifyType.isNullConversion(src, boxType))
@@ -642,19 +736,46 @@
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? */
static boolean canBoxArgument(Class> src, Class> dst) {
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.
*/
static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target,
int arg, Class> convType) {
- // this is difficult to do in the JVM because it must GC
- return null;
+ MethodType oldType = target.type();
+ 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? */
@@ -699,7 +820,7 @@
int slotCount = keep1InSlot - dropSlot;
assert(slotCount >= dropArgCount);
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);
}
@@ -739,7 +860,7 @@
int keep1InSlot = newType.parameterSlotDepth(dupArgPos);
int slotCount = keep1InSlot - dupSlot;
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);
}
@@ -900,7 +1021,7 @@
for (int i = 0; i < spreadArgCount; i++) {
Class> src = VerifyType.spreadArgElementType(spreadArgType, i);
Class> dst = targetType.parameterType(spreadArgPos + i);
- if (src == null || !VerifyType.isNullConversion(src, dst))
+ if (src == null || !canConvertArgument(src, dst, 1))
return false;
}
return true;
@@ -910,24 +1031,100 @@
/** Factory method: Spread selected argument. */
static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target,
Class> spreadArgType, int spreadArgPos, int spreadArgCount) {
+ // FIXME: Get rid of newType; derive new arguments from structure of spreadArgType
MethodType targetType = target.type();
if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
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... ]
// out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ]
int keep2OutPos = spreadArgPos + spreadArgCount;
- int spreadSlot = targetType.parameterSlotDepth(keep2OutPos);
- int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos);
- int slotCount = keep1OutSlot - spreadSlot;
- assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+1));
+ int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos); // leading edge of |spread...|
+ int spreadSlot = targetType.parameterSlotDepth(keep2OutPos); // trailing edge of |spread...|
+ assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+arrayArgSize));
+ int slotCount = keep1OutSlot - spreadSlot; // slots in |spread...|
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);
assert(res.type().parameterType(spreadArgPos) == spreadArgType);
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
public String toString() {
diff -r 04f88d98efe3 -r 9fc3d991dc63 jdk/src/share/classes/java/lang/invoke/CallSite.java
--- a/jdk/src/share/classes/java/lang/invoke/CallSite.java Thu May 12 17:17:36 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/CallSite.java Tue May 17 14:29:59 2011 -0700
@@ -273,9 +273,9 @@
Object binding;
info = maybeReBox(info);
if (info == null) {
- binding = bootstrapMethod.invokeGeneric(caller, name, type);
+ binding = bootstrapMethod.invoke(caller, name, type);
} else if (!info.getClass().isArray()) {
- binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
+ binding = bootstrapMethod.invoke(caller, name, type, info);
} else {
Object[] argv = (Object[]) info;
maybeReBoxElements(argv);
@@ -283,10 +283,10 @@
throw new BootstrapMethodError("too many bootstrap method arguments");
MethodType bsmType = bootstrapMethod.type();
if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
- binding = bootstrapMethod.invokeGeneric(caller, name, type, argv);
+ binding = bootstrapMethod.invoke(caller, name, type, argv);
else
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);
if (binding instanceof CallSite) {
diff -r 04f88d98efe3 -r 9fc3d991dc63 jdk/src/share/classes/java/lang/invoke/FilterGeneric.java
--- a/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java Thu May 12 17:17:36 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java Tue May 17 14:29:59 2011 -0700
@@ -61,6 +61,10 @@
return ad;
}
+ static {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
+ }
+
Adapter makeInstance(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
Adapter ad = getAdapter(kind, pos);
return ad.makeInstance(ad.prototypeEntryPoint(), filter, target);
diff -r 04f88d98efe3 -r 9fc3d991dc63 jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java
--- a/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java Thu May 12 17:17:36 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java Tue May 17 14:29:59 2011 -0700
@@ -67,6 +67,10 @@
this.target = target;
}
+ static {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
+ }
+
public static MethodHandle make(MethodHandle filter, MethodHandle target) {
if (filter == null) return target;
if (target == null) return filter;
diff -r 04f88d98efe3 -r 9fc3d991dc63 jdk/src/share/classes/java/lang/invoke/FromGeneric.java
--- a/jdk/src/share/classes/java/lang/invoke/FromGeneric.java Thu May 12 17:17:36 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FromGeneric.java Tue May 17 14:29:59 2011 -0700
@@ -98,6 +98,10 @@
this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
}
+ static {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
+ }
+
/**
* The typed target will be called according to targetType.
* The adapter code will in fact see the raw result from internalType,
@@ -112,10 +116,10 @@
assert(iret == Object.class);
return ValueConversions.identity();
} else if (wrap.primitiveType() == iret) {
- return ValueConversions.box(wrap, false);
+ return ValueConversions.box(wrap);
} else {
assert(tret == double.class ? iret == long.class : iret == int.class);
- return ValueConversions.boxRaw(wrap, false);
+ return ValueConversions.boxRaw(wrap);
}
}
@@ -135,7 +139,7 @@
MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
MethodHandle fixArgs = MethodHandleImpl.convertArguments(
invoker, Invokers.invokerType(fixArgsType),
- invoker.type(), null);
+ invoker.type(), 0);
if (fixArgs == null)
throw new InternalError("bad fixArgs");
// reinterpret the calling sequence as raw:
@@ -160,7 +164,6 @@
/** Build an adapter of the given generic type, which invokes typedTarget
* on the incoming arguments, after unboxing as necessary.
* The return value is boxed if necessary.
- * @param genericType the required type of the result
* @param typedTarget the target
* @return an adapter method handle
*/
@@ -231,7 +234,7 @@
}
static Adapter buildAdapterFromBytecodes(MethodType internalType) {
- throw new UnsupportedOperationException("NYI");
+ throw new UnsupportedOperationException("NYI "+internalType);
}
/**
diff -r 04f88d98efe3 -r 9fc3d991dc63 jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java
--- a/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java Thu May 12 17:17:36 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java Tue May 17 14:29:59 2011 -0700
@@ -29,12 +29,12 @@
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.
* @author jrose
*/
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;
// an invoker of type (MT, MH; A...) -> R
private final MethodHandle initialInvoker;
@@ -56,7 +56,7 @@
}
/** 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);
return gen.initialInvoker;
}
diff -r 04f88d98efe3 -r 9fc3d991dc63 jdk/src/share/classes/java/lang/invoke/Invokers.java
--- a/jdk/src/share/classes/java/lang/invoke/Invokers.java Thu May 12 17:17:36 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/Invokers.java Tue May 17 14:29:59 2011 -0700
@@ -43,10 +43,10 @@
private /*lazy*/ MethodHandle erasedInvoker;
/*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric
- // generic (untyped) invoker for the outgoing call
- private /*lazy*/ MethodHandle genericInvoker;
+ // general invoker for the outgoing call
+ 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;
// invoker for an unbound callsite
@@ -77,13 +77,13 @@
return invoker;
}
- /*non-public*/ MethodHandle genericInvoker() {
+ /*non-public*/ MethodHandle generalInvoker() {
MethodHandle invoker1 = exactInvoker();
- MethodHandle invoker = genericInvoker;
+ MethodHandle invoker = generalInvoker;
if (invoker != null) return invoker;
- MethodType genericType = targetType.generic();
- invoker = MethodHandles.convertArguments(invoker1, invokerType(genericType));
- genericInvoker = invoker;
+ MethodType generalType = targetType.generic();
+ invoker = invoker1.asType(invokerType(generalType));
+ generalInvoker = invoker;
return invoker;
}
@@ -93,9 +93,9 @@
if (invoker != null) return invoker;
MethodType erasedType = targetType.erase();
if (erasedType == targetType.generic())
- invoker = genericInvoker();
+ invoker = generalInvoker();
else
- invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType));
+ invoker = invoker1.asType(invokerType(erasedType));
erasedInvoker = invoker;
return invoker;
}
@@ -103,7 +103,7 @@
/*non-public*/ MethodHandle spreadInvoker(int objectArgCount) {
MethodHandle vaInvoker = spreadInvokers[objectArgCount];
if (vaInvoker != null) return vaInvoker;
- MethodHandle gInvoker = genericInvoker();
+ MethodHandle gInvoker = generalInvoker();
vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount);
spreadInvokers[objectArgCount] = vaInvoker;
return vaInvoker;
diff -r 04f88d98efe3 -r 9fc3d991dc63 jdk/src/share/classes/java/lang/invoke/MemberName.java
--- a/jdk/src/share/classes/java/lang/invoke/MemberName.java Thu May 12 17:17:36 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MemberName.java Tue May 17 14:29:59 2011 -0700
@@ -525,7 +525,7 @@
/** A factory type for resolving member names with the help of the VM.
* TBD: Define access-safe public constructors for this factory.
*/
- public static class Factory {
+ /*non-public*/ static class Factory {
private Factory() { } // singleton pattern
static Factory INSTANCE = new Factory();
diff -r 04f88d98efe3 -r 9fc3d991dc63 jdk/src/share/classes/java/lang/invoke/MethodHandle.java
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Thu May 12 17:17:36 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Tue May 17 14:29:59 2011 -0700
@@ -26,6 +26,7 @@
package java.lang.invoke;
+import sun.invoke.util.ValueConversions;
import static java.lang.invoke.MethodHandleStatics.*;
/**
@@ -53,12 +54,12 @@
* and the kinds of transformations that apply to it.
*
* 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
* underlying method, constructor, field, or other operation,
* as modified by transformations of arguments and return values.
* 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.
*
* Method handles are immutable and have no visible state.
* Of course, they can be bound to underlying methods or data which exhibit state.
@@ -76,7 +77,7 @@
* may change from time to time or across implementations from different vendors.
*
*
Method handle compilation
- * 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.
* From the viewpoint of source code, these methods can take any arguments
* and their result can be cast to any return type.
@@ -86,7 +87,7 @@
* which connects this freedom of invocation directly to the JVM execution stack.
*
* 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,
* and may not perform method invocation conversions on the arguments.
* Instead, it must push them on the stack according to their own unconverted types.
@@ -109,7 +110,7 @@
* The first time a {@code invokevirtual} instruction is executed
* it is linked, by symbolically resolving the names in the instruction
* 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
* correct syntax and names it contains are resolved.
* Thus, an {@code invokevirtual} instruction which invokes
@@ -127,18 +128,18 @@
* In the case of {@code invokeExact}, the type descriptor of the invocation
* (after resolving symbolic type names) must exactly match the method type
* 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.
- * Thus, {@code invokeGeneric} is more permissive than {@code invokeExact}.
+ * Thus, plain {@code invoke} is more permissive than {@code invokeExact}.
*
* After type matching, a call to {@code invokeExact} directly
* and immediately invoke the method handle's underlying method
* (or other behavior, as the case may be).
*
- * 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
* 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,
* as if by a call to {@link #asType asType},
* to obtain an exactly invokable method handle {@code M2}.
@@ -152,7 +153,7 @@
* In typical programs, method handle type matching will usually succeed.
* But if a match fails, the JVM will throw a {@link WrongMethodTypeException},
* 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}).
*
* Thus, a method type mismatch which might show up as a linkage error
* in a statically typed program can show up as
@@ -249,8 +250,8 @@
mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
assert(mh.isVarargsCollector());
-x = mh.invokeGeneric("one", "two");
-// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+x = mh.invoke("one", "two");
+// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
assert(x.equals(java.util.Arrays.asList("one","two")));
// mt is (Object,Object,Object)Object
mt = MethodType.genericMethodType(3);
@@ -269,12 +270,12 @@
mh.invokeExact(System.out, "Hello, world.");
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
*
- * 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
* the type descriptor indicated in the following comment.
*
*
Exceptions
- * 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},
* 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
@@ -288,7 +289,7 @@
*
*
Signature polymorphism
* The unusual compilation and linkage behavior of
- * {@code invokeExact} and {@code invokeGeneric}
+ * {@code invokeExact} and plain {@code invoke}
* is referenced by the term signature polymorphism.
* A signature polymorphic method is one which can operate with
* any of a wide range of call signatures and return types.
@@ -322,7 +323,7 @@
* The following methods (and no others) are signature polymorphic:
*
* A signature polymorphic method will be declared with the following properties:
@@ -374,24 +375,34 @@
*
* As a special case,
* when the Core Reflection API is used to view the signature polymorphic
- * methods {@code invokeExact} or {@code invokeGeneric} in this class,
- * they appear as single, non-polymorphic native methods.
- * Calls to these native methods do not result in method handle invocations.
+ * methods {@code invokeExact} or plain {@code invoke} in this class,
+ * they appear as ordinary non-polymorphic methods.
+ * 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.
+ *
+ * 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}.
+ *
* Since {@code invokevirtual} instructions can natively
* invoke method handles under any type descriptor, this reflective view conflicts
- * with the normal presentation via bytecodes.
- * Thus, these two native methods, as viewed by
- * {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod},
- * are placeholders only.
- * If invoked via {@link java.lang.reflect.Method#invoke Method.invoke},
- * they will throw {@code UnsupportedOperationException}.
+ * with the normal presentation of these methods via bytecodes.
+ * Thus, these two native methods, when reflectively viewed by
+ * {@code Class.getDeclaredMethod}, may be regarded as placeholders only.
*
* In order to obtain an invoker method for a particular type descriptor,
* 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}
* 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 .
*
*
Interoperation between method handles and Java generics
@@ -523,7 +534,7 @@
* adaptations directly on the caller's arguments,
* and call the target method handle according to its own exact type.
*
- * 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.
* In particular, the caller must specify the same argument arity
* as the callee's type,
@@ -539,11 +550,18 @@
* @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
*/
+ public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
+
+ /**
+ * Temporary alias 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;
/**
* 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
* of the argument array.
*
@@ -553,7 +571,7 @@
*
*
Determine the length of the argument array as {@code N}.
* For a null reference, {@code N=0}.
- *
Determine the generic type {@code TN} of {@code N} arguments as
+ *
Determine the general type {@code TN} of {@code N} arguments as
* as {@code TN=MethodType.genericMethodType(N)}.
*
Force the original target method handle {@code MH0} to the
* required type, as {@code MH1 = MH0.asType(TN)}.
- * 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.
* It can therefore be used as a bridge between native or reflective code and method handles.
*
@@ -595,11 +613,11 @@
int argc = arguments == null ? 0 : arguments.length;
MethodType type = type();
if (type.parameterCount() != argc) {
- // simulate invokeGeneric
+ // simulate invoke
return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments);
}
if (argc <= 10) {
- MethodHandle invoker = type.invokers().genericInvoker();
+ MethodHandle invoker = type.invokers().generalInvoker();
switch (argc) {
case 0: return invoker.invokeExact(this);
case 1: return invoker.invokeExact(this,
@@ -644,7 +662,7 @@
/**
* 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
* of the argument array.
*
@@ -672,9 +690,9 @@
* If the original type and new type are equal, returns {@code this}.
*
* 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
- * 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
* to match up the caller's and callee's types.
*
@@ -689,6 +707,9 @@
* @see MethodHandles#convertArguments
*/
public MethodHandle asType(MethodType newType) {
+ if (!type.isConvertibleTo(newType)) {
+ throw new WrongMethodTypeException("cannot convert "+type+" to "+newType);
+ }
return MethodHandles.convertArguments(this, newType);
}
@@ -731,13 +752,9 @@
public MethodHandle asSpreader(Class> arrayType, int arrayLength) {
Class> arrayElement = arrayType.getComponentType();
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
- MethodType oldType = type();
- int nargs = oldType.parameterCount();
+ int nargs = type().parameterCount();
if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length");
- int keepPosArgs = nargs - arrayLength;
- MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs);
- newType = newType.insertParameterTypes(keepPosArgs, arrayType);
- return MethodHandles.spreadArguments(this, newType);
+ return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
}
/**
@@ -780,15 +797,18 @@
* @see #asVarargsCollector
*/
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();
- if (arrayElement == null) throw newIllegalArgumentException("not an array type");
- MethodType oldType = type();
- int nargs = oldType.parameterCount();
- if (nargs == 0) throw newIllegalArgumentException("no trailing argument");
- MethodType newType = oldType.dropParameterTypes(nargs-1, nargs);
- newType = newType.insertParameterTypes(nargs-1,
- java.util.Collections.>nCopies(arrayLength, arrayElement));
- return MethodHandles.collectArguments(this, newType);
+ if (arrayElement == null)
+ throw newIllegalArgumentException("not an array type", arrayType);
+ int nargs = type().parameterCount();
+ if (nargs == 0 || !type().parameterType(nargs-1).isAssignableFrom(arrayType))
+ throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
}
/**
@@ -798,7 +818,7 @@
*
* The type and behavior of the adapter will be the same as
* 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 parameter.
* Also, the last parameter type of the adapter will be
@@ -812,17 +832,17 @@
* since it accepts a whole array of indeterminate length,
* rather than a fixed number of arguments.)
*
- * 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
* {@code invokeExact}.
- * (This is the normal behavior for {@code invokeGeneric} when types match.)
+ * (This is the normal behavior for {@code invoke} when types match.)
*
* Otherwise, if the caller and adapter arity are the same, and the
* trailing parameter type of the caller is a reference type identical to
* or assignable to the trailing parameter type of the adapter,
* the arguments and return values are converted pairwise,
* 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.)
*
* Otherwise, the arities differ, or the adapter's trailing parameter
* type is not assignable from the corresponding caller type.
@@ -838,7 +858,7 @@
* where {@code N} is the arity of the target.
* Also, there must exist conversions from the incoming 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}
* may be thrown.
*
@@ -856,7 +876,7 @@
*
* The behavior of {@link #asType asType} is also specialized for
* 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}.
* Therefore, a variable arity adapter responds
* to an {@code asType} request by building a fixed arity collector,
@@ -893,12 +913,12 @@
MethodHandle asList = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
.asVarargsCollector(Object[].class);
-assertEquals("[]", asList.invokeGeneric().toString());
-assertEquals("[1]", asList.invokeGeneric(1).toString());
-assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
+assertEquals("[]", asList.invoke().toString());
+assertEquals("[1]", asList.invoke(1).toString());
+assertEquals("[two, too]", asList.invoke("two", "too").toString());
Object[] argv = { "three", "thee", "tee" };
-assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
-List ls = (List) asList.invokeGeneric((Object)argv);
+assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
+List ls = (List) asList.invoke((Object)argv);
assertEquals(1, ls.size());
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
*
@@ -926,9 +946,9 @@
.asVarargsCollector(Object[].class);
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
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;
-try { mh.invokeGeneric(1,2,3); }
+try { mh.invoke(1,2,3); }
catch (WrongMethodTypeException ex) { failed = true; }
assert(failed);
*
@@ -960,7 +980,7 @@
*
an {@code ldc} instruction of a {@code CONSTANT_MethodHandle}
* which resolves to a variable arity Java method or constructor
*
- * @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
*/
public boolean isVarargsCollector() {
diff -r 04f88d98efe3 -r 9fc3d991dc63 jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Thu May 12 17:17:36 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Tue May 17 14:29:59 2011 -0700
@@ -121,11 +121,11 @@
if (nargs < INVOKES.length) {
MethodHandle invoke = INVOKES[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;
MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon);
assert(galloc.type() == newType.generic());
- return convertArguments(galloc, newType, galloc.type(), null);
+ return convertArguments(galloc, newType, galloc.type(), 0);
} else {
MethodHandle invoke = VARARGS_INVOKE;
MethodType conType = CON_TYPES[nargs];
@@ -256,8 +256,8 @@
FieldAccessor.ahandle(arrayClass, true)
};
if (mhs[0].type().parameterType(0) == Class.class) {
- mhs[0] = MethodHandles.insertArguments(mhs[0], 0, elemClass);
- mhs[1] = MethodHandles.insertArguments(mhs[1], 0, elemClass);
+ mhs[0] = mhs[0].bindTo(elemClass);
+ mhs[1] = mhs[1].bindTo(elemClass);
}
synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier
FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
@@ -372,7 +372,7 @@
if (evclass != vclass || (!isStatic && ecclass != cclass)) {
MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
- mh = MethodHandles.convertArguments(mh, strongType);
+ mh = convertArguments(mh, strongType, 0);
}
return mh;
}
@@ -439,8 +439,8 @@
}
if (caclass != null) {
MethodType strongType = FieldAccessor.atype(caclass, isSetter);
- mh = MethodHandles.insertArguments(mh, 0, caclass);
- mh = MethodHandles.convertArguments(mh, strongType);
+ mh = mh.bindTo(caclass);
+ mh = convertArguments(mh, strongType, 0);
}
return mh;
}
@@ -465,7 +465,7 @@
dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) {
MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0);
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 @@
return new BoundMethodHandle(target, receiver, argnum);
}
- static MethodHandle convertArguments(MethodHandle target,
+ static MethodHandle permuteArguments(MethodHandle target,
MethodType newType,
MethodType oldType,
int[] permutationOrNull) {
assert(oldType.parameterCount() == target.type().parameterCount());
- if (permutationOrNull != null) {
- int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
- if (permutationOrNull.length != outargs)
- throw newIllegalArgumentException("wrong number of arguments in permutation");
- // Make the individual outgoing argument types match up first.
- Class>[] callTypeArgs = new Class>[outargs];
- for (int i = 0; i < outargs; i++)
- callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
- MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
- target = convertArguments(target, callType, oldType, null);
- assert(target != null);
- oldType = target.type();
- List goal = new ArrayList(); // i*TOKEN
- List state = new ArrayList(); // i*TOKEN
- List drops = new ArrayList(); // not tokens
- List dups = new ArrayList(); // not tokens
- final int TOKEN = 10; // to mark items which are symbolic only
- // state represents the argument values coming into target
- for (int i = 0; i < outargs; i++) {
- state.add(permutationOrNull[i] * TOKEN);
+ int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
+ if (permutationOrNull.length != outargs)
+ throw newIllegalArgumentException("wrong number of arguments in permutation");
+ // Make the individual outgoing argument types match up first.
+ Class>[] callTypeArgs = new Class>[outargs];
+ for (int i = 0; i < outargs; i++)
+ callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
+ MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
+ target = convertArguments(target, callType, oldType, 0);
+ assert(target != null);
+ oldType = target.type();
+ List goal = new ArrayList(); // i*TOKEN
+ List state = new ArrayList(); // i*TOKEN
+ List drops = new ArrayList(); // not tokens
+ List dups = new ArrayList(); // not tokens
+ final int TOKEN = 10; // to mark items which are symbolic only
+ // state represents the argument values coming into target
+ for (int i = 0; i < outargs; i++) {
+ state.add(permutationOrNull[i] * TOKEN);
+ }
+ // goal represents the desired state
+ for (int i = 0; i < inargs; i++) {
+ if (state.contains(i * TOKEN)) {
+ goal.add(i * TOKEN);
+ } else {
+ // adapter must initially drop all unused arguments
+ drops.add(i);
}
- // goal represents the desired state
- for (int i = 0; i < inargs; i++) {
- if (state.contains(i * TOKEN)) {
- goal.add(i * TOKEN);
- } else {
- // adapter must initially drop all unused arguments
- 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);
}
}
- // 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;
}
}
}
- 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 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> 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;
}
}
- 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 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> 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);
- }
+ // Else de-rotate, and drop through to the swap-fest.
+ Collections.rotate(rotSpan, rotBy);
+ }
- // Now swap like the wind!
- List> 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);
- }
+ // Now swap like the wind!
+ List> 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));
+ }
+ // 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> 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;
}
- 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> 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();
+ //if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount));
+ drops.subList(0, dropArgCount).clear();
+ List> 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);
}
- 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> 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();
- }
+ 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)
return target;
if (oldType.parameterCount() != newType.parameterCount())
- throw newIllegalArgumentException("mismatched parameter count");
- MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target);
+ throw newIllegalArgumentException("mismatched parameter count", oldType, newType);
+ MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target, level);
if (res != null)
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();
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
// 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
// Use a heavier method: Convert all the arguments to Object,
// then back to the desired types. We might have to use Java-based
// method handles to do this.
MethodType objType = MethodType.genericMethodType(argc);
- MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target);
+ MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target, level);
if (objTarget == null)
objTarget = FromGeneric.make(target);
- res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget);
+ res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget, level);
if (res != null)
return res;
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,
MethodType newType,
- int spreadArg) {
+ int spreadArgPos,
+ Class> arrayType,
+ int arrayLength) {
// TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
MethodType oldType = target.type();
// spread the last argument of newType to oldType
- int spreadCount = oldType.parameterCount() - spreadArg;
- Class