--- old/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2016-03-22 13:02:09.830559873 +0300
+++ new/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2016-03-22 13:02:09.509559575 +0300
@@ -1192,9 +1192,7 @@
...
MethodHandle mh0 = lookup().findVirtual(defc, name, type);
MethodHandle mh1 = mh0.bindTo(receiver);
-MethodType mt1 = mh1.type();
-if (mh0.isVarargsCollector())
- mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1));
+mh1 = mh1.withVarargs(mh0.isVarargsCollector());
return mh1;
* }
* where {@code defc} is either {@code receiver.getClass()} or a super
@@ -2385,6 +2383,50 @@
assert(btw == Wrapper.OBJECT);
return makeIdentity(type);
}
+
+ /** Produces a constant method handle of the requested return type which
+ * returns the default value for that type every time it is invoked.
+ * The resulting constant method handle will have no side effects.
+ *
The returned method handle is equivalent to {@code empty(methodType(type))}.
+ * It is also equivalent to {@code explicitCastArguments(constant(Object.class, null), methodType(type))},
+ * since {@code explicitCastArguments} converts {@code null} to default values.
+ * @param type the expected return type of the desired method handle
+ * @return a constant method handle that takes no arguments and returns the default value of the given type (or void, if the type is void)
+ * @throws NullPointerException if the argument is null
+ * @see MethodHandles#constant
+ * @see MethodHandles#empty
+ * @since 9
+ */
+ public static MethodHandle zero(Class> type) {
+ Objects.requireNonNull(type);
+ return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type);
+ }
+
+ private static MethodHandle identityOrVoid(Class> type) {
+ return type == void.class ? zero(type) : identity(type);
+ }
+
+ /**
+ * Produces a method handle of the requested type which ignores any arguments, does nothing,
+ * and returns a suitable default depending on the return type.
+ * That is, it returns a zero primitive value, a {@code null}, or {@code void}.
+ *
The returned method handle is equivalent to
+ * {@code dropArguments(zero(type.returnType()), 0, type.parameterList())}.
+ *
+ * Example: Given a predicate and target, a useful "if-then" construct can be constructed as
+ * {@code guardWithTest(pred, target, empty(target.type())}.
+ * @param type the type of the desired method handle
+ * @return a constant method handle of the given type, which returns a default value of the given return type
+ * @throws NullPointerException if the argument is null
+ * @see MethodHandles#zero
+ * @see MethodHandles#constant
+ * @since 9
+ */
+ public static MethodHandle empty(MethodType type) {
+ Objects.requireNonNull(type);
+ return dropArguments(zero(type.returnType()), 0, type.parameterList());
+ }
+
private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.values().length];
private static MethodHandle makeIdentity(Class> ptype) {
MethodType mtype = MethodType.methodType(ptype, ptype);
@@ -2616,6 +2658,100 @@
return dropArguments(target, pos, Arrays.asList(valueTypes));
}
+ // private version which allows caller some freedom with error handling
+ private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List> newTypes, int pos,
+ boolean nullOnFailure) {
+ List> oldTypes = target.type().parameterList();
+ int match = oldTypes.size();
+ if (skip != 0) {
+ if (skip < 0 || skip > match) {
+ throw newIllegalArgumentException("illegal skip", skip, target);
+ }
+ oldTypes = oldTypes.subList(skip, match);
+ match -= skip;
+ }
+ List> addTypes = newTypes;
+ int add = addTypes.size();
+ if (pos != 0) {
+ if (pos < 0 || pos > add) {
+ throw newIllegalArgumentException("illegal pos", pos, newTypes);
+ }
+ addTypes = addTypes.subList(pos, add);
+ add -= pos; assert(addTypes.size() == add);
+ }
+ // Do not add types which already match the existing arguments.
+ if (match > add || !oldTypes.equals(addTypes.subList(0, match))) {
+ if (nullOnFailure) {
+ return null;
+ }
+ throw newIllegalArgumentException("argument lists do not match", oldTypes, newTypes);
+ }
+ addTypes = addTypes.subList(match, add);
+ add -= match; assert(addTypes.size() == add);
+ // newTypes: ( P*[pos], M*[match], A*[add] )
+ // target: ( S*[skip], M*[match] )
+ MethodHandle adapter = target;
+ if (add > 0) {
+ adapter = dropArguments(adapter, skip+ match, addTypes);
+ }
+ // adapter: (S*[skip], M*[match], A*[add] )
+ if (pos > 0) {
+ adapter = dropArguments(adapter, skip, newTypes.subList(0, pos));
+ }
+ // adapter: (S*[skip], P*[pos], M*[match], A*[add] )
+ return adapter;
+ }
+
+ /**
+ * Adapts a target method handle to match the given parameter type list, if necessary, by adding dummy arguments.
+ * Some leading parameters are first skipped; they will be left unchanged and are otherwise ignored.
+ * The remaining types in the target's parameter type list must be contained as a sub-list of the given type list,
+ * at the given position.
+ * Any non-matching parameter types (before or after the matching sub-list) are inserted in corresponding
+ * positions of the target method handle's parameters, as if by {@link #dropArguments}.
+ * (More precisely, elements in the new list before {@code pos} are inserted into the target list at {@code skip},
+ * while elements in the new list after the match beginning at {@code pos} are inserted at the end of the
+ * target list.)
+ * The target's return type will be unchanged.
+ *
+ * Example:
+ * Two method handles whose argument lists are "effectively identical" (i.e., identical
+ * in a common prefix) may be mutually converted to a common type
+ * by two calls to {@code dropArgumentsToMatch}, as follows:
+ *
{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+...
+MethodHandle h0= constant(boolean.class, true);
+MethodHandle h1 = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
+MethodType bigType = h1.type().insertParameterTypes(1, String.class, int.class);
+MethodHandle h2 = dropArguments(h1, 0, bigType.parameterList());
+if (h1.type().parameterCount() < h2.type().parameterCount())
+ h1 = dropArgumentsToMatch(h1, 0, h2.type().parameterList(), 0); // lengthen h1
+else
+ h2 = dropArgumentsToMatch(h2, 0, h1.type().parameterList(), 0); // lengthen h2
+MethodHandle h3 = guardWithTest(h0, h1, h2);
+assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
+ * }
+ * @param target the method handle to adapt
+ * @param skip number of targets parameters to disregard (they will be unchanged)
+ * @param newTypes the desired argument list of the method handle
+ * @param pos place in {@code newTypes} where the non-skipped target parameters must occur
+ * @return a possibly adapted method handle
+ * @throws NullPointerException if either argument is null
+ * @throws IllegalArgumentException
+ * if either index is out of range in its corresponding list, or
+ * if the non-skipped target parameter types match the new types at {@code pos}
+ * @since 9
+ */
+ public static
+ MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List> newTypes, int pos) {
+ Objects.requireNonNull(target);
+ Objects.requireNonNull(newTypes);
+ return dropArgumentsToMatch(target, skip, newTypes, pos, false);
+ }
+
/**
* Adapts a target method handle by pre-processing
* one or more of its arguments, each with its own unary filter function,
@@ -3127,13 +3263,9 @@
if (gtype.returnType() != boolean.class)
throw newIllegalArgumentException("guard type is not a predicate "+gtype);
List> targs = ttype.parameterList();
- List> gargs = gtype.parameterList();
- if (!targs.equals(gargs)) {
- int gpc = gargs.size(), tpc = targs.size();
- if (gpc >= tpc || !targs.subList(0, gpc).equals(gargs))
- throw misMatchedTypes("target and test types", ttype, gtype);
- test = dropArguments(test, gpc, targs.subList(gpc, tpc));
- gtype = test.type();
+ test = dropArgumentsToMatch(test, 0, targs, 0, true);
+ if (test == null) {
+ throw misMatchedTypes("target and test types", ttype, gtype);
}
return MethodHandleImpl.makeGuardWithTest(test, target, fallback);
}
@@ -3205,15 +3337,9 @@
throw newIllegalArgumentException("handler does not accept exception type "+exType);
if (htype.returnType() != ttype.returnType())
throw misMatchedTypes("target and handler return types", ttype, htype);
- List> targs = ttype.parameterList();
- List> hargs = htype.parameterList();
- hargs = hargs.subList(1, hargs.size()); // omit leading parameter from handler
- if (!targs.equals(hargs)) {
- int hpc = hargs.size(), tpc = targs.size();
- if (hpc >= tpc || !targs.subList(0, hpc).equals(hargs))
- throw misMatchedTypes("target and handler types", ttype, htype);
- handler = dropArguments(handler, 1+hpc, targs.subList(hpc, tpc));
- htype = handler.type();
+ handler = dropArgumentsToMatch(handler, 1, ttype.parameterList(), 0, true);
+ if (handler == null) {
+ throw misMatchedTypes("target and handler types", ttype, htype);
}
return MethodHandleImpl.makeGuardWithCatch(target, exType, handler);
}
@@ -3471,16 +3597,16 @@
for (int i = 0; i < nclauses; ++i) {
Class> t = iterationVariableTypes.get(i);
if (init.get(i) == null) {
- init.set(i, zeroHandle(t));
+ init.set(i, empty(MethodType.methodType(t, commonSuffix)));
}
if (step.get(i) == null) {
- step.set(i, dropArguments(t == void.class ? zeroHandle(t) : identity(t), 0, commonPrefix.subList(0, i)));
+ step.set(i, dropArgumentsToMatch(identityOrVoid(t), 0, commonParameterSequence, i));
}
if (pred.get(i) == null) {
- pred.set(i, constant(boolean.class, true));
+ pred.set(i, dropArguments(constant(boolean.class, true), 0, commonParameterSequence));
}
if (fini.get(i) == null) {
- fini.set(i, zeroHandle(t));
+ fini.set(i, empty(MethodType.methodType(t, commonParameterSequence)));
}
}
@@ -3574,7 +3700,7 @@
* @since 9
*/
public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
- MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType());
+ MethodHandle fin = init == null ? zero(void.class) : identity(init.type().returnType());
MethodHandle[] checkExit = {null, null, pred, fin};
MethodHandle[] varBody = {init, body};
return loop(checkExit, varBody);
@@ -3640,7 +3766,7 @@
* @since 9
*/
public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
- MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType());
+ MethodHandle fin = init == null ? zero(void.class) : identity(init.type().returnType());
MethodHandle[] clause = {init, body, pred, fin};
return loop(clause);
}
@@ -3775,7 +3901,7 @@
* @since 9
*/
public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
- MethodHandle returnVar = dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()),
+ MethodHandle returnVar = dropArguments(init == null ? zero(void.class) : identity(init.type().returnType()),
0, int.class, int.class);
MethodHandle[] indexVar = {start, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep)};
MethodHandle[] loopLimit = {end, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred), returnVar};
@@ -3876,7 +4002,7 @@
Class> ttype = body.type().parameterType(0);
MethodHandle returnVar =
- dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), 0, itype);
+ dropArguments(init == null ? zero(void.class) : identity(init.type().returnType()), 0, itype);
MethodHandle initnx = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext);
MethodHandle nextVal = initnx.asType(initnx.type().changeReturnType(ttype));
@@ -3970,15 +4096,11 @@
checkTryFinally(target, cleanup);
// Match parameter lists: if the cleanup has a shorter parameter list than the target, add ignored arguments.
- int tpSize = targetParamTypes.size();
- int cpPrefixLength = rtype == void.class ? 1 : 2;
- int cpSize = cleanupParamTypes.size();
- MethodHandle aCleanup = cpSize - cpPrefixLength < tpSize ?
- dropArguments(cleanup, cpSize, targetParamTypes.subList(tpSize - (cpSize - cpPrefixLength), tpSize)) :
- cleanup;
-
+ // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the
+ // target parameter list.
+ cleanup = dropArgumentsToMatch(cleanup, (rtype == void.class ? 1 : 2), targetParamTypes, 0);
MethodHandle aTarget = target.asSpreader(Object[].class, target.type().parameterCount());
- aCleanup = aCleanup.asSpreader(Object[].class, tpSize);
+ MethodHandle aCleanup = cleanup.asSpreader(Object[].class, targetParamTypes.size());
return MethodHandleImpl.makeTryFinally(aTarget, aCleanup, rtype, targetParamTypes);
}
@@ -4069,16 +4191,6 @@
return result;
}
- /**
- * Wrap creation of a proper zero handle for a given type.
- *
- * @param type the type.
- *
- * @return a zero value for the given type.
- */
- static MethodHandle zeroHandle(Class> type) {
- return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type);
- }
private static void checkLoop0(MethodHandle[][] clauses) {
if (clauses == null || clauses.length == 0) {