--- old/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2015-11-13 17:33:59.000000000 +0100 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2015-11-13 17:33:59.000000000 +0100 @@ -26,10 +26,7 @@ package java.lang.invoke; import java.lang.reflect.*; -import java.util.BitSet; -import java.util.List; -import java.util.Arrays; -import java.util.Objects; +import java.util.*; import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyAccess; @@ -39,11 +36,13 @@ import sun.reflect.misc.ReflectUtil; import sun.security.util.SecurityConstants; import java.lang.invoke.LambdaForm.BasicType; -import static java.lang.invoke.LambdaForm.BasicType.*; + import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleImpl.Intrinsic; import static java.lang.invoke.MethodHandleNatives.Constants.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * This class consists exclusively of static methods that operate on or return @@ -176,7 +175,7 @@ * equivalent of a particular bytecode behavior. * (Bytecode behaviors are described in section 5.4.3.5 of the Java Virtual Machine Specification.) * Here is a summary of the correspondence between these factory methods and - * the behavior the resulting method handles: + * the behavior of the resulting method handles: *
+ * The bytecode behavior for a {@code findClass} operation is a load of a constant class, + * as if by {@code ldc CONSTANT_Class}. + * The behavior is represented, not as a method handle, but directly as a {@code Class} constant. + *
* In cases where the given member is of variable arity (i.e., a method or constructor) * the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}. * In all other cases, the returned method handle will be of fixed arity. @@ -423,7 +430,7 @@ * and the Core Reflection API * (as found on {@link java.lang.Class Class}). *
- * If a security manager is present, member lookups are subject to + * If a security manager is present, member and class lookups are subject to * additional checks. * From one to three calls are made to the security manager. * Any of these calls can refuse access by throwing a @@ -433,6 +440,8 @@ * {@code refc} as the containing class in which the member * is being sought, and {@code defc} as the class in which the * member is actually defined. + * (If a class or other type is being accessed, + * the {@code refc} and {@code defc} values are the class itself.) * The value {@code lookc} is defined as not present * if the current lookup object does not have * private access. @@ -444,11 +453,16 @@ * then {@link SecurityManager#checkPackageAccess * smgr.checkPackageAccess(refcPkg)} is called, * where {@code refcPkg} is the package of {@code refc}. - *
+ * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration value and/or a loop + * exit. Each iteration of the loop executes each clause in order. A clause can optionally update its iteration + * variable; it can also optionally perform a test and conditional loop exit. In order to express this logic in + * terms of method handles, each clause will determine four actions:
+ * Some of these clause parts may be omitted according to certain rules, and useful default behavior is provided in + * this case. See below for a detailed description. + *
+ * Each clause function, with the exception of clause initializers, is able to observe the entire loop state, + * because it will be passed all current iteration variable values, as well as all incoming loop + * parameters. Most clause functions will not need all of this information, but they will be formally connected as + * if by {@link #dropArguments}. + *
+ * Given a set of clauses, there is a number of checks and adjustments performed to connect all the parts of the + * loop. They are spelled out in detail in the steps below. In these steps, every occurrence of the word "must" + * corresponds to a place where {@link IllegalArgumentException} may be thrown if the required constraint is not met + * by the inputs to the loop combinator. The term "effectively identical", applied to parameter type lists, means + * that they must be identical, or else one list must be a proper prefix of the other. + *
+ * Step 0: Determine clause structure.
+ * Step 1A: Determine iteration variables.
+ * Step 1B: Determine loop parameters.
+ * Step 1C: Determine loop return type.
+ * Step 1D: Check other types.
+ * (Implementation Note: Steps 1A, 1B, 1C, 1D are logically independent of each other, and may be performed in any + * order.) + *
+ * Step 2: Determine parameter lists.
+ * Step 3: Fill in omitted functions.
+ * Step 4: Fill in missing parameter types.
+ * Final observations.
+ * Loop execution.
+ * Here is pseudocode for the resulting loop handle: + *
+ *{@code + * V... init...(A...); + * boolean pred...(V..., A...); + * V... step...(V..., A...); + * R fini...(V..., A...); + * R loop(A... a) { + * V... v... = init...(a...); + * for (;;) { + * for ((v, p, s, f) in (v..., pred..., step..., fini...)) { + * v = s(v..., a...); + * if (!p(v..., a...)) { + * return f(v..., a...); + * } + * } + * } + * } + * }
+ * Example: + *
+ * + * @param clauses an array of arrays (4-tuples) of {@link MethodHandle}s adhering to the rules described above. + * + * @return a method handle embodying the looping behavior as defined by the arguments. + * + * @throws IllegalArgumentException in case any of the constraints described above is violated. + * + * @see MethodHandles#whileLoop(MethodHandle, MethodHandle, MethodHandle) + * @see MethodHandles#doWhileLoop(MethodHandle, MethodHandle, MethodHandle) + * @see MethodHandles#countedLoop(MethodHandle, MethodHandle, MethodHandle) + * @see MethodHandles#iteratedLoop(MethodHandle, MethodHandle, MethodHandle) + */ + public static MethodHandle loop(MethodHandle[]... clauses) { + // Step 0: determine clause structure. + if (clauses == null || clauses.length == 0) { + err("null or no clauses passed"); + } + if (Stream.of(clauses).anyMatch(Objects::isNull)) { + err("null clauses are not allowed"); + } + if (Stream.of(clauses).anyMatch(c -> c.length > 4)) { + err("All loop clauses must be represented as MethodHandle arrays with at most 4 elements."); + } + + List{@code + * // iterative implementation of the factorial function as a loop handle + * static int one(int k) { return 1; } + * int inc(int i, int acc, int k) { return i + 1; } + * int mult(int i, int acc, int k) { return i * acc; } + * boolean pred(int i, int acc, int k) { return i < k; } + * int fin(int i, int acc, int k) { return acc; } + * // assume MH_one, MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods + * // null initializer for counter, should initialize to 0 + * MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc}; + * MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin}; + * MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); + * assertEquals(120, loop.invoke(5)); + * }
+ * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}. + * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle + * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code + * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][]) + * generic loop combinator}. + *
+ * Here is pseudocode for the resulting loop handle: + *
+ *{@code + * V init(A); + * boolean pred(V, A); + * V body(V, A); + * V whileLoop(A a) { + * V v = init(a); + * while (pred(v, a)) { + * v = body(v, a); + * } + * return v; + * } + * }
+ * Example: + *
+ * + *{@code + * // implement the zip function for lists as a loop handle + * ListinitZip(Iterator a, Iterator b) { return new ArrayList<>(); } + * boolean zipPred(List zip, Iterator a, Iterator b) { return a.hasNext() && b.hasNext(); } + * List zipStep(List zip, Iterator a, Iterator b) { + * zip.add(a.next()); + * zip.add(b.next()); + * return zip; + * } + * // assume MH_initZip, MH_zipPred, and MH_zipStep are handles to the above methods + * MethodHandle loop = MethodHandles.doWhileLoop(MH_initZip, MH_zipPred, MH_zipStep); + * List a = Arrays.asList(new String[]{"a", "b", "c", "d"}); + * List b = Arrays.asList(new String[]{"e", "f", "g", "h"}); + * List zipped = Arrays.asList(new String[]{"a", "e", "b", "f", "c", "g", "d", "h"}); + * assertEquals(zipped, (List ) loop.invoke(a.iterator(), b.iterator())); + * }
+ * The implementation of this method is equivalent to: + *
+ * + * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's + * result type. Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param pred condition for the loop, which may not be {@code null}. + * @param body body of the loop, which may not be {@code null}. + * + * @return the value of the loop variable as the loop terminates. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @see MethodHandles#loop(MethodHandle[][]) + */ + public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) { + MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType()); + MethodHandle[] checkExit = {null, null, pred, fin}; + MethodHandle[] varBody = {init, body}; + return loop(checkExit, varBody); + } + + /** + * Constructs a {@code do-while} loop from an initializer, a body, and a predicate. This is a convenience wrapper + * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *{@code + * MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) { + * MethodHandle[] + * checkExit = {null, null, pred, identity(init.type().returnType())}, + * varBody = {init, body}; + * return loop(checkExit, varBody); + * } + * }
+ * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}. + * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle + * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code + * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][]) + * generic loop combinator}. + *
+ * Here is pseudocode for the resulting loop handle: + *
+ *{@code + * V init(A); + * boolean pred(V, A); + * V body(V, A); + * V doWhileLoop(A a) { + * V v = init(a); + * do { + * v = body(v, a); + * } while (pred(v, a)); + * return v; + * } + * }
+ * Example: + *
+ * + * *{@code + * // int i = 0; while (i < limit) { ++i; } return i; => limit + * int zero(int limit) { return 0; } + * int step(int i, int limit) { return i + 1; } + * boolean pred(int i, int limit) { return i < limit; } + * // assume MH_zero, MH_step, and MH_pred are handles to the above methods + * MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred); + * assertEquals(23, loop.invoke(23)); + * }
+ * The implementation of this method is equivalent to: + *
+ * + * + * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's + * result type. Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param pred condition for the loop, which may not be {@code null}. + * @param body body of the loop, which may not be {@code null}. + * + * @return the value of the loop variable as the loop terminates. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @see MethodHandles#loop(MethodHandle[][]) + */ + public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) { + MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType()); + MethodHandle[] clause = {init, body, pred, fin}; + return loop(clause); + } + + /** + * Constructs a loop that runs a given number of iterations. The loop counter is an {@code int} initialized from the + * {@code iterations} handle evaluation result. The counter is passed to the {@code body} function, so that must + * accept an initial {@code int} argument. The result of the loop execution is the final value of the additional + * local state. This is a convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *{@code + * MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) { + * MethodHandle[] clause = { init, body, pred, identity(init.type().returnType()) }; + * return loop(clause); + * } + * }
+ * The result type and parameter type list of {@code init} determine those of the resulting handle. The {@code + * iterations} handle must accept the same parameter types as {@code init} but return an {@code int}. The {@code + * body} handle must accept the same parameter types as well, preceded by an {@code int} parameter for the counter, + * and a parameter of the same type as {@code init}'s result. These constraints follow directly from those described + * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *
+ * Here is pseudocode for the resulting loop handle: + *
+ *{@code + * int iterations(A); + * V init(A); + * V body(int, V, A); + * V countedLoop(A a) { + * int end = iterations(a); + * V v = init(a); + * for (int i = 0; i < end; ++i) { + * v = body(i, v, a); + * } + * return v; + * } + * }
+ * Example: + *
+ * + *{@code + * // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; + * // => a variation on a well known theme + * String start(String arg) { return arg; } + * String step(int counter, String v, String arg) { return "na " + v; } + * // assume MH_start and MH_step are handles to the two methods above + * MethodHandle loop = MethodHandles.countedLoop(13, MH_start, MH_step); + * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!")); + * }
+ * The implementation of this method is equivalent to: + *
+ * + * @param iterations a handle to return the number of iterations this loop should run. + * @param init initializer for additional loop state. This determines the loop's result type. + * Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param body the body of the loop, which must not be {@code null}. + * It must accept an initial {@code int} parameter (for the counter), and then any + * additional loop-local variable plus loop parameters. + * + * @return a method handle representing the loop. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + */ + public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) { + return countedLoop(null, iterations, init, body); + } + + /** + * Constructs a loop that counts over a range of numbers. The loop counter is an {@code int} that will be + * initialized to the {@code int} value returned from the evaluation of the {@code start} handle and run to the + * value returned from {@code end} (exclusively) with a step width of 1. The counter value is passed to the {@code + * body} function in each iteration; it has to accept an initial {@code int} parameter + * for that. The result of the loop execution is the final value of the additional local state + * obtained by running {@code init}. + * This is a + * convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *{@code + * MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) { + * return countedLoop(null, iterations, init, body); // null => constant zero + * } + * }
+ * The constraints for the {@code init} and {@code body} handles are the same as for {@link + * #countedLoop(MethodHandle, MethodHandle, MethodHandle)}. Additionally, the {@code start} and {@code end} handles + * must return an {@code int} and accept the same parameters as {@code init}. + *
+ * Here is pseudocode for the resulting loop handle: + *
+ * + *{@code + * int start(A); + * int end(A); + * V init(A); + * V body(int, V, A); + * V countedLoop(A a) { + * int s = start(a); + * int e = end(a); + * V v = init(a); + * for (int i = s; i < e; ++i) { + * v = body(i, v, a); + * } + * return v; + * } + * }
+ * The implementation of this method is equivalent to: + *
+ * + * @param start a handle to return the start value of the loop counter. + * If it is {@code null}, a constant zero is assumed. + * @param end a non-{@code null} handle to return the end value of the loop counter (the loop will run to {@code end-1}). + * @param init initializer for additional loop state. This determines the loop's result type. + * Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param body the body of the loop, which must not be {@code null}. + * It must accept an initial {@code int} parameter (for the counter), and then any + * additional loop-local variable plus loop parameters. + * + * @return a method handle representing the loop. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + */ + public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) { + MethodHandle returnVar = dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), + 0, int.class, int.class); + MethodHandle[] indexVar = {start, MethodHandleImpl.Lazy.MH_countedLoopStep}; + MethodHandle[] loopLimit = {end, null, MethodHandleImpl.Lazy.MH_countedLoopPred, returnVar}; + MethodHandle[] bodyClause = {init, dropArguments(body, 1, int.class)}; + return loop(indexVar, loopLimit, bodyClause); + } + + /** + * Constructs a loop that ranges over the elements produced by an {@code Iterator{@code + * MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) { + * MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class); + * // assume MH_increment and MH_lessThan are handles to x+1 and x
+ * This is a convenience wrapper for the + * {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}, and the constraints imposed on the {@code body} + * handle follow directly from those described for the latter. + *
+ * Here is pseudocode for the resulting loop handle: + *
+ *{@code + * Iteratoriterator(A); // defaults to Iterable::iterator + * V init(A); + * V body(T,V,A); + * V iteratedLoop(A a) { + * Iterator it = iterator(a); + * V v = init(a); + * for (T t : it) { + * v = body(t, v, a); + * } + * return v; + * } + * }
+ * The type {@code T} may be either a primitive or reference.
+ * Since type {@code Iterator
+ * Example:
+ *
+ * The implementation of this method is equivalent to:
+ *
+ * The {@code cleanup} handle will be passed one or two additional leading arguments.
+ * The first is the exception thrown during the
+ * execution of the {@code target} handle, or {@code null} if no exception was thrown.
+ * The second is the result of the execution of the {@code target} handle, or, if it throws an exception,
+ * a {@code null}, zero, or {@code false} value of the required type is supplied as a placeholder.
+ * The second argument is not present if the {@code target} handle has a {@code void} return type.
+ * (Note that, except for argument type conversions, combinators represent {@code void} values in parameter lists
+ * by omitting the corresponding paradoxical arguments, not by inserting {@code null} or zero values.)
+ *
+ * The {@code target} and {@code cleanup} handles' return types must be the same. Their parameter type lists also
+ * must be the same, but the {@code cleanup} handle must accept one or two more leading parameters:
+ * The pseudocode for the resulting adapter looks like this:
+ *
+ * Note that the saved arguments ({@code a...} in the pseudocode) cannot
+ * be modified by execution of the target, and so are passed unchanged
+ * from the caller to the cleanup, if it is invoked.
+ *
+ * The target and cleanup must return the same type, even if the cleanup
+ * always throws.
+ * To create such a throwing cleanup, compose the cleanup logic
+ * with {@link #throwException throwException},
+ * in order to create a method handle of the correct return type.
+ *
+ * Note that {@code tryFinally} never converts exceptions into normal returns.
+ * In rare cases where exceptions must be converted in that way, first wrap
+ * the target with {@link #catchException(MethodHandle, Class, MethodHandle)}
+ * to capture an outgoing exception, and then wrap with {@code tryFinally}.
+ *
+ * @param target the handle whose execution is to be wrapped in a {@code try} block.
+ * @param cleanup the handle that is invoked in the finally block.
+ *
+ * @return a method handle embodying the {@code try-finally} block composed of the two arguments.
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if {@code cleanup} does not accept
+ * the required leading arguments, or if the method handle types do
+ * not match in their return types and their
+ * corresponding trailing parameters
+ *
+ * @see MethodHandles#catchException(MethodHandle, Class, MethodHandle)
+ */
+ public static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup) {
+ List
+ * This method is closely related to {@link #foldArguments(MethodHandle, MethodHandle)}, but allows to control the
+ * position in the parameter list at which folding takes place. The argument controlling this, {@code pos}, is a
+ * zero-based index. The aforementioned method {@link #foldArguments(MethodHandle, MethodHandle)} assumes position
+ * 0.
+ *
+ * Example:
+ * Here is pseudocode for the resulting adapter:
+ *
+ * {@code
+ * // reverse a list
+ * List
+ *
+ * @param iterator a handle to return the iterator to start the loop.
+ * Passing {@code null} will make the loop call {@link Iterable#iterator()} on the first
+ * incoming value.
+ * @param init initializer for additional loop state. This determines the loop's result type.
+ * Passing {@code null} or a {@code void} init function will make the loop's result type
+ * {@code void}.
+ * @param body the body of the loop, which must not be {@code null}.
+ * It must accept an initial {@code T} parameter (for the iterated values), and then any
+ * additional loop-local variable plus loop parameters.
+ *
+ * @return a method handle embodying the iteration loop functionality.
+ * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ */
+ public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
+ if (null == body) {
+ err("iterated loop body must not be null");
+ }
+
+ MethodHandle initIterator = iterator == null ?
+ MethodHandleImpl.Lazy.MH_initIterator.asType(MethodHandleImpl.Lazy.MH_initIterator.type().
+ changeParameterType(0, body.type().parameterType(init == null ? 1 : 2))) :
+ iterator;
+ Class> itype = initIterator.type().returnType();
+ Class> ttype = body.type().parameterType(0);
+
+ MethodHandle returnVar =
+ dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), 0, itype);
+ MethodHandle nextVal = MethodHandleImpl.Lazy.MH_iterateNext.
+ asType(MethodHandleImpl.Lazy.MH_iterateNext.type().changeReturnType(ttype));
+
+ MethodHandle[] iterVar = {initIterator, null, MethodHandleImpl.Lazy.MH_iteratePred, returnVar};
+ MethodHandle[] bodyClause = {init, filterArgument(body, 0, nextVal)};
+
+ return loop(iterVar, bodyClause);
+ }
+
+ /**
+ * Makes a method handle that adapts a {@code target} method handle by wrapping it in a {@code try-finally} block.
+ * Another method handle, {@code cleanup}, represents the functionality of the {@code finally} block. Any exception
+ * thrown during the execution of the {@code target} handle will be passed to the {@code cleanup} handle. The
+ * exception will be rethrown, unless {@code cleanup} handle throws an exception first. The
+ * value returned from the {@code cleanup} handle's execution will be the result of the execution of the
+ * {@code try-finally} handle.
+ * {@code
+ * MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
+ * // assume MH_next and MH_hasNext are handles to methods of Iterator
+ * Class> itype = iterator.type().returnType();
+ * Class> ttype = body.type().parameterType(0);
+ * MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, itype);
+ * MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
+ * MethodHandle[]
+ * iterVar = {iterator, null, MH_hasNext, returnVar}, // it = iterator(); while (it.hasNext)
+ * bodyClause = {init, filterArgument(body, 0, nextVal)}; // v = body(t, v, a);
+ * return loop(iterVar, bodyClause);
+ * }
+ * }
+ *
+ *
+ * {@code
+ * T target(A..., B...);
+ * T cleanup(Throwable, T, A...);
+ * T adapter(A... a, B... b) {
+ * T result = (zero value for T);
+ * Throwable throwable = null;
+ * try {
+ * result = target(a..., b...);
+ * } catch (Throwable t) {
+ * throwable = t;
+ * throw t;
+ * } finally {
+ * result = cleanup(throwable, result, a...);
+ * }
+ * return result;
+ * }
+ * }
+ * {@code
+ import static java.lang.invoke.MethodHandles.*;
+ import static java.lang.invoke.MethodType.*;
+ ...
+ MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
+ "println", methodType(void.class, String.class))
+ .bindTo(System.out);
+ MethodHandle cat = lookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+ assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
+ MethodHandle catTrace = foldArguments(cat, 1, trace);
+ // also prints "jum":
+ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+ * }
+ *
+ * @param target the method handle to invoke after arguments are combined
+ * @param pos the position at which to start folding and at which to insert the folding result; if this is {@code
+ * 0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}.
+ * @param combiner method handle to call initially on the incoming arguments
+ * @return method handle which incorporates the specified argument folding logic
+ * @throws NullPointerException if either argument is null
+ * @throws IllegalArgumentException if {@code combiner}'s return type
+ * is non-void and not the same as the argument type at position {@code pos} of
+ * the target signature, or if the {@code N} argument types at position {@code pos}
+ * of the target signature
+ * (skipping one matching the {@code combiner}'s return type)
+ * are not identical with the argument types of {@code combiner}
+ *
+ * @see #foldArguments(MethodHandle, MethodHandle)
+ */
+ public static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner) {
+ MethodType targetType = target.type();
+ MethodType combinerType = combiner.type();
+ Class> rtype = foldArgumentChecks(pos, targetType, combinerType);
+ BoundMethodHandle result = target.rebind();
+ boolean dropResult = rtype == void.class;
+ LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType());
+ MethodType newType = targetType;
+ if (!dropResult) {
+ newType = newType.dropParameterTypes(pos, pos + 1);
+ }
+ result = result.copyWithExtendL(newType, lform, combiner);
+ 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 checkTryFinally(MethodHandle cleanup, List{@code
+ * // there are N arguments in A...
+ * T target(Z..., V, A[N]..., B...);
+ * V combiner(A...);
+ * T adapter(Z... z, A... a, B... b) {
+ * V v = combiner(a...);
+ * return target(z..., v, a..., b...);
+ * }
+ * // and if the combiner has a void return:
+ * T target2(Z..., A[N]..., B...);
+ * void combiner2(A...);
+ * T adapter2(Z... z, A... a, B... b) {
+ * combiner2(a...);
+ * return target2(z..., a..., b...);
+ * }
+ * }