diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index ca4f050553f..05ef3c983f0 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -1962,12 +1962,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; * This method is bound as the predicate in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, * MethodHandle) counting loops}. * - * @param counter the counter parameter, passed in during loop execution. * @param limit the upper bound of the parameter, statically bound at loop creation time. + * @param counter the counter parameter, passed in during loop execution. * * @return whether the counter has reached the limit. */ - static boolean countedLoopPredicate(int counter, int limit) { + static boolean countedLoopPredicate(int limit, int counter) { return counter < limit; } @@ -1975,26 +1975,15 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; * This method is bound as the step function in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, * MethodHandle) counting loops} to increment the counter. * + * @param limit the upper bound of the loop counter (ignored). * @param counter the loop counter. * * @return the loop counter incremented by 1. */ - static int countedLoopStep(int counter, int limit) { + static int countedLoopStep(int limit, int counter) { return counter + 1; } - /** - * This method is bound as a filter in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, MethodHandle, - * MethodHandle) counting loops} to pass the correct counter value to the body. - * - * @param counter the loop counter. - * - * @return the loop counter decremented by 1. - */ - static int decrementCounter(int counter) { - return counter - 1; - } - /** * This is bound to initialize the loop-local iterator in {@linkplain MethodHandles#iteratedLoop iterating loops}. * @@ -2164,12 +2153,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; MH_arrayIdentity = 5, MH_countedLoopPred = 6, MH_countedLoopStep = 7, - MH_iteratePred = 8, - MH_initIterator = 9, + MH_initIterator = 8, + MH_iteratePred = 9, MH_iterateNext = 10, - MH_decrementCounter = 11, - MH_Array_newInstance = 12, - MH_LIMIT = 13; + MH_Array_newInstance = 11, + MH_LIMIT = 12; static MethodHandle getConstantHandle(int idx) { MethodHandle handle = HANDLES[idx]; @@ -2220,18 +2208,15 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; case MH_countedLoopStep: return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopStep", MethodType.methodType(int.class, int.class, int.class)); - case MH_iteratePred: - return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate", - MethodType.methodType(boolean.class, Iterator.class)); case MH_initIterator: return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "initIterator", MethodType.methodType(Iterator.class, Iterable.class)); + case MH_iteratePred: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate", + MethodType.methodType(boolean.class, Iterator.class)); case MH_iterateNext: return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iterateNext", MethodType.methodType(Object.class, Iterator.class)); - case MH_decrementCounter: - return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "decrementCounter", - MethodType.methodType(int.class, int.class)); case MH_Array_newInstance: return IMPL_LOOKUP.findStatic(Array.class, "newInstance", MethodType.methodType(Object.class, Class.class, int.class)); diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index 28364e53ae9..104aa220932 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -44,8 +44,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ReflectPermission; import java.nio.ByteOrder; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@ -3114,7 +3112,7 @@ assert((int)twice.invokeExact(21) == 42); * @see MethodHandles#explicitCastArguments * @since 9 */ - public static MethodHandle zero(Class> type) { + public static MethodHandle zero(Class> type) { Objects.requireNonNull(type); return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type); } @@ -3403,7 +3401,8 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); throw newIllegalArgumentException("illegal pos", pos, newTypes); } addTypes = addTypes.subList(pos, add); - add -= pos; assert(addTypes.size() == 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))) { @@ -3413,7 +3412,8 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); throw newIllegalArgumentException("argument lists do not match", oldTypes, newTypes); } addTypes = addTypes.subList(match, add); - add -= match; assert(addTypes.size() == add); + add -= match; + assert(addTypes.size() == add); // newTypes: ( P*[pos], M*[match], A*[add] ) // target: ( S*[skip], M*[match] ) MethodHandle adapter = target; @@ -3423,26 +3423,37 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); // adapter: (S*[skip], M*[match], A*[add] ) if (pos > 0) { adapter = dropArguments0(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. + * Adapts a target method handle to match the given parameter type list. If necessary, adds dummy arguments. Some + * leading parameters can be skipped before matching begins. The remaining types in the {@code target}'s parameter + * type list must be a sub-list of the {@code newTypes} type list at the starting position {@code pos}. The + * resulting handle will have the target handle's parameter type list, with any non-matching parameter types (before + * or after the matching sub-list) inserted in corresponding positions of the target's original parameters, as if by + * {@link #dropArguments(MethodHandle, int, Class[])}. + *
+ * The resulting handle will have the same return type as the target handle. + *
+ * In more formal terms, assume these two type lists:
* @apiNote - * 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: + * 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.*;
@@ -3461,14 +3472,15 @@ 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 newTypes the list of types to match {@code target}'s parameter type list to
* @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 any element of {@code newTypes} is {@code void.class},
* or if {@code skip} is negative or greater than the arity of the target,
* or if {@code pos} is negative or greater than the newTypes list size,
- * or if the non-skipped target parameter types match the new types at {@code pos}
+ * or if {@code newTypes} does not contain the {@code target}'s non-skipped parameter types at position
+ * {@code pos}.
* @since 9
*/
public static
@@ -3922,6 +3934,113 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
return foldArguments(target, 0, combiner);
}
+ /**
+ * Adapts a target method handle by pre-processing some of its arguments, starting at a given position, and then
+ * calling the target with the result of the pre-processing, inserted into the original sequence of arguments just
+ * before the folded arguments.
+ * + * 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. + *
+ * @apiNote Example: + *
{@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"));
+ * }
+ * Here is pseudocode for the resulting adapter. In the code, {@code T} + * represents the result type of the {@code target} and resulting adapter. + * {@code V}/{@code v} represent the type and value of the parameter and argument + * of {@code target} that precedes the folding position; {@code V} also is + * the result type of the {@code combiner}. {@code A}/{@code a} denote the + * types and values of the {@code N} parameters and arguments at the folding + * position. {@code Z}/{@code z} and {@code B}/{@code b} represent the types + * and values of the {@code target} parameters and arguments that precede and + * follow the folded parameters and arguments starting at {@code pos}, + * respectively. + *
{@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...);
+ * }
+ * }
+ * + * Note: The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector + * variable-arity method handle}, even if the original target method handle was. + * + * @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 either of the following two conditions holds: + * (1) {@code combiner}'s return type is non-{@code void} and not the same as the argument type at position + * {@code pos} of the target signature; + * (2) 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) + * @since 9 + */ + 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; + } + + /** + * As {@see foldArguments(MethodHandle, int, MethodHandle)}, but with the + * added capability of selecting the arguments from the targets parameters + * to call the combiner with. This allows us to avoid some simple cases of + * permutations and padding the combiner with dropArguments to select the + * right argument, which may ultimately produce fewer intermediaries. + */ + static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner, int ... argPositions) { + MethodType targetType = target.type(); + MethodType combinerType = combiner.type(); + Class> rtype = foldArgumentChecks(pos, targetType, combinerType, argPositions); + BoundMethodHandle result = target.rebind(); + boolean dropResult = rtype == void.class; + LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType(), argPositions); + MethodType newType = targetType; + if (!dropResult) { + newType = newType.dropParameterTypes(pos, pos + 1); + } + result = result.copyWithExtendL(newType, lform, combiner); + return result; + } + private static Class> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) { int foldArgs = combinerType.parameterCount(); Class> rtype = combinerType.returnType(); @@ -4125,32 +4244,69 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); * iteration. Upon termination of the loop due to one of the predicates, a corresponding finalizer is run and * delivers the loop's result, which is the return value of the resulting handle. *
- * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration value and/or a loop + * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration variable 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}. + * Parameters optional everywhere: + * Each clause function is allowed but not required to accept a parameter for each iteration variable {@code v}. + * As an exception, the init functions cannot take any {@code v} parameters, + * because those values are not yet computed when the init functions are executed. + * Any clause function may neglect to take any trailing subsequence of parameters it is entitled to take. + * In fact, any clause function may take no arguments at all. *
+ * Loop parameters: + * A clause function may take all the iteration variable values it is entitled to, in which case + * it may also take more trailing parameters. Such extra values are called loop parameters, + * with their types and values notated as {@code (A...)} and {@code (a...)}. + * These become the parameters of the resulting loop handle, to be supplied whenever the loop is executed. + * (Since init functions do not accept iteration variables {@code v}, any parameter to an + * init function is automatically a loop parameter {@code a}.) + * As with iteration variables, clause functions are allowed but not required to accept loop parameters. + * These loop parameters act as loop-invariant values visible across the whole loop. + *
+ * Parameters visible everywhere: + * Each non-init clause function is permitted to observe the entire loop state, because it can be passed the full + * list {@code (v... a...)} of current iteration variable values and incoming loop parameters. + * The init functions can observe initial pre-loop state, in the form {@code (a...)}. + * Most clause functions will not need all of this information, but they will be formally connected to it + * as if by {@link #dropArguments}. + * + * More specifically, we shall use the notation {@code (V*)} to express an arbitrary prefix of a full + * sequence {@code (V...)} (and likewise for {@code (v*)}, {@code (A*)}, {@code (a*)}). + * In that notation, the general form of an init function parameter list + * is {@code (A*)}, and the general form of a non-init function parameter list is {@code (V*)} or {@code (V... A*)}. + *
+ * Checking clause structure: * 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. + * corresponds to a place where {@link IllegalArgumentException} will be thrown if the required constraint is not + * met by the inputs to the loop combinator. + *
+ * Effectively identical sequences: + * + * A parameter list {@code A} is defined to be effectively identical to another parameter list {@code B} + * if {@code A} and {@code B} are identical, or if {@code A} is shorter and is identical with a proper prefix of {@code B}. + * When speaking of an unordered set of parameter lists, we say they the set is "effectively identical" + * as a whole if the set contains a longest list, and all members of the set are effectively identical to + * that longest list. + * For example, any set of type sequences of the form {@code (V*)} is effectively identical, + * and the same is true if more sequences of the form {@code (V... A*)} are added. *
* 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.
* Step 2: Determine parameter lists.
* Step 3: Fill in omitted functions.
* Step 4: Fill in missing parameter types.
* Final observations.
+ * Example. As a consequence of step 1A above, the {@code loop} combinator has the following property: + *
* Loop execution.
- * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the types / values - * of loop variables; {@code A}/{@code a}, those of arguments passed to the resulting loop; and {@code R}, the - * result types of finalizers as well as of the resulting loop. + * Usage tips. + *
+ * Here is pseudocode for the resulting loop handle. As above, {@code V} and {@code v} represent the types + * and values of loop variables; {@code A} and {@code a} represent arguments passed to the whole loop; + * and {@code R} is the common result type of all finalizers as well as of the resulting loop. *
{@code
* V... init...(A...);
* boolean pred...(V..., A...);
@@ -4270,6 +4469,9 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* }
* }
* }
+ * Note that the parameter type lists {@code (V...)} and {@code (A...)} have been expanded
+ * to their full length, even though individual clause functions may neglect to take them all.
+ * As noted above, missing parameters are filled in as if by {@link #dropArgumentsToMatch}.
* * @apiNote Example: *
{@code
@@ -4286,6 +4488,43 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
* assertEquals(120, loop.invoke(5));
* }
+ * The same example, dropping arguments and using combinators:
+ * {@code
+ * // simplified implementation of the factorial function as a loop handle
+ * static int inc(int i) { return i + 1; } // drop acc, k
+ * static int mult(int i, int acc) { return i * acc; } //drop k
+ * static boolean cmp(int i, int k) { return i < k; }
+ * // assume MH_inc, MH_mult, and MH_cmp are handles to the above methods
+ * // null initializer for counter, should initialize to 0
+ * MethodHandle MH_one = MethodHandles.constant(int.class, 1);
+ * MethodHandle MH_pred = MethodHandles.dropArguments(MH_cmp, 1, int.class); // drop acc
+ * MethodHandle MH_fin = MethodHandles.dropArguments(MethodHandles.identity(int.class), 0, int.class); // drop i
+ * 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(720, loop.invoke(6));
+ * }
+ * A similar example, using a helper object to hold a loop parameter:
+ * {@code
+ * // instance-based implementation of the factorial function as a loop handle
+ * static class FacLoop {
+ * final int k;
+ * FacLoop(int k) { this.k = k; }
+ * int inc(int i) { return i + 1; }
+ * int mult(int i, int acc) { return i * acc; }
+ * boolean pred(int i) { return i < k; }
+ * int fin(int i, int acc) { return acc; }
+ * }
+ * // assume MH_FacLoop is a handle to the constructor
+ * // assume MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
+ * // null initializer for counter, should initialize to 0
+ * MethodHandle MH_one = MethodHandles.constant(int.class, 1);
+ * MethodHandle[] instanceClause = new MethodHandle[]{MH_FacLoop};
+ * MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
+ * MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
+ * MethodHandle loop = MethodHandles.loop(instanceClause, counterClause, accumulatorClause);
+ * assertEquals(5040, loop.invoke(7));
+ * }
*
* @param clauses an array of arrays (4-tuples) of {@link MethodHandle}s adhering to the rules described above.
*
@@ -4301,7 +4540,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
*/
public static MethodHandle loop(MethodHandle[]... clauses) {
// Step 0: determine clause structure.
- checkLoop0(clauses);
+ loopChecks0(clauses);
List- * 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}. + * The {@code pred} handle describes the loop condition; and {@code body}, its body. The loop resulting from this + * method will, in each iteration, first evaluate the predicate and then execute its body (if the predicate + * evaluates to {@code true}). + * The loop will terminate once the predicate evaluates to {@code false} (the body will not be executed in this case). + *
+ * The {@code init} handle describes the initial value of an additional optional loop-local variable. + * In each iteration, this loop-local variable, if present, will be passed to the {@code body} + * and updated with the value returned from its invocation. The result of loop execution will be + * the final value of the additional loop-local variable (if present). + *
+ * The following rules hold for these argument handles:
+ * The resulting loop handle's result type and parameter signature are determined as follows:
* Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument * passed to the loop. *
{@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);
+ * 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;
* }
@@ -4439,58 +4785,96 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* }
*
* - * @implSpec The implementation of this method is equivalent to: + * @apiNote The implementation of this method can be expressed as follows: *
{@code
* MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
+ * MethodHandle fini = (body.type().returnType() == void.class
+ * ? null : identity(body.type().returnType()));
* MethodHandle[]
- * checkExit = {null, null, pred, identity(init.type().returnType())},
- * varBody = {init, body};
+ * checkExit = { null, null, pred, fini },
+ * varBody = { init, body };
* return loop(checkExit, varBody);
* }
* }
*
- * @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}.
+ * @param init optional initializer, providing the initial value of the loop variable.
+ * May be {@code null}, implying a default initial value. See above for other constraints.
+ * @param pred condition for the loop, which may not be {@code null}. Its result type must be {@code boolean}. See
+ * above for other constraints.
+ * @param body body of the loop, which may not be {@code null}. It controls the loop parameters and result type.
+ * See above for other constraints.
*
- * @return the value of the loop variable as the loop terminates.
- * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ * @return a method handle implementing the {@code while} loop as described by the arguments.
+ * @throws IllegalArgumentException if the rules for the arguments are violated.
+ * @throws NullPointerException if {@code pred} or {@code body} are {@code null}.
*
- * @see MethodHandles#loop(MethodHandle[][])
+ * @see #loop(MethodHandle[][])
+ * @see #doWhileLoop(MethodHandle, MethodHandle, MethodHandle)
* @since 9
*/
public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
- MethodHandle fin = init == null || init.type().returnType() == void.class ? zero(void.class) :
- identity(init.type().returnType());
- MethodHandle[] checkExit = {null, null, pred, fin};
- MethodHandle[] varBody = {init, body};
+ whileLoopChecks(init, pred, body);
+ MethodHandle fini = identityOrVoid(body.type().returnType());
+ MethodHandle[] checkExit = { null, null, pred, fini };
+ 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}.
+ * Constructs a {@code do-while} loop from an initializer, a body, and a predicate.
+ * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
* - * 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}. + * The {@code pred} handle describes the loop condition; and {@code body}, its body. The loop resulting from this + * method will, in each iteration, first execute its body and then evaluate the predicate. + * The loop will terminate once the predicate evaluates to {@code false} after an execution of the body. + *
+ * The {@code init} handle describes the initial value of an additional optional loop-local variable. + * In each iteration, this loop-local variable, if present, will be passed to the {@code body} + * and updated with the value returned from its invocation. The result of loop execution will be + * the final value of the additional loop-local variable (if present). + *
+ * The following rules hold for these argument handles:
+ * The resulting loop handle's result type and parameter signature are determined as follows:
* Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument * passed to the loop. *
{@code
- * V init(A);
- * boolean pred(V, A);
- * V body(V, A);
- * V doWhileLoop(A a) {
- * V v = init(a);
+ * 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));
+ * v = body(v, a...);
+ * } while (pred(v, a...));
* return v;
* }
* }
@@ -4507,303 +4891,656 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* }
*
* - * @implSpec The implementation of this method is equivalent to: + * @apiNote The implementation of this method can be expressed as follows: *
{@code
* MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
- * MethodHandle[] clause = { init, body, pred, identity(init.type().returnType()) };
+ * MethodHandle fini = (body.type().returnType() == void.class
+ * ? null : identity(body.type().returnType()));
+ * MethodHandle[] clause = { init, body, pred, fini };
* return loop(clause);
* }
* }
*
+ * @param init optional initializer, providing the initial value of the loop variable.
+ * May be {@code null}, implying a default initial value. See above for other constraints.
+ * @param body body of the loop, which may not be {@code null}. It controls the loop parameters and result type.
+ * See above for other constraints.
+ * @param pred condition for the loop, which may not be {@code null}. Its result type must be {@code boolean}. See
+ * above for other constraints.
*
- * @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 a method handle implementing the {@code while} loop as described by the arguments.
+ * @throws IllegalArgumentException if the rules for the arguments are violated.
+ * @throws NullPointerException if {@code pred} or {@code body} are {@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[][])
+ * @see #loop(MethodHandle[][])
+ * @see #whileLoop(MethodHandle, MethodHandle, MethodHandle)
* @since 9
*/
public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
- MethodHandle fin = init == null || init.type().returnType() == void.class ? zero(void.class) :
- identity(init.type().returnType());
- MethodHandle[] clause = {init, body, pred, fin};
+ whileLoopChecks(init, pred, body);
+ MethodHandle fini = identityOrVoid(body.type().returnType());
+ MethodHandle[] clause = {init, body, pred, fini };
return loop(clause);
}
+ private static void whileLoopChecks(MethodHandle init, MethodHandle pred, MethodHandle body) {
+ Objects.requireNonNull(pred);
+ Objects.requireNonNull(body);
+ MethodType bodyType = body.type();
+ Class> returnType = bodyType.returnType();
+ List- * 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}. + * The number of iterations is determined by the {@code iterations} handle evaluation result. + * The loop counter {@code i} is an extra loop iteration variable of type {@code int}. + * It will be initialized to 0 and incremented by 1 in each iteration. + *
+ * If the {@code body} handle returns a non-{@code void} type {@code V}, a leading loop iteration variable + * of that type is also present. This variable is initialized using the optional {@code init} handle, + * or to the {@linkplain #empty default value} of type {@code V} if that handle is {@code null}. + *
+ * In each iteration, the iteration variables are passed to an invocation of the {@code body} handle. + * A non-{@code void} value returned from the body (of type {@code V}) updates the leading + * iteration variable. + * The result of the loop handle execution will be the final {@code V} value of that variable + * (or {@code void} if there is no {@code V} variable). + *
+ * The following rules hold for the argument handles:
+ * The resulting loop handle's result type and parameter signature are determined as follows:
* Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of - * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument - * passed to the loop. + * the second loop variable as well as the result type of the loop; and {@code A...}/{@code a...} represent + * arguments passed to the loop. *
{@code
- * int iterations(A);
- * V init(A);
- * V body(int, V, A);
- * V countedLoop(A a) {
- * int end = iterations(a);
- * V v = init(a);
+ * int iterations(A...);
+ * V init(A...);
+ * V body(V, int, 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);
+ * v = body(v, i, a...);
* }
* return v;
* }
* }
* - * @apiNote Example: + * @apiNote Example with a fully conformant body method: *
{@code
* // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
* // => a variation on a well known theme
- * static String start(String arg) { return arg; }
- * static String step(int counter, String v, String arg) { return "na " + v; }
- * // assume MH_start and MH_step are handles to the two methods above
+ * static String step(String v, int counter, String init) { return "na " + v; }
+ * // assume MH_step is a handle to the method above
* MethodHandle fit13 = MethodHandles.constant(int.class, 13);
- * MethodHandle loop = MethodHandles.countedLoop(fit13, MH_start, MH_step);
+ * MethodHandle start = MethodHandles.identity(String.class);
+ * MethodHandle loop = MethodHandles.countedLoop(fit13, start, MH_step);
* assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
* }
- *
* - * @implSpec The implementation of this method is equivalent to: + * @apiNote Example with the simplest possible body method type, + * and passing the number of iterations to the loop invocation: + *
{@code
+ * // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
+ * // => a variation on a well known theme
+ * static String step(String v, int counter ) { return "na " + v; }
+ * // assume MH_step is a handle to the method above
+ * MethodHandle count = MethodHandles.dropArguments(MethodHandles.identity(int.class), 1, String.class);
+ * MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class);
+ * MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step); // (v, i) -> "na " + v
+ * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "Lambdaman!"));
+ * }
+ * + * @apiNote Example that treats the number of iterations, string to append to, and string to append + * as loop parameters: + *
{@code
+ * // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
+ * // => a variation on a well known theme
+ * static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; }
+ * // assume MH_step is a handle to the method above
+ * MethodHandle count = MethodHandles.identity(int.class);
+ * MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class, String.class);
+ * MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step); // (v, i, _, pre, _) -> pre + " " + v
+ * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "na", "Lambdaman!"));
+ * }
+ * + * @apiNote Example that illustrates the usage of {@link #dropArgumentsToMatch(MethodHandle, int, List, int)} + * to enforce a loop type: + *
{@code
+ * // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
+ * // => a variation on a well known theme
+ * static String step(String v, int counter, String pre) { return pre + " " + v; }
+ * // assume MH_step is a handle to the method above
+ * MethodType loopType = methodType(String.class, String.class, int.class, String.class);
+ * MethodHandle count = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(int.class), 0, loopType.parameterList(), 1);
+ * MethodHandle start = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(String.class), 0, loopType.parameterList(), 2);
+ * MethodHandle body = MethodHandles.dropArgumentsToMatch(MH_step, 2, loopType.parameterList(), 0);
+ * MethodHandle loop = MethodHandles.countedLoop(count, start, body); // (v, i, pre, _, _) -> pre + " " + v
+ * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("na", 13, "Lambdaman!"));
+ * }
+ * + * @apiNote The implementation of this method can be expressed as follows: *
{@code
* MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
- * return countedLoop(null, iterations, init, body); // null => constant zero
+ * return countedLoop(empty(iterations.type()), iterations, init, body);
* }
* }
*
- * @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.
+ * @param iterations a non-{@code null} handle to return the number of iterations this loop should run. The handle's
+ * result type must be {@code int}. See above for other constraints.
+ * @param init optional initializer, providing the initial value of the loop variable.
+ * May be {@code null}, implying a default initial value. See above for other constraints.
+ * @param body body of the loop, which may not be {@code null}.
+ * It controls the loop parameters and result type in the standard case (see above for details).
+ * It must accept its own return type (if non-void) plus an {@code int} parameter (for the counter),
+ * and may accept any number of additional types.
+ * See above for other constraints.
*
* @return a method handle representing the loop.
- * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ * @throws NullPointerException if either of the {@code iterations} or {@code body} handles is {@code null}.
+ * @throws IllegalArgumentException if any argument violates the rules formulated above.
*
+ * @see #countedLoop(MethodHandle, MethodHandle, MethodHandle, MethodHandle)
* @since 9
*/
public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
- return countedLoop(null, iterations, init, body);
+ return countedLoop(empty(iterations.type()), 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}.
+ * Constructs a loop that counts over a range of numbers.
+ * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
* - * 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}. + * The loop counter {@code i} is a loop iteration variable of type {@code int}. + * The {@code start} and {@code end} handles determine the start (inclusive) and end (exclusive) + * values of the loop counter. + * The loop counter 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. + *
+ * If the {@code body} handle returns a non-{@code void} type {@code V}, a leading loop iteration variable + * of that type is also present. This variable is initialized using the optional {@code init} handle, + * or to the {@linkplain #empty default value} of type {@code V} if that handle is {@code null}. + *
+ * In each iteration, the iteration variables are passed to an invocation of the {@code body} handle. + * A non-{@code void} value returned from the body (of type {@code V}) updates the leading + * iteration variable. + * The result of the loop handle execution will be the final {@code V} value of that variable + * (or {@code void} if there is no {@code V} variable). + *
+ * The following rules hold for the argument handles:
+ * The resulting loop handle's result type and parameter signature are determined as follows:
* Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of - * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument - * passed to the loop. + * the second loop variable as well as the result type of the loop; and {@code A...}/{@code a...} represent + * arguments passed to the loop. *
{@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);
+ * int start(A...);
+ * int end(A...);
+ * V init(A...);
+ * V body(V, int, A...);
+ * V countedLoop(A... a...) {
+ * int e = end(a...);
+ * int s = start(a...);
+ * V v = init(a...);
* for (int i = s; i < e; ++i) {
- * v = body(i, v, a);
+ * v = body(v, i, a...);
* }
* return v;
* }
* }
*
* - * @implSpec The implementation of this method is equivalent to: + * @apiNote The implementation of this method can be expressed as follows: *
{@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 counter + 1
+ * // MH_predicate: (int limit, int counter) -> counter < limit
+ * Class> counterType = start.type().returnType(); // int
+ * Class> returnType = body.type().returnType();
+ * MethodHandle incr = MH_increment, pred = MH_predicate, retv = null;
+ * if (returnType != void.class) { // ignore the V variable
+ * incr = dropArguments(incr, 1, returnType); // (limit, v, i) => (limit, i)
+ * pred = dropArguments(pred, 1, returnType); // ditto
+ * retv = dropArguments(identity(returnType), 0, counterType); // ignore limit
+ * }
+ * body = dropArguments(body, 0, counterType); // ignore the limit variable
* MethodHandle[]
- * indexVar = {start, MH_increment}, // i = start; i = i+1
- * loopLimit = {end, null,
- * filterArgument(MH_lessThan, 0, MH_decrement), returnVar}, // i-1
*
- * @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.
+ * @param start a non-{@code null} handle to return the start value of the loop counter, which must be {@code int}.
+ * See above for other constraints.
+ * @param end a non-{@code null} handle to return the end value of the loop counter (the loop will run to
+ * {@code end-1}). The result type must be {@code int}. See above for other constraints.
+ * @param init optional initializer, providing the initial value of the loop variable.
+ * May be {@code null}, implying a default initial value. See above for other constraints.
+ * @param body body of the loop, which may not be {@code null}.
+ * It controls the loop parameters and result type in the standard case (see above for details).
+ * It must accept its own return type (if non-void) plus an {@code int} parameter (for the counter),
+ * and may accept any number of additional types.
+ * See above for other constraints.
*
* @return a method handle representing the loop.
- * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ * @throws NullPointerException if any of the {@code start}, {@code end}, or {@code body} handles is {@code null}.
+ * @throws IllegalArgumentException if any argument violates the rules formulated above.
*
+ * @see #countedLoop(MethodHandle, MethodHandle, MethodHandle)
* @since 9
*/
public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
- Class> resultType;
- MethodHandle actualInit;
- if (init == null) {
- resultType = body == null ? void.class : body.type().returnType();
- actualInit = empty(methodType(resultType));
- } else {
- resultType = init.type().returnType();
- actualInit = init;
+ countedLoopChecks(start, end, init, body);
+ Class> counterType = start.type().returnType(); // int, but who's counting?
+ Class> limitType = end.type().returnType(); // yes, int again
+ Class> returnType = body.type().returnType();
+ MethodHandle incr = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep);
+ MethodHandle pred = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred);
+ MethodHandle retv = null;
+ if (returnType != void.class) {
+ incr = dropArguments(incr, 1, returnType); // (limit, v, i) => (limit, i)
+ pred = dropArguments(pred, 1, returnType); // ditto
+ retv = dropArguments(identity(returnType), 0, counterType);
+ }
+ body = dropArguments(body, 0, counterType); // ignore the limit variable
+ MethodHandle[]
+ loopLimit = { end, null, pred, retv }, // limit = end(); i < limit || return v
+ bodyClause = { init, body }, // v = init(); v = body(v, i)
+ indexVar = { start, incr }; // i = start(); i = i + 1
+ return loop(loopLimit, bodyClause, indexVar);
+ }
+
+ private static void countedLoopChecks(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
+ Objects.requireNonNull(start);
+ Objects.requireNonNull(end);
+ Objects.requireNonNull(body);
+ Class> counterType = start.type().returnType();
+ if (counterType != int.class) {
+ MethodType expected = start.type().changeReturnType(int.class);
+ throw misMatchedTypes("start function", start.type(), expected);
+ } else if (end.type().returnType() != counterType) {
+ MethodType expected = end.type().changeReturnType(counterType);
+ throw misMatchedTypes("end function", end.type(), expected);
+ }
+ MethodType bodyType = body.type();
+ Class> returnType = bodyType.returnType();
+ List- * 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. + * The iterator itself will be determined by the evaluation of the {@code iterator} handle. + * Each value it produces will be stored in a loop iteration variable of type {@code T}. + *
+ * If the {@code body} handle returns a non-{@code void} type {@code V}, a leading loop iteration variable + * of that type is also present. This variable is initialized using the optional {@code init} handle, + * or to the {@linkplain #empty default value} of type {@code V} if that handle is {@code null}. + *
+ * In each iteration, the iteration variables are passed to an invocation of the {@code body} handle. + * A non-{@code void} value returned from the body (of type {@code V}) updates the leading + * iteration variable. + * The result of the loop handle execution will be the final {@code V} value of that variable + * (or {@code void} if there is no {@code V} variable). + *
+ * The following rules hold for the argument handles:
+ * The type {@code T} may be either a primitive or reference.
+ * Since type {@code Iterator
+ * The resulting loop handle's result type and parameter signature are determined as follows:
* Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of
* the loop variable as well as the result type of the loop; {@code T}/{@code t}, that of the elements of the
- * structure the loop iterates over, and {@code A}/{@code a}, that of the argument passed to the loop.
+ * structure the loop iterates over, and {@code A...}/{@code a...} represent arguments passed to the loop.
*
- * The type {@code T} may be either a primitive or reference.
- * Since type {@code Iterator
* @apiNote Example:
*
- * @implSpec The implementation of this method is equivalent to (excluding error handling):
+ * @apiNote The implementation of this method can be expressed approximately as follows:
*
- * 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.
- *
- * @apiNote Example:
- * Here is pseudocode for the resulting adapter. In the code, {@code T}
- * represents the result type of the {@code target} and resulting adapter.
- * {@code V}/{@code v} represent the type and value of the parameter and argument
- * of {@code target} that precedes the folding position; {@code V} also is
- * the result type of the {@code combiner}. {@code A}/{@code a} denote the
- * types and values of the {@code N} parameters and arguments at the folding
- * position. {@code Z}/{@code z} and {@code B}/{@code b} represent the types
- * and values of the {@code target} parameters and arguments that precede and
- * follow the folded parameters and arguments starting at {@code pos},
- * respectively.
- *
- * Note: The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
- * variable-arity method handle}, even if the original target method handle was.
- *
- * @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)
- * @since 9
- */
- 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;
- }
-
- /**
- * As {@see foldArguments(MethodHandle, int, MethodHandle)}, but with the
- * added capability of selecting the arguments from the targets parameters
- * to call the combiner with. This allows us to avoid some simple cases of
- * permutations and padding the combiner with dropArguments to select the
- * right argument, which may ultimately produce fewer intermediaries.
- */
- static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner, int ... argPositions) {
- MethodType targetType = target.type();
- MethodType combinerType = combiner.type();
- Class> rtype = foldArgumentChecks(pos, targetType, combinerType, argPositions);
- BoundMethodHandle result = target.rebind();
- boolean dropResult = rtype == void.class;
- LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType(), argPositions);
- MethodType newType = targetType;
- if (!dropResult) {
- newType = newType.dropParameterTypes(pos, pos + 1);
- }
- result = result.copyWithExtendL(newType, lform, combiner);
- return result;
- }
-
- private static void checkLoop0(MethodHandle[][] clauses) {
- if (clauses == null || clauses.length == 0) {
- throw newIllegalArgumentException("null or no clauses passed");
- }
- if (Stream.of(clauses).anyMatch(Objects::isNull)) {
- throw newIllegalArgumentException("null clauses are not allowed");
- }
- if (Stream.of(clauses).anyMatch(c -> c.length > 4)) {
- throw newIllegalArgumentException("All loop clauses must be represented as MethodHandle arrays with at most 4 elements.");
- }
- }
-
- private static void checkLoop1a(int i, MethodHandle in, MethodHandle st) {
- if (in.type().returnType() != st.type().returnType()) {
- throw misMatchedTypes("clause " + i + ": init and step return types", in.type().returnType(),
- st.type().returnType());
- }
- }
-
- private static List
+ *
*
* {@code
- * Iterator
* {@code
- * // reverse a list
- * static List
*
- * @param iterator a handle to return the iterator to start the loop.
- * The handle must have {@link java.util.Iterator} as its return type.
- * 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.
+ * @param iterator an optional handle to return the iterator to start the loop.
+ * If non-{@code null}, the handle must return {@link java.util.Iterator} or a subtype.
+ * See above for other constraints.
+ * @param init optional initializer, providing the initial value of the loop variable.
+ * May be {@code null}, implying a default initial value. See above for other constraints.
+ * @param body body of the loop, which may not be {@code null}.
+ * It controls the loop parameters and result type in the standard case (see above for details).
+ * It must accept its own return type (if non-void) plus a {@code T} parameter (for the iterated values),
+ * and may accept any number of additional types.
+ * See above for other constraints.
*
* @return a method handle embodying the iteration loop functionality.
- * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+ * @throws NullPointerException if the {@code body} handle is {@code null}.
+ * @throws IllegalArgumentException if any argument violates the above requirements.
*
* @since 9
*/
public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
- checkIteratedLoop(iterator, body);
- Class> resultType = init == null ?
- body == null ? void.class : body.type().returnType() :
- init.type().returnType();
- boolean voidResult = resultType == void.class;
+ Class> iterableType = iteratedLoopChecks(iterator, init, body);
+ Class> returnType = body.type().returnType();
+ MethodHandle hasNext = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iteratePred);
+ MethodHandle nextRaw = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext);
+ MethodHandle startIter;
+ MethodHandle nextVal;
+ {
+ MethodType iteratorType;
+ if (iterator == null) {
+ // derive argument type from body, if available, else use Iterable
+ startIter = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator);
+ iteratorType = startIter.type().changeParameterType(0, iterableType);
+ } else {
+ // force return type to the internal iterator class
+ iteratorType = iterator.type().changeReturnType(Iterator.class);
+ startIter = iterator;
+ }
+ Class> ttype = body.type().parameterType(returnType == void.class ? 0 : 1);
+ MethodType nextValType = nextRaw.type().changeReturnType(ttype);
- MethodHandle initIterator;
- if (iterator == null) {
- MethodHandle initit = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator);
- initIterator = initit.asType(initit.type().changeParameterType(0,
- body.type().parameterType(voidResult ? 1 : 2)));
- } else {
- initIterator = iterator.asType(iterator.type().changeReturnType(Iterator.class));
+ // perform the asType transforms under an exception transformer, as per spec.:
+ try {
+ startIter = startIter.asType(iteratorType);
+ nextVal = nextRaw.asType(nextValType);
+ } catch (WrongMethodTypeException ex) {
+ throw new IllegalArgumentException(ex);
+ }
}
- Class> ttype = body.type().parameterType(0);
-
- MethodHandle returnVar =
- dropArguments(voidResult ? zero(void.class) : identity(resultType), 0, Iterator.class);
- MethodHandle initnx = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext);
- MethodHandle nextVal = initnx.asType(initnx.type().changeReturnType(ttype));
-
- MethodHandle[] iterVar = {initIterator, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iteratePred),
- returnVar};
- MethodHandle[] bodyClause = {init, filterArgument(body, 0, nextVal)};
+ MethodHandle retv = null, step = body;
+ if (returnType != void.class) {
+ // the simple thing first: in (I V A...), drop the I to get V
+ retv = dropArguments(identity(returnType), 0, Iterator.class);
+ // body type signature (V T A...), internal loop types (I V A...)
+ step = swapArguments(body, 0, 1); // swap V <-> T
+ }
+ MethodHandle[]
+ iterVar = { startIter, null, hasNext, retv },
+ bodyClause = { init, filterArgument(step, 0, nextVal) };
return loop(iterVar, bodyClause);
}
+ private static Class> iteratedLoopChecks(MethodHandle iterator, MethodHandle init, MethodHandle body) {
+ Objects.requireNonNull(body);
+ MethodType bodyType = body.type();
+ Class> returnType = bodyType.returnType();
+ List{@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);
+ * // assume MH_next, MH_hasNext, MH_startIter are handles to methods of Iterator/Iterable
+ * Class> returnType = body.type().returnType();
+ * Class> ttype = body.type().parameterType(returnType == void.class ? 0 : 1);
* MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
+ * MethodHandle retv = null, step = body, startIter = iterator;
+ * if (returnType != void.class) {
+ * // the simple thing first: in (I V A...), drop the I to get V
+ * retv = dropArguments(identity(returnType), 0, Iterator.class);
+ * // body type signature (V T A...), internal loop types (I V A...)
+ * step = swapArguments(body, 0, 1); // swap V <-> T
+ * }
+ * if (startIter == null) startIter = MH_getIter;
* MethodHandle[]
- * iterVar = {iterator, null, MH_hasNext, returnVar}, // it = iterator(); while (it.hasNext)
- * bodyClause = {init, filterArgument(body, 0, nextVal)}; // v = body(t, v, a);
+ * iterVar = { startIter, null, MH_hasNext, retv }, // it = iterator; while (it.hasNext())
+ * bodyClause = { init, filterArguments(step, 0, nextVal) }; // v = body(v, t, a)
* return loop(iterVar, bodyClause);
* }
* }
- * {@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"));
- * }
- * {@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...);
- * }
- * }