changeset 41207:813a335bcb0c

8151179: address issues raised by JCK team on JEP 274 API Reviewed-by: jrose, redestad, psandoz
author mhaupt
date Wed, 28 Sep 2016 14:02:21 +0200
parents 591cd107586c
children 8a97b5704e66
files jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java jdk/test/java/lang/invoke/CountedLoopIterationCountsTest.java jdk/test/java/lang/invoke/JavaDocExamplesTest.java jdk/test/java/lang/invoke/LoopCombinatorTest.java
diffstat 6 files changed, 1731 insertions(+), 766 deletions(-) [+]
line wrap: on
line diff
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 28 03:18:01 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 28 14:02:21 2016 +0200
@@ -1962,12 +1962,12 @@
      * This method is bound as the predicate in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle,
      * MethodHandle) counting loops}.
      *
+     * @param limit the upper bound of the parameter, statically bound at loop creation time.
      * @param counter the counter parameter, passed in during loop execution.
-     * @param limit the upper bound of the parameter, statically bound at loop creation time.
      *
      * @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,27 +1975,16 @@
      * 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}.
      *
      * @param it the {@link Iterable} over which the loop iterates.
@@ -2164,12 +2153,11 @@
             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 @@
                 case MH_countedLoopStep:
                     return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopStep",
                             MethodType.methodType(int.class, int.class, int.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_initIterator:
-                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "initIterator",
-                            MethodType.methodType(Iterator.class, Iterable.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));
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 28 03:18:01 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 28 14:02:21 2016 +0200
@@ -44,8 +44,6 @@
 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 @@
      * @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 @@
                 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 @@
             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 @@
         // 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[])}.
+     * <p>
+     * The resulting handle will have the same return type as the target handle.
+     * <p>
+     * In more formal terms, assume these two type lists:<ul>
+     * <li>The target handle has the parameter type list {@code S..., M...}, with as many types in {@code S} as
+     * indicated by {@code skip}. The {@code M} types are those that are supposed to match part of the given type list,
+     * {@code newTypes}.
+     * <li>The {@code newTypes} list contains types {@code P..., M..., A...}, with as many types in {@code P} as
+     * indicated by {@code pos}. The {@code M} types are precisely those that the {@code M} types in the target handle's
+     * parameter type list are supposed to match. The types in {@code A} are additional types found after the matching
+     * sub-list.
+     * </ul>
+     * Given these assumptions, the result of an invocation of {@code dropArgumentsToMatch} will have the parameter type
+     * list {@code S..., P..., M..., A...}, with the {@code P} and {@code A} types inserted as if by
+     * {@link #dropArguments(MethodHandle, int, Class[])}.
+     * <p>
      * @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:
      * <blockquote><pre>{@code
 import static java.lang.invoke.MethodHandles.*;
 import static java.lang.invoke.MethodType.*;
@@ -3461,14 +3472,15 @@
      * }</pre></blockquote>
      * @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 @@
         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.
+     * <p>
+     * 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.
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@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"));
+     * }</pre></blockquote>
+     * <p>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.
+     * <blockquote><pre>{@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...);
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * <em>Note:</em> 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 @@
      * 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.
      * <p>
-     * 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 <em>iteration variable</em> 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:<ul>
-     * <li>Before the loop executes, the initialization of an iteration variable or loop invariant local.
-     * <li>When a clause executes, an update step for the iteration variable.
-     * <li>When a clause executes, a predicate execution to test for loop exit.
-     * <li>If a clause causes a loop exit, a finalizer execution to compute the loop's return value.
+     * terms of method handles, each clause will specify up to four independent actions:<ul>
+     * <li><em>init:</em> Before the loop executes, the initialization of an iteration variable {@code v} of type {@code V}.
+     * <li><em>step:</em> When a clause executes, an update step for the iteration variable {@code v}.
+     * <li><em>pred:</em> When a clause executes, a predicate execution to test for loop exit.
+     * <li><em>fini:</em> If a clause causes a loop exit, a finalizer execution to compute the loop's return value.
      * </ul>
+     * The full sequence of all iteration variable types, in clause order, will be notated as {@code (V...)}.
+     * The values themselves will be {@code (v...)}.  When we speak of "parameter lists", we will usually
+     * be referring to types, but in some contexts (describing execution) the lists will be of actual values.
      * <p>
      * 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.
      * <p>
-     * Each clause function, with the exception of clause initializers, is able to observe the entire loop state,
-     * because it will be passed <em>all</em> 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}.
+     * <em>Parameters optional everywhere:</em>
+     * 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.
      * <p>
+     * <em>Loop parameters:</em>
+     * 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 <em>loop parameters</em>,
+     * 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.
+     * <p>
+     * <em>Parameters visible everywhere:</em>
+     * 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}.
+     * <a name="astar"></a>
+     * 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*)}.
+     * <p>
+     * <em>Checking clause structure:</em>
      * 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.
+     * <p>
+     * <em>Effectively identical sequences:</em>
+     * <a name="effid"></a>
+     * A parameter list {@code A} is defined to be <em>effectively identical</em> 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.
      * <p>
      * <em>Step 0: Determine clause structure.</em><ol type="a">
-     * <li>The clause array (of type {@code MethodHandle[][]} must be non-{@code null} and contain at least one element.
+     * <li>The clause array (of type {@code MethodHandle[][]}) must be non-{@code null} and contain at least one element.
      * <li>The clause array may not contain {@code null}s or sub-arrays longer than four elements.
      * <li>Clauses shorter than four elements are treated as if they were padded by {@code null} elements to length
      * four. Padding takes place by appending elements to the array.
@@ -4158,30 +4314,35 @@
      * <li>Each clause is treated as a four-tuple of functions, called "init", "step", "pred", and "fini".
      * </ol>
      * <p>
-     * <em>Step 1A: Determine iteration variables.</em><ol type="a">
-     * <li>Examine init and step function return types, pairwise, to determine each clause's iteration variable type.
-     * <li>If both functions are omitted, use {@code void}; else if one is omitted, use the other's return type; else
-     * use the common return type (they must be identical).
+     * <em>Step 1A: Determine iteration variable types {@code (V...)}.</em><ol type="a">
+     * <li>The iteration variable type for each clause is determined using the clause's init and step return types.
+     * <li>If both functions are omitted, there is no iteration variable for the corresponding clause ({@code void} is
+     * used as the type to indicate that). If one of them is omitted, the other's return type defines the clause's
+     * iteration variable type. If both are given, the common return type (they must be identical) defines the clause's
+     * iteration variable type.
      * <li>Form the list of return types (in clause order), omitting all occurrences of {@code void}.
-     * <li>This list of types is called the "common prefix".
+     * <li>This list of types is called the "iteration variable types" ({@code (V...)}).
      * </ol>
      * <p>
-     * <em>Step 1B: Determine loop parameters.</em><ul>
-     * <li><b>If at least one init function is given,</b><ol type="a">
-     *   <li>Examine init function parameter lists.
-     *   <li>Omitted init functions are deemed to have {@code null} parameter lists.
-     *   <li>All init function parameter lists must be effectively identical.
-     *   <li>The longest parameter list (which is necessarily unique) is called the "common suffix".
-     * </ol>
-     * <li><b>If no init function is given,</b><ol type="a">
-     *   <li>Examine the suffixes of the step, pred, and fini parameter lists, after removing the "common prefix".
-     *   <li>The longest of these suffixes is taken as the "common suffix".
-     * </ol></ul>
+     * <em>Step 1B: Determine loop parameters {@code (A...)}.</em><ul>
+     * <li>Examine and collect init function parameter lists (which are of the form {@code (A*)}).
+     * <li>Examine and collect the suffixes of the step, pred, and fini parameter lists, after removing the iteration variable types.
+     * (They must have the form {@code (V... A*)}; collect the {@code (A*)} parts only.)
+     * <li>Do not collect suffixes from step, pred, and fini parameter lists that do not begin with all the iteration variable types.
+     * (These types will checked in step 2, along with all the clause function types.)
+     * <li>Omitted clause functions are ignored.  (Equivalently, they are deemed to have empty parameter lists.)
+     * <li>All of the collected parameter lists must be effectively identical.
+     * <li>The longest parameter list (which is necessarily unique) is called the "external parameter list" ({@code (A...)}).
+     * <li>If there is no such parameter list, the external parameter list is taken to be the empty sequence.
+     * <li>The combined list consisting of iteration variable types followed by the external parameter types is called
+     * the "internal parameter list".
+     * </ul>
      * <p>
      * <em>Step 1C: Determine loop return type.</em><ol type="a">
      * <li>Examine fini function return types, disregarding omitted fini functions.
-     * <li>If there are no fini functions, use {@code void} as the loop return type.
-     * <li>Otherwise, use the common return type of the fini functions; they must all be identical.
+     * <li>If there are no fini functions, the loop return type is {@code void}.
+     * <li>Otherwise, the common return type {@code R} of the fini functions (their return types must be identical) defines the loop return
+     * type.
      * </ol>
      * <p>
      * <em>Step 1D: Check other types.</em><ol type="a">
@@ -4190,69 +4351,107 @@
      * </ol>
      * <p>
      * <em>Step 2: Determine parameter lists.</em><ol type="a">
-     * <li>The parameter list for the resulting loop handle will be the "common suffix".
-     * <li>The parameter list for init functions will be adjusted to the "common suffix". (Note that their parameter
-     * lists are already effectively identical to the common suffix.)
-     * <li>The parameter list for non-init (step, pred, and fini) functions will be adjusted to the common prefix
-     * followed by the common suffix, called the "common parameter sequence".
-     * <li>Every non-init, non-omitted function parameter list must be effectively identical to the common parameter
-     * sequence.
+     * <li>The parameter list for the resulting loop handle will be the external parameter list {@code (A...)}.
+     * <li>The parameter list for init functions will be adjusted to the external parameter list.
+     * (Note that their parameter lists are already effectively identical to this list.)
+     * <li>The parameter list for every non-omitted, non-init (step, pred, and fini) function must be
+     * effectively identical to the internal parameter list {@code (V... A...)}.
      * </ol>
      * <p>
      * <em>Step 3: Fill in omitted functions.</em><ol type="a">
-     * <li>If an init function is omitted, use a {@linkplain #constant constant function} of the appropriate
-     * {@code null}/zero/{@code false}/{@code void} type. (For this purpose, a constant {@code void} is simply a
-     * function which does nothing and returns {@code void}; it can be obtained from another constant function by
-     * {@linkplain MethodHandle#asType type conversion}.)
+     * <li>If an init function is omitted, use a {@linkplain #empty default value} for the clause's iteration variable
+     * type.
      * <li>If a step function is omitted, use an {@linkplain #identity identity function} of the clause's iteration
      * variable type; insert dropped argument parameters before the identity function parameter for the non-{@code void}
      * iteration variables of preceding clauses. (This will turn the loop variable into a local loop invariant.)
-     * <li>If a pred function is omitted, the corresponding fini function must also be omitted.
      * <li>If a pred function is omitted, use a constant {@code true} function. (This will keep the loop going, as far
-     * as this clause is concerned.)
-     * <li>If a fini function is omitted, use a constant {@code null}/zero/{@code false}/{@code void} function of the
+     * as this clause is concerned.  Note that in such cases the corresponding fini function is unreachable.)
+     * <li>If a fini function is omitted, use a {@linkplain #empty default value} for the
      * loop return type.
      * </ol>
      * <p>
      * <em>Step 4: Fill in missing parameter types.</em><ol type="a">
-     * <li>At this point, every init function parameter list is effectively identical to the common suffix, but some
-     * lists may be shorter. For every init function with a short parameter list, pad out the end of the list by
-     * {@linkplain #dropArguments dropping arguments}.
-     * <li>At this point, every non-init function parameter list is effectively identical to the common parameter
-     * sequence, but some lists may be shorter. For every non-init function with a short parameter list, pad out the end
-     * of the list by {@linkplain #dropArguments dropping arguments}.
+     * <li>At this point, every init function parameter list is effectively identical to the external parameter list {@code (A...)},
+     * but some lists may be shorter. For every init function with a short parameter list, pad out the end of the list.
+     * <li>At this point, every non-init function parameter list is effectively identical to the internal parameter
+     * list {@code (V... A...)}, but some lists may be shorter. For every non-init function with a short parameter list,
+     * pad out the end of the list.
+     * <li>Argument lists are padded out by {@linkplain #dropArgumentsToMatch dropping unused trailing arguments}.
      * </ol>
      * <p>
      * <em>Final observations.</em><ol type="a">
      * <li>After these steps, all clauses have been adjusted by supplying omitted functions and arguments.
-     * <li>All init functions have a common parameter type list, which the final loop handle will also have.
-     * <li>All fini functions have a common return type, which the final loop handle will also have.
-     * <li>All non-init functions have a common parameter type list, which is the common parameter sequence, of
-     * (non-{@code void}) iteration variables followed by loop parameters.
-     * <li>Each pair of init and step functions agrees in their return types.
-     * <li>Each non-init function will be able to observe the current values of all iteration variables, by means of the
-     * common prefix.
+     * <li>All init functions have a common parameter type list {@code (A...)}, which the final loop handle will also have.
+     * <li>All fini functions have a common return type {@code R}, which the final loop handle will also have.
+     * <li>All non-init functions have a common parameter type list {@code (V... A...)}, of
+     * (non-{@code void}) iteration variables {@code V} followed by loop parameters.
+     * <li>Each pair of init and step functions agrees in their return type {@code V}.
+     * <li>Each non-init function will be able to observe the current values {@code (v...)} of all iteration variables.
+     * <li>Every function will be able to observe the incoming values {@code (a...)} of all loop parameters.
      * </ol>
      * <p>
+     * <em>Example.</em> As a consequence of step 1A above, the {@code loop} combinator has the following property:
+     * <ul>
+     * <li>Given {@code N} clauses {@code Cn = {null, Sn, Pn}} with {@code n = 1..N}.
+     * <li>Suppose predicate handles {@code Pn} are either {@code null} or have no parameters.
+     * (Only one {@code Pn} has to be non-{@code null}.)
+     * <li>Suppose step handles {@code Sn} have signatures {@code (B1..BX)Rn}, for some constant {@code X>=N}.
+     * <li>Suppose {@code Q} is the count of non-void types {@code Rn}, and {@code (V1...VQ)} is the sequence of those types.
+     * <li>It must be that {@code Vn == Bn} for {@code n = 1..min(X,Q)}.
+     * <li>The parameter types {@code Vn} will be interpreted as loop-local state elements {@code (V...)}.
+     * <li>Any remaining types {@code BQ+1..BX} (if {@code Q<X}) will determine
+     * the resulting loop handle's parameter types {@code (A...)}.
+     * </ul>
+     * In this example, the loop handle parameters {@code (A...)} were derived from the step functions,
+     * which is natural if most of the loop computation happens in the steps.  For some loops,
+     * the burden of computation might be heaviest in the pred functions, and so the pred functions
+     * might need to accept the loop parameter values.  For loops with complex exit logic, the fini
+     * functions might need to accept loop parameters, and likewise for loops with complex entry logic,
+     * where the init functions will need the extra parameters.  For such reasons, the rules for
+     * determining these parameters are as symmetric as possible, across all clause parts.
+     * In general, the loop parameters function as common invariant values across the whole
+     * loop, while the iteration variables function as common variant values, or (if there is
+     * no step function) as internal loop invariant temporaries.
+     * <p>
      * <em>Loop execution.</em><ol type="a">
-     * <li>When the loop is called, the loop input values are saved in locals, to be passed (as the common suffix) to
+     * <li>When the loop is called, the loop input values are saved in locals, to be passed to
      * every clause function. These locals are loop invariant.
-     * <li>Each init function is executed in clause order (passing the common suffix) and the non-{@code void} values
-     * are saved (as the common prefix) into locals. These locals are loop varying (unless their steps are identity
-     * functions, as noted above).
-     * <li>All function executions (except init functions) will be passed the common parameter sequence, consisting of
-     * the non-{@code void} iteration values (in clause order) and then the loop inputs (in argument order).
+     * <li>Each init function is executed in clause order (passing the external arguments {@code (a...)})
+     * and the non-{@code void} values are saved (as the iteration variables {@code (v...)}) into locals.
+     * These locals will be loop varying (unless their steps behave as identity functions, as noted above).
+     * <li>All function executions (except init functions) will be passed the internal parameter list, consisting of
+     * the non-{@code void} iteration values {@code (v...)} (in clause order) and then the loop inputs {@code (a...)}
+     * (in argument order).
      * <li>The step and pred functions are then executed, in clause order (step before pred), until a pred function
      * returns {@code false}.
-     * <li>The non-{@code void} result from a step function call is used to update the corresponding loop variable. The
-     * updated value is immediately visible to all subsequent function calls.
+     * <li>The non-{@code void} result from a step function call is used to update the corresponding value in the
+     * sequence {@code (v...)} of loop variables.
+     * The updated value is immediately visible to all subsequent function calls.
      * <li>If a pred function returns {@code false}, the corresponding fini function is called, and the resulting value
-     * is returned from the loop as a whole.
+     * (of type {@code R}) is returned from the loop as a whole.
+     * <li>If all the pred functions always return true, no fini function is ever invoked, and the loop cannot exit
+     * except by throwing an exception.
      * </ol>
      * <p>
-     * 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.
+     * <em>Usage tips.</em>
+     * <ul>
+     * <li>Although each step function will receive the current values of <em>all</em> the loop variables,
+     * sometimes a step function only needs to observe the current value of its own variable.
+     * In that case, the step function may need to explicitly {@linkplain #dropArguments drop all preceding loop variables}.
+     * This will require mentioning their types, in an expression like {@code dropArguments(step, 0, V0.class, ...)}.
+     * <li>Loop variables are not required to vary; they can be loop invariant.  A clause can create
+     * a loop invariant by a suitable init function with no step, pred, or fini function.  This may be
+     * useful to "wire" an incoming loop argument into the step or pred function of an adjacent loop variable.
+     * <li>If some of the clause functions are virtual methods on an instance, the instance
+     * itself can be conveniently placed in an initial invariant loop "variable", using an initial clause
+     * like {@code new MethodHandle[]{identity(ObjType.class)}}.  In that case, the instance reference
+     * will be the first iteration variable value, and it will be easy to use virtual
+     * methods as clause parts, since all of them will take a leading instance reference matching that value.
+     * </ul>
+     * <p>
+     * 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.
      * <blockquote><pre>{@code
      * V... init...(A...);
      * boolean pred...(V..., A...);
@@ -4270,6 +4469,9 @@
      *   }
      * }
      * }</pre></blockquote>
+     * 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}.
      * <p>
      * @apiNote Example:
      * <blockquote><pre>{@code
@@ -4286,6 +4488,43 @@
      * MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
      * assertEquals(120, loop.invoke(5));
      * }</pre></blockquote>
+     * The same example, dropping arguments and using combinators:
+     * <blockquote><pre>{@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));
+     * }</pre></blockquote>
+     * A similar example, using a helper object to hold a loop parameter:
+     * <blockquote><pre>{@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));
+     * }</pre></blockquote>
      *
      * @param clauses an array of arrays (4-tuples) of {@link MethodHandle}s adhering to the rules described above.
      *
@@ -4301,7 +4540,7 @@
      */
     public static MethodHandle loop(MethodHandle[]... clauses) {
         // Step 0: determine clause structure.
-        checkLoop0(clauses);
+        loopChecks0(clauses);
 
         List<MethodHandle> init = new ArrayList<>();
         List<MethodHandle> step = new ArrayList<>();
@@ -4318,7 +4557,7 @@
         assert Stream.of(init, step, pred, fini).map(List::size).distinct().count() == 1;
         final int nclauses = init.size();
 
-        // Step 1A: determine iteration variables.
+        // Step 1A: determine iteration variables (V...).
         final List<Class<?>> iterationVariableTypes = new ArrayList<>();
         for (int i = 0; i < nclauses; ++i) {
             MethodHandle in = init.get(i);
@@ -4326,7 +4565,7 @@
             if (in == null && st == null) {
                 iterationVariableTypes.add(void.class);
             } else if (in != null && st != null) {
-                checkLoop1a(i, in, st);
+                loopChecks1a(i, in, st);
                 iterationVariableTypes.add(in.type().returnType());
             } else {
                 iterationVariableTypes.add(in == null ? st.type().returnType() : in.type().returnType());
@@ -4335,20 +4574,20 @@
         final List<Class<?>> commonPrefix = iterationVariableTypes.stream().filter(t -> t != void.class).
                 collect(Collectors.toList());
 
-        // Step 1B: determine loop parameters.
+        // Step 1B: determine loop parameters (A...).
         final List<Class<?>> commonSuffix = buildCommonSuffix(init, step, pred, fini, commonPrefix.size());
-        checkLoop1b(init, commonSuffix);
+        loopChecks1b(init, commonSuffix);
 
         // Step 1C: determine loop return type.
         // Step 1D: check other types.
         final Class<?> loopReturnType = fini.stream().filter(Objects::nonNull).map(MethodHandle::type).
                 map(MethodType::returnType).findFirst().orElse(void.class);
-        checkLoop1cd(pred, fini, loopReturnType);
+        loopChecks1cd(pred, fini, loopReturnType);
 
         // Step 2: determine parameter lists.
         final List<Class<?>> commonParameterSequence = new ArrayList<>(commonPrefix);
         commonParameterSequence.addAll(commonSuffix);
-        checkLoop2(step, pred, fini, commonParameterSequence);
+        loopChecks2(step, pred, fini, commonParameterSequence);
 
         // Step 3: fill in omitted functions.
         for (int i = 0; i < nclauses; ++i) {
@@ -4382,6 +4621,79 @@
         return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, finit, fstep, fpred, ffini);
     }
 
+    private static void loopChecks0(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 loopChecks1a(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<Class<?>> longestParameterList(Stream<MethodHandle> mhs, int skipSize) {
+        final List<Class<?>> empty = List.of();
+        final List<Class<?>> longest = mhs.filter(Objects::nonNull).
+                // take only those that can contribute to a common suffix because they are longer than the prefix
+                        map(MethodHandle::type).
+                        filter(t -> t.parameterCount() > skipSize).
+                        map(MethodType::parameterList).
+                        reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty);
+        return longest.size() == 0 ? empty : longest.subList(skipSize, longest.size());
+    }
+
+    private static List<Class<?>> longestParameterList(List<List<Class<?>>> lists) {
+        final List<Class<?>> empty = List.of();
+        return lists.stream().reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty);
+    }
+
+    private static List<Class<?>> buildCommonSuffix(List<MethodHandle> init, List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini, int cpSize) {
+        final List<Class<?>> longest1 = longestParameterList(Stream.of(step, pred, fini).flatMap(List::stream), cpSize);
+        final List<Class<?>> longest2 = longestParameterList(init.stream(), 0);
+        return longestParameterList(Arrays.asList(longest1, longest2));
+    }
+
+    private static void loopChecks1b(List<MethodHandle> init, List<Class<?>> commonSuffix) {
+        if (init.stream().filter(Objects::nonNull).map(MethodHandle::type).
+                anyMatch(t -> !t.effectivelyIdenticalParameters(0, commonSuffix))) {
+            throw newIllegalArgumentException("found non-effectively identical init parameter type lists: " + init +
+                    " (common suffix: " + commonSuffix + ")");
+        }
+    }
+
+    private static void loopChecks1cd(List<MethodHandle> pred, List<MethodHandle> fini, Class<?> loopReturnType) {
+        if (fini.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType).
+                anyMatch(t -> t != loopReturnType)) {
+            throw newIllegalArgumentException("found non-identical finalizer return types: " + fini + " (return type: " +
+                    loopReturnType + ")");
+        }
+
+        if (!pred.stream().filter(Objects::nonNull).findFirst().isPresent()) {
+            throw newIllegalArgumentException("no predicate found", pred);
+        }
+        if (pred.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType).
+                anyMatch(t -> t != boolean.class)) {
+            throw newIllegalArgumentException("predicates must have boolean return type", pred);
+        }
+    }
+
+    private static void loopChecks2(List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini, List<Class<?>> commonParameterSequence) {
+        if (Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).map(MethodHandle::type).
+                anyMatch(t -> !t.effectivelyIdenticalParameters(0, commonParameterSequence))) {
+            throw newIllegalArgumentException("found non-effectively identical parameter type lists:\nstep: " + step +
+                    "\npred: " + pred + "\nfini: " + fini + " (common parameter sequence: " + commonParameterSequence + ")");
+        }
+    }
+
     private static List<MethodHandle> fillParameterTypes(List<MethodHandle> hs, final List<Class<?>> targetParams) {
         return hs.stream().map(h -> {
             int pc = h.type().parameterCount();
@@ -4395,26 +4707,60 @@
     }
 
     /**
-     * Constructs a {@code while} loop from an initializer, a body, and a predicate. This is a convenience wrapper for
-     * the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
+     * Constructs a {@code while} loop from an initializer, a body, and a predicate.
+     * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
      * <p>
-     * 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).
+     * <p>
+     * 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).
+     * <p>
+     * The following rules hold for these argument handles:<ul>
+     * <li>The {@code body} handle must not be {@code null}; its type must be of the form
+     * {@code (V A...)V}, where {@code V} is non-{@code void}, or else {@code (A...)void}.
+     * (In the {@code void} case, we assign the type {@code void} to the name {@code V},
+     * and we will write {@code (V A...)V} with the understanding that a {@code void} type {@code V}
+     * is quietly dropped from the parameter list, leaving {@code (A...)V}.)
+     * <li>The parameter list {@code (V A...)} of the body is called the <em>internal parameter list</em>.
+     * It will constrain the parameter lists of the other loop parts.
+     * <li>If the iteration variable type {@code V} is dropped from the internal parameter list, the resulting shorter
+     * list {@code (A...)} is called the <em>external parameter list</em>.
+     * <li>The body return type {@code V}, if non-{@code void}, determines the type of an
+     * additional state variable of the loop.
+     * The body must both accept and return a value of this type {@code V}.
+     * <li>If {@code init} is non-{@code null}, it must have return type {@code V}.
+     * Its parameter list (of some <a href="MethodHandles.html#astar">form {@code (A*)}</a>) must be
+     * <a href="MethodHandles.html#effid">effectively identical</a>
+     * to the external parameter list {@code (A...)}.
+     * <li>If {@code init} is {@code null}, the loop variable will be initialized to its
+     * {@linkplain #empty default value}.
+     * <li>The {@code pred} handle must not be {@code null}.  It must have {@code boolean} as its return type.
+     * Its parameter list (either empty or of the form {@code (V A*)}) must be
+     * effectively identical to the internal parameter list.
+     * </ul>
+     * <p>
+     * The resulting loop handle's result type and parameter signature are determined as follows:<ul>
+     * <li>The loop handle's result type is the result type {@code V} of the body.
+     * <li>The loop handle's parameter types are the types {@code (A...)},
+     * from the external parameter list.
+     * </ul>
      * <p>
      * 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.
      * <blockquote><pre>{@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 @@
      * }</pre></blockquote>
      *
      * <p>
-     * @implSpec The implementation of this method is equivalent to:
+     * @apiNote The implementation of this method can be expressed as follows:
      * <blockquote><pre>{@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);
      * }
      * }</pre></blockquote>
      *
-     * @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}.
      * <p>
-     * 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.
+     * <p>
+     * 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).
+     * <p>
+     * The following rules hold for these argument handles:<ul>
+     * <li>The {@code body} handle must not be {@code null}; its type must be of the form
+     * {@code (V A...)V}, where {@code V} is non-{@code void}, or else {@code (A...)void}.
+     * (In the {@code void} case, we assign the type {@code void} to the name {@code V},
+     * and we will write {@code (V A...)V} with the understanding that a {@code void} type {@code V}
+     * is quietly dropped from the parameter list, leaving {@code (A...)V}.)
+     * <li>The parameter list {@code (V A...)} of the body is called the <em>internal parameter list</em>.
+     * It will constrain the parameter lists of the other loop parts.
+     * <li>If the iteration variable type {@code V} is dropped from the internal parameter list, the resulting shorter
+     * list {@code (A...)} is called the <em>external parameter list</em>.
+     * <li>The body return type {@code V}, if non-{@code void}, determines the type of an
+     * additional state variable of the loop.
+     * The body must both accept and return a value of this type {@code V}.
+     * <li>If {@code init} is non-{@code null}, it must have return type {@code V}.
+     * Its parameter list (of some <a href="MethodHandles.html#astar">form {@code (A*)}</a>) must be
+     * <a href="MethodHandles.html#effid">effectively identical</a>
+     * to the external parameter list {@code (A...)}.
+     * <li>If {@code init} is {@code null}, the loop variable will be initialized to its
+     * {@linkplain #empty default value}.
+     * <li>The {@code pred} handle must not be {@code null}.  It must have {@code boolean} as its return type.
+     * Its parameter list (either empty or of the form {@code (V A*)}) must be
+     * effectively identical to the internal parameter list.
+     * </ul>
+     * <p>
+     * The resulting loop handle's result type and parameter signature are determined as follows:<ul>
+     * <li>The loop handle's result type is the result type {@code V} of the body.
+     * <li>The loop handle's parameter types are the types {@code (A...)},
+     * from the external parameter list.
+     * </ul>
      * <p>
      * 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.
      * <blockquote><pre>{@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;
      * }
      * }</pre></blockquote>
@@ -4507,59 +4891,491 @@
      * }</pre></blockquote>
      *
      * <p>
-     * @implSpec The implementation of this method is equivalent to:
+     * @apiNote The implementation of this method can be expressed as follows:
      * <blockquote><pre>{@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);
      * }
      * }</pre></blockquote>
      *
+     * @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<Class<?>> innerList = bodyType.parameterList();
+        List<Class<?>> outerList = innerList;
+        if (returnType == void.class) {
+            // OK
+        } else if (innerList.size() == 0 || innerList.get(0) != returnType) {
+            // leading V argument missing => error
+            MethodType expected = bodyType.insertParameterTypes(0, returnType);
+            throw misMatchedTypes("body function", bodyType, expected);
+        } else {
+            outerList = innerList.subList(1, innerList.size());
+        }
+        MethodType predType = pred.type();
+        if (predType.returnType() != boolean.class ||
+                !predType.effectivelyIdenticalParameters(0, innerList)) {
+            throw misMatchedTypes("loop predicate", predType, methodType(boolean.class, innerList));
+        }
+        if (init != null) {
+            MethodType initType = init.type();
+            if (initType.returnType() != returnType ||
+                    !initType.effectivelyIdenticalParameters(0, outerList)) {
+                throw misMatchedTypes("loop initializer", initType, methodType(returnType, outerList));
+            }
+        }
+    }
+
     /**
-     * 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}.
+     * Constructs a loop that runs a given number of iterations.
+     * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
      * <p>
-     * 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.
+     * <p>
+     * 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}.
+     * <p>
+     * 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).
+     * <p>
+     * The following rules hold for the argument handles:<ul>
+     * <li>The {@code iterations} handle must not be {@code null}, and must return
+     * the type {@code int}, referred to here as {@code I} in parameter type lists.
+     * <li>The {@code body} handle must not be {@code null}; its type must be of the form
+     * {@code (V I A...)V}, where {@code V} is non-{@code void}, or else {@code (I A...)void}.
+     * (In the {@code void} case, we assign the type {@code void} to the name {@code V},
+     * and we will write {@code (V I A...)V} with the understanding that a {@code void} type {@code V}
+     * is quietly dropped from the parameter list, leaving {@code (I A...)V}.)
+     * <li>The parameter list {@code (V I A...)} of the body contributes to a list
+     * of types called the <em>internal parameter list</em>.
+     * It will constrain the parameter lists of the other loop parts.
+     * <li>As a special case, if the body contributes only {@code V} and {@code I} types,
+     * with no additional {@code A} types, then the internal parameter list is extended by
+     * the argument types {@code A...} of the {@code iterations} handle.
+     * <li>If the iteration variable types {@code (V I)} are dropped from the internal parameter list, the resulting shorter
+     * list {@code (A...)} is called the <em>external parameter list</em>.
+     * <li>The body return type {@code V}, if non-{@code void}, determines the type of an
+     * additional state variable of the loop.
+     * The body must both accept a leading parameter and return a value of this type {@code V}.
+     * <li>If {@code init} is non-{@code null}, it must have return type {@code V}.
+     * Its parameter list (of some <a href="MethodHandles.html#astar">form {@code (A*)}</a>) must be
+     * <a href="MethodHandles.html#effid">effectively identical</a>
+     * to the external parameter list {@code (A...)}.
+     * <li>If {@code init} is {@code null}, the loop variable will be initialized to its
+     * {@linkplain #empty default value}.
+     * <li>The parameter list of {@code iterations} (of some form {@code (A*)}) must be
+     * effectively identical to the external parameter list {@code (A...)}.
+     * </ul>
+     * <p>
+     * The resulting loop handle's result type and parameter signature are determined as follows:<ul>
+     * <li>The loop handle's result type is the result type {@code V} of the body.
+     * <li>The loop handle's parameter types are the types {@code (A...)},
+     * from the external parameter list.
+     * </ul>
      * <p>
      * 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.
      * <blockquote><pre>{@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;
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example with a fully conformant body method:
+     * <blockquote><pre>{@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, String init) { return "na " + v; }
+     * // assume MH_step is a handle to the method above
+     * MethodHandle fit13 = MethodHandles.constant(int.class, 13);
+     * 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!"));
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example with the simplest possible body method type,
+     * and passing the number of iterations to the loop invocation:
+     * <blockquote><pre>{@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!"));
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example that treats the number of iterations, string to append to, and string to append
+     * as loop parameters:
+     * <blockquote><pre>{@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!"));
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example that illustrates the usage of {@link #dropArgumentsToMatch(MethodHandle, int, List, int)}
+     * to enforce a loop type:
+     * <blockquote><pre>{@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!"));
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote The implementation of this method can be expressed as follows:
+     * <blockquote><pre>{@code
+     * MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
+     *     return countedLoop(empty(iterations.type()), iterations, init, body);
+     * }
+     * }</pre></blockquote>
+     *
+     * @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 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(empty(iterations.type()), iterations, init, body);
+    }
+
+    /**
+     * Constructs a loop that counts over a range of numbers.
+     * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
+     * <p>
+     * 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.
+     * <p>
+     * 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}.
+     * <p>
+     * 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).
+     * <p>
+     * The following rules hold for the argument handles:<ul>
+     * <li>The {@code start} and {@code end} handles must not be {@code null}, and must both return
+     * the common type {@code int}, referred to here as {@code I} in parameter type lists.
+     * <li>The {@code body} handle must not be {@code null}; its type must be of the form
+     * {@code (V I A...)V}, where {@code V} is non-{@code void}, or else {@code (I A...)void}.
+     * (In the {@code void} case, we assign the type {@code void} to the name {@code V},
+     * and we will write {@code (V I A...)V} with the understanding that a {@code void} type {@code V}
+     * is quietly dropped from the parameter list, leaving {@code (I A...)V}.)
+     * <li>The parameter list {@code (V I A...)} of the body contributes to a list
+     * of types called the <em>internal parameter list</em>.
+     * It will constrain the parameter lists of the other loop parts.
+     * <li>As a special case, if the body contributes only {@code V} and {@code I} types,
+     * with no additional {@code A} types, then the internal parameter list is extended by
+     * the argument types {@code A...} of the {@code end} handle.
+     * <li>If the iteration variable types {@code (V I)} are dropped from the internal parameter list, the resulting shorter
+     * list {@code (A...)} is called the <em>external parameter list</em>.
+     * <li>The body return type {@code V}, if non-{@code void}, determines the type of an
+     * additional state variable of the loop.
+     * The body must both accept a leading parameter and return a value of this type {@code V}.
+     * <li>If {@code init} is non-{@code null}, it must have return type {@code V}.
+     * Its parameter list (of some <a href="MethodHandles.html#astar">form {@code (A*)}</a>) must be
+     * <a href="MethodHandles.html#effid">effectively identical</a>
+     * to the external parameter list {@code (A...)}.
+     * <li>If {@code init} is {@code null}, the loop variable will be initialized to its
+     * {@linkplain #empty default value}.
+     * <li>The parameter list of {@code start} (of some form {@code (A*)}) must be
+     * effectively identical to the external parameter list {@code (A...)}.
+     * <li>Likewise, the parameter list of {@code end} must be effectively identical
+     * to the external parameter list.
+     * </ul>
+     * <p>
+     * The resulting loop handle's result type and parameter signature are determined as follows:<ul>
+     * <li>The loop handle's result type is the result type {@code V} of the body.
+     * <li>The loop handle's parameter types are the types {@code (A...)},
+     * from the external parameter list.
+     * </ul>
+     * <p>
+     * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of
+     * the second loop variable as well as the result type of the loop; and {@code A...}/{@code a...} represent
+     * arguments passed to the loop.
+     * <blockquote><pre>{@code
+     * 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(v, i, a...);
+     *   }
+     *   return v;
+     * }
+     * }</pre></blockquote>
+     *
+     * <p>
+     * @apiNote The implementation of this method can be expressed as follows:
+     * <blockquote><pre>{@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_predicate are handles to implementation-internal methods with
+     *     // the following semantics:
+     *     // MH_increment: (int limit, int counter) -> 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[]
+     *         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);
+     * }
+     * }</pre></blockquote>
+     *
+     * @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 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) {
+        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<Class<?>> innerList = bodyType.parameterList();
+        // strip leading V value if present
+        int vsize = (returnType == void.class ? 0 : 1);
+        if (vsize != 0 && (innerList.size() == 0 || innerList.get(0) != returnType)) {
+            // argument list has no "V" => error
+            MethodType expected = bodyType.insertParameterTypes(0, returnType);
+            throw misMatchedTypes("body function", bodyType, expected);
+        } else if (innerList.size() <= vsize || innerList.get(vsize) != counterType) {
+            // missing I type => error
+            MethodType expected = bodyType.insertParameterTypes(vsize, counterType);
+            throw misMatchedTypes("body function", bodyType, expected);
+        }
+        List<Class<?>> outerList = innerList.subList(vsize + 1, innerList.size());
+        if (outerList.isEmpty()) {
+            // special case; take lists from end handle
+            outerList = end.type().parameterList();
+            innerList = bodyType.insertParameterTypes(vsize + 1, outerList).parameterList();
+        }
+        MethodType expected = methodType(counterType, outerList);
+        if (!start.type().effectivelyIdenticalParameters(0, outerList)) {
+            throw misMatchedTypes("start parameter types", start.type(), expected);
+        }
+        if (end.type() != start.type() &&
+            !end.type().effectivelyIdenticalParameters(0, outerList)) {
+            throw misMatchedTypes("end parameter types", end.type(), expected);
+        }
+        if (init != null) {
+            MethodType initType = init.type();
+            if (initType.returnType() != returnType ||
+                !initType.effectivelyIdenticalParameters(0, outerList)) {
+                throw misMatchedTypes("loop initializer", initType, methodType(returnType, outerList));
+            }
+        }
+    }
+
+    /**
+     * Constructs a loop that ranges over the values produced by an {@code Iterator<T>}.
+     * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
+     * <p>
+     * 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}.
+     * <p>
+     * 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}.
+     * <p>
+     * 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).
+     * <p>
+     * The following rules hold for the argument handles:<ul>
+     * <li>The {@code body} handle must not be {@code null}; its type must be of the form
+     * {@code (V T A...)V}, where {@code V} is non-{@code void}, or else {@code (T A...)void}.
+     * (In the {@code void} case, we assign the type {@code void} to the name {@code V},
+     * and we will write {@code (V T A...)V} with the understanding that a {@code void} type {@code V}
+     * is quietly dropped from the parameter list, leaving {@code (T A...)V}.)
+     * <li>The parameter list {@code (V T A...)} of the body contributes to a list
+     * of types called the <em>internal parameter list</em>.
+     * It will constrain the parameter lists of the other loop parts.
+     * <li>As a special case, if the body contributes only {@code V} and {@code T} types,
+     * with no additional {@code A} types, then the internal parameter list is extended by
+     * the argument types {@code A...} of the {@code iterator} handle; if it is {@code null} the
+     * single type {@code Iterable} is added and constitutes the {@code A...} list.
+     * <li>If the iteration variable types {@code (V T)} are dropped from the internal parameter list, the resulting shorter
+     * list {@code (A...)} is called the <em>external parameter list</em>.
+     * <li>The body return type {@code V}, if non-{@code void}, determines the type of an
+     * additional state variable of the loop.
+     * The body must both accept a leading parameter and return a value of this type {@code V}.
+     * <li>If {@code init} is non-{@code null}, it must have return type {@code V}.
+     * Its parameter list (of some <a href="MethodHandles.html#astar">form {@code (A*)}</a>) must be
+     * <a href="MethodHandles.html#effid">effectively identical</a>
+     * to the external parameter list {@code (A...)}.
+     * <li>If {@code init} is {@code null}, the loop variable will be initialized to its
+     * {@linkplain #empty default value}.
+     * <li>If the {@code iterator} handle is non-{@code null}, it must have the return
+     * type {@code java.util.Iterator} or a subtype thereof.
+     * The iterator it produces when the loop is executed will be assumed
+     * to yield values which can be converted to type {@code T}.
+     * <li>The parameter list of an {@code iterator} that is non-{@code null} (of some form {@code (A*)}) must be
+     * effectively identical to the external parameter list {@code (A...)}.
+     * <li>If {@code iterator} is {@code null} it defaults to a method handle which behaves
+     * like {@link java.lang.Iterable#iterator()}.  In that case, the internal parameter list
+     * {@code (V T A...)} must have at least one {@code A} type, and the default iterator
+     * handle parameter is adjusted to accept the leading {@code A} type, as if by
+     * the {@link MethodHandle#asType asType} conversion method.
+     * The leading {@code A} type must be {@code Iterable} or a subtype thereof, or an array type.
+     * This conversion step, done at loop construction time, must not throw a {@code WrongMethodTypeException}.
+     * </ul>
+     * <p>
+     * The type {@code T} may be either a primitive or reference.
+     * Since type {@code Iterator<T>} is erased in the method handle representation to the raw type {@code Iterator},
+     * the {@code iteratedLoop} combinator adjusts the leading argument type for {@code body} to {@code Object}
+     * as if by the {@link MethodHandle#asType asType} conversion method.
+     * Therefore, if an iterator of the wrong type appears as the loop is executed, runtime exceptions may occur
+     * as the result of dynamic conversions performed by {@link MethodHandle#asType(MethodType)}.
+     * <p>
+     * The resulting loop handle's result type and parameter signature are determined as follows:<ul>
+     * <li>The loop handle's result type is the result type {@code V} of the body.
+     * <li>The loop handle's parameter types are the types {@code (A...)},
+     * from the external parameter list.
+     * </ul>
+     * <p>
+     * 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...} represent arguments passed to the loop.
+     * <blockquote><pre>{@code
+     * Iterator<T> iterator(A...);  // defaults to Iterable::iterator
+     * V init(A...);
+     * V body(V,T,A...);
+     * V iteratedLoop(A... a...) {
+     *   Iterator<T> it = iterator(a...);
+     *   V v = init(a...);
+     *   for (T t : it) {
+     *     v = body(v, t, a...);
      *   }
      *   return v;
      * }
@@ -4567,243 +5383,164 @@
      * <p>
      * @apiNote Example:
      * <blockquote><pre>{@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
-     * MethodHandle fit13 = MethodHandles.constant(int.class, 13);
-     * MethodHandle loop = MethodHandles.countedLoop(fit13, MH_start, MH_step);
-     * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
-     * }</pre></blockquote>
-     *
-     * <p>
-     * @implSpec The implementation of this method is equivalent to:
-     * <blockquote><pre>{@code
-     * MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
-     *     return countedLoop(null, iterations, init, body);  // null => constant zero
-     * }
-     * }</pre></blockquote>
-     *
-     * @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
-     *
-     * @since 9
-     */
-    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}.
-     * <p>
-     * 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}.
-     * <p>
-     * 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.
-     * <blockquote><pre>{@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;
-     * }
-     * }</pre></blockquote>
-     *
-     * <p>
-     * @implSpec The implementation of this method is equivalent to:
-     * <blockquote><pre>{@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<y of type int,
-     *     // assume MH_decrement is a handle to x-1 of type int
-     *     MethodHandle[]
-     *         indexVar = {start, MH_increment}, // i = start; i = i+1
-     *         loopLimit = {end, null,
-     *                       filterArgument(MH_lessThan, 0, MH_decrement), returnVar}, // i-1<end
-     *         bodyClause = {init,
-     *                       filterArgument(dropArguments(body, 1, int.class), 0, MH_decrement}; // v = body(i-1, v)
-     *     return loop(indexVar, loopLimit, bodyClause);
-     * }
-     * }</pre></blockquote>
-     *
-     * @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
-     *
-     * @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;
-        }
-        MethodHandle defaultResultHandle = resultType == void.class ? zero(void.class) : identity(resultType);
-        MethodHandle actualBody = body == null ? dropArguments(defaultResultHandle, 0, int.class) : body;
-        MethodHandle returnVar = dropArguments(defaultResultHandle, 0, int.class, int.class);
-        MethodHandle actualEnd = end == null ? constant(int.class, 0) : end;
-        MethodHandle decr = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_decrementCounter);
-        MethodHandle[] indexVar = {start, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep)};
-        MethodHandle[] loopLimit = {actualEnd, null,
-                filterArgument(MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred), 0, decr),
-                returnVar};
-        MethodHandle[] bodyClause = {actualInit, filterArgument(dropArguments(actualBody, 1, int.class), 0, decr)};
-        return loop(indexVar, loopLimit, bodyClause);
-    }
-
-    /**
-     * Constructs a loop that ranges over the elements produced by an {@code Iterator<T>}.
-     * The iterator will be produced by the evaluation of the {@code iterator} handle.
-     * This handle must have {@link java.util.Iterator} as its return type.
-     * If this handle is passed as {@code null} the method {@link Iterable#iterator} will be used instead,
-     * and will be applied to a leading argument of the loop handle.
-     * Each value produced by the iterator is passed to the {@code body}, which must accept an initial {@code T} parameter.
-     * The result of the loop execution is the final value of the additional local state
-     * obtained by running {@code init}.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <blockquote><pre>{@code
-     * Iterator<T> iterator(A);  // defaults to Iterable::iterator
-     * V init(A);
-     * V body(T,V,A);
-     * V iteratedLoop(A a) {
-     *   Iterator<T> it = iterator(a);
-     *   V v = init(a);
-     *   for (T t : it) {
-     *     v = body(t, v, a);
-     *   }
-     *   return v;
-     * }
-     * }</pre></blockquote>
-     * <p>
-     * The type {@code T} may be either a primitive or reference.
-     * Since type {@code Iterator<T>} is erased in the method handle representation to the raw type
-     * {@code Iterator}, the {@code iteratedLoop} combinator adjusts the leading argument type for {@code body}
-     * to {@code Object} as if by the {@link MethodHandle#asType asType} conversion method.
-     * Therefore, if an iterator of the wrong type appears as the loop is executed,
-     * runtime exceptions may occur as the result of dynamic conversions performed by {@code asType}.
-     * <p>
-     * @apiNote Example:
-     * <blockquote><pre>{@code
-     * // reverse a list
-     * static List<String> reverseStep(String e, List<String> r, List<String> l) {
+     * // get an iterator from a list
+     * static List<String> reverseStep(List<String> r, String e) {
      *   r.add(0, e);
      *   return r;
      * }
-     * static List<String> newArrayList(List<String> l) { return new ArrayList<>(); }
-     * // assume MH_reverseStep, MH_newArrayList are handles to the above methods
+     * static List<String> newArrayList() { return new ArrayList<>(); }
+     * // assume MH_reverseStep and MH_newArrayList are handles to the above methods
      * MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep);
      * List<String> list = Arrays.asList("a", "b", "c", "d", "e");
      * List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a");
      * assertEquals(reversedList, (List<String>) loop.invoke(list));
      * }</pre></blockquote>
      * <p>
-     * @implSpec The implementation of this method is equivalent to (excluding error handling):
+     * @apiNote The implementation of this method can be expressed approximately as follows:
      * <blockquote><pre>{@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);
      * }
      * }</pre></blockquote>
      *
-     * @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;
-
-        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));
+        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);
+
+            // 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<Class<?>> innerList = bodyType.parameterList();
+        // strip leading V value if present
+        int vsize = (returnType == void.class ? 0 : 1);
+        if (vsize != 0 && (innerList.size() == 0 || innerList.get(0) != returnType)) {
+            // argument list has no "V" => error
+            MethodType expected = bodyType.insertParameterTypes(0, returnType);
+            throw misMatchedTypes("body function", bodyType, expected);
+        } else if (innerList.size() <= vsize) {
+            // missing T type => error
+            MethodType expected = bodyType.insertParameterTypes(vsize, Object.class);
+            throw misMatchedTypes("body function", bodyType, expected);
+        }
+        //Class<?> elementType = innerList.get(vsize);  // do not need this
+        List<Class<?>> outerList = innerList.subList(vsize + 1, innerList.size());
+        if (outerList.isEmpty()) {
+            // special case; take lists from iterator handle
+            outerList = ((iterator != null)
+                    ? iterator.type().parameterList()
+                    : Arrays.asList(Iterable.class));
+            innerList = bodyType.insertParameterTypes(vsize + 1, outerList).parameterList();
+        }
+        if (iterator != null) {
+            MethodType itype = iterator.type();
+            if (!Iterator.class.isAssignableFrom(itype.returnType())) {
+                throw newIllegalArgumentException("iteratedLoop first argument must have Iterator return type");
+            }
+            if (!itype.effectivelyIdenticalParameters(0, outerList)) {
+                MethodType expected = methodType(itype.returnType(), outerList);
+                throw misMatchedTypes("iterator parameters", itype, expected);
+            }
+        }
+        if (init != null) {
+            MethodType initType = init.type();
+            if (initType.returnType() != returnType ||
+                    !initType.effectivelyIdenticalParameters(0, outerList)) {
+                throw misMatchedTypes("loop initializer", initType, methodType(returnType, outerList));
+            }
+        }
+        Class<?> iterableType = outerList.isEmpty() ? null : outerList.get(0);
+        if (iterableType != null && !Iterable.class.isAssignableFrom(iterableType) && !iterableType.isArray()) {
+            throw newIllegalArgumentException(
+                    "inferred first loop argument must be an array or inherit from Iterable: " + iterableType);
+        }
+        return iterableType;  // help the caller a bit
+    }
+
+    /*non-public*/ static MethodHandle swapArguments(MethodHandle mh, int i, int j) {
+        // there should be a better way to uncross my wires
+        int arity = mh.type().parameterCount();
+        int[] order = new int[arity];
+        for (int k = 0; k < arity; k++)  order[k] = k;
+        order[i] = j; order[j] = i;
+        Class<?>[] types = mh.type().parameterArray();
+        Class<?> ti = types[i]; types[i] = types[j]; types[j] = ti;
+        MethodType swapType = methodType(mh.type().returnType(), types);
+        return permuteArguments(mh, swapType, order);
+    }
+
     /**
      * 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
@@ -4885,7 +5622,7 @@
         List<Class<?>> cleanupParamTypes = cleanup.type().parameterList();
         Class<?> rtype = target.type().returnType();
 
-        checkTryFinally(target, cleanup);
+        tryFinallyChecks(target, cleanup);
 
         // Match parameter lists: if the cleanup has a shorter parameter list than the target, add ignored arguments.
         // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the
@@ -4896,210 +5633,22 @@
         return MethodHandleImpl.makeTryFinally(target.asFixedArity(), cleanup.asFixedArity(), rtype, targetParamTypes);
     }
 
-    /**
-     * 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.
-     * <p>
-     * 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.
-     * <p>
-     * @apiNote Example:
-     * <blockquote><pre>{@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"));
-     * }</pre></blockquote>
-     * <p>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.
-     * <blockquote><pre>{@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...);
-     * }
-     * }</pre></blockquote>
-     * <p>
-     * <em>Note:</em> 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<Class<?>> buildCommonSuffix(List<MethodHandle> init, List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini, int cpSize) {
-        final List<Class<?>> empty = List.of();
-        final List<MethodHandle> nonNullInits = init.stream().filter(Objects::nonNull).collect(Collectors.toList());
-        if (nonNullInits.isEmpty()) {
-            final List<Class<?>> longest = Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).
-                    // take only those that can contribute to a common suffix because they are longer than the prefix
-                    map(MethodHandle::type).filter(t -> t.parameterCount() > cpSize).map(MethodType::parameterList).
-                    reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty);
-            return longest.size() == 0 ? empty : longest.subList(cpSize, longest.size());
-        } else {
-            return nonNullInits.stream().map(MethodHandle::type).map(MethodType::parameterList).
-                    reduce((p, q) -> p.size() >= q.size() ? p : q).get();
-        }
-    }
-
-    private static void checkLoop1b(List<MethodHandle> init, List<Class<?>> commonSuffix) {
-        if (init.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::parameterList).
-                anyMatch(pl -> !pl.equals(commonSuffix.subList(0, pl.size())))) {
-            throw newIllegalArgumentException("found non-effectively identical init parameter type lists: " + init +
-                    " (common suffix: " + commonSuffix + ")");
-        }
-    }
-
-    private static void checkLoop1cd(List<MethodHandle> pred, List<MethodHandle> fini, Class<?> loopReturnType) {
-        if (fini.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType).
-                anyMatch(t -> t != loopReturnType)) {
-            throw newIllegalArgumentException("found non-identical finalizer return types: " + fini + " (return type: " +
-                    loopReturnType + ")");
-        }
-
-        if (!pred.stream().filter(Objects::nonNull).findFirst().isPresent()) {
-            throw newIllegalArgumentException("no predicate found", pred);
-        }
-        if (pred.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType).
-                anyMatch(t -> t != boolean.class)) {
-            throw newIllegalArgumentException("predicates must have boolean return type", pred);
-        }
-    }
-
-    private static void checkLoop2(List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini, List<Class<?>> commonParameterSequence) {
-        final int cpSize = commonParameterSequence.size();
-        if (Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).map(MethodHandle::type).
-                map(MethodType::parameterList).
-                anyMatch(pl -> pl.size() > cpSize || !pl.equals(commonParameterSequence.subList(0, pl.size())))) {
-            throw newIllegalArgumentException("found non-effectively identical parameter type lists:\nstep: " + step +
-                    "\npred: " + pred + "\nfini: " + fini + " (common parameter sequence: " + commonParameterSequence + ")");
-        }
-    }
-
-    private static void checkIteratedLoop(MethodHandle iterator, MethodHandle body) {
-        if (null != iterator && !Iterator.class.isAssignableFrom(iterator.type().returnType())) {
-            throw newIllegalArgumentException("iteratedLoop first argument must have Iterator return type");
-        }
-        if (null == body) {
-            throw newIllegalArgumentException("iterated loop body must not be null");
-        }
-    }
-
-    private static void checkTryFinally(MethodHandle target, MethodHandle cleanup) {
+    private static void tryFinallyChecks(MethodHandle target, MethodHandle cleanup) {
         Class<?> rtype = target.type().returnType();
         if (rtype != cleanup.type().returnType()) {
             throw misMatchedTypes("target and return types", cleanup.type().returnType(), rtype);
         }
-        List<Class<?>> cleanupParamTypes = cleanup.type().parameterList();
-        if (!Throwable.class.isAssignableFrom(cleanupParamTypes.get(0))) {
+        MethodType cleanupType = cleanup.type();
+        if (!Throwable.class.isAssignableFrom(cleanupType.parameterType(0))) {
             throw misMatchedTypes("cleanup first argument and Throwable", cleanup.type(), Throwable.class);
         }
-        if (rtype != void.class && cleanupParamTypes.get(1) != rtype) {
+        if (rtype != void.class && cleanupType.parameterType(1) != rtype) {
             throw misMatchedTypes("cleanup second argument and target return type", cleanup.type(), rtype);
         }
         // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the
         // target parameter list.
         int cleanupArgIndex = rtype == void.class ? 1 : 2;
-        List<Class<?>> cleanupArgSuffix = cleanupParamTypes.subList(cleanupArgIndex, cleanupParamTypes.size());
-        List<Class<?>> targetParamTypes = target.type().parameterList();
-        if (targetParamTypes.size() < cleanupArgSuffix.size() ||
-                !cleanupArgSuffix.equals(targetParamTypes.subList(0, cleanupParamTypes.size() - cleanupArgIndex))) {
+        if (!cleanupType.effectivelyIdenticalParameters(cleanupArgIndex, target.type().parameterList())) {
             throw misMatchedTypes("cleanup parameters after (Throwable,result) and target parameter list prefix",
                     cleanup.type(), target.type());
         }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Wed Sep 28 03:18:01 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Wed Sep 28 14:02:21 2016 +0200
@@ -809,6 +809,28 @@
         return sj.toString();
     }
 
+    /** True if my parameter list is effectively identical to the given full list,
+     *  after skipping the given number of my own initial parameters.
+     *  In other words, after disregarding {@code skipPos} parameters,
+     *  my remaining parameter list is no longer than the {@code fullList}, and
+     *  is equal to the same-length initial sublist of {@code fullList}.
+     */
+    /*non-public*/
+    boolean effectivelyIdenticalParameters(int skipPos, List<Class<?>> fullList) {
+        int myLen = ptypes.length, fullLen = fullList.size();
+        if (skipPos > myLen || myLen - skipPos > fullLen)
+            return false;
+        List<Class<?>> myList = Arrays.asList(ptypes);
+        if (skipPos != 0) {
+            myList = myList.subList(skipPos, myLen);
+            myLen -= skipPos;
+        }
+        if (fullLen == myLen)
+            return myList.equals(fullList);
+        else
+            return myList.equals(fullList.subList(0, myLen));
+    }
+
     /** True if the old return type can always be viewed (w/o casting) under new return type,
      *  and the new parameters can be viewed (w/o casting) under the old parameter types.
      */
--- a/jdk/test/java/lang/invoke/CountedLoopIterationCountsTest.java	Wed Sep 28 03:18:01 2016 +0000
+++ b/jdk/test/java/lang/invoke/CountedLoopIterationCountsTest.java	Wed Sep 28 14:02:21 2016 +0200
@@ -71,7 +71,7 @@
         }
     }
 
-    static int step(int counter, int stepCount) {
+    static int step(int stepCount, int counter) {
         return stepCount + 1;
     }
 
--- a/jdk/test/java/lang/invoke/JavaDocExamplesTest.java	Wed Sep 28 03:18:01 2016 +0000
+++ b/jdk/test/java/lang/invoke/JavaDocExamplesTest.java	Wed Sep 28 14:02:21 2016 +0200
@@ -703,6 +703,66 @@
         }}
     }
 
+    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; }
+
+    @Test public void testSimplerLoop() throws Throwable {
+        MethodHandle MH_inc, MH_mult, MH_cmp;
+        Class<?> I = int.class;
+        MH_inc = LOOKUP.findStatic(THIS_CLASS, "inc", methodType(I, I));
+        MH_mult = LOOKUP.findStatic(THIS_CLASS, "mult", methodType(I, I, I));
+        MH_cmp = LOOKUP.findStatic(THIS_CLASS, "cmp", methodType(boolean.class, I, I));
+        {{
+{} /// JAVADOC
+// simplified implementation of the factorial function as a loop handle
+// 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));
+{}
+        }}
+    }
+
+    // for testFacLoop
+{}
+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_inc, MH_mult, and MH_pred are handles to the above methods
+    @Test public void testFacLoop() throws Throwable {
+        MethodHandle MH_FacLoop, MH_inc, MH_mult, MH_pred, MH_fin;
+        Class<?> I = int.class;
+        MH_FacLoop = LOOKUP.findConstructor(FacLoop.class, methodType(void.class, I));
+        MH_inc = LOOKUP.findVirtual(FacLoop.class, "inc", methodType(I, I));
+        MH_mult = LOOKUP.findVirtual(FacLoop.class, "mult", methodType(I, I, I));
+        MH_pred = LOOKUP.findVirtual(FacLoop.class, "pred", methodType(boolean.class, I));
+        MH_fin = LOOKUP.findVirtual(FacLoop.class, "fin", methodType(I, I, I));
+        {{
+{} /// JAVADOC
+// instance-based implementation of the factorial function as a loop handle
+// 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));
+{}
+        }}
+    }
+
     static List<String> initZip(Iterator<String> a, Iterator<String> b) { return new ArrayList<>(); }
     static boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { return a.hasNext() && b.hasNext(); }
     static List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) {
@@ -749,36 +809,81 @@
         }}
     }
 
-    static String start(String arg) { return arg; }
-    static String step(int counter, String v, String arg) { return "na " + v; }
+    static String step(String v, int counter, String start_) { return "na " + v; }  //#0
+    static String step(String v, int counter ) { return "na " + v; } //#1
+    static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; } //#2
+    static String step3(String v, int counter, String pre) { return pre + " " + v; } //#3
 
     @Test public void testCountedLoop() throws Throwable {
-        MethodHandle MH_start, MH_step;
-        Class<?> S = String.class;
-        MH_start = LOOKUP.findStatic(THIS_CLASS, "start", methodType(S, S));
-        MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, int.class, S, S));
+        MethodHandle MH_step;
+        Class<?> S = String.class, I = int.class;
+        // Theme:
+        MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, S, I, S));
         {{
 {} /// JAVADOC
 // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
 // => a variation on a well known theme
 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);  // (v, i, _) -> "na " + v
 assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
 {}
         }}
+        // Variation #1:
+        MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, S, I));
+        {{
+{} /// JAVADOC
+// String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
+// => a variation on a well known theme
+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!"));
+{}
+            assertEquals("na na Lambdaman!", loop.invoke(2, "Lambdaman!"));
+            assertEquals("Lambdaman!", loop.invoke(0, "Lambdaman!"));
+            assertEquals("Lambdaman!", loop.invoke(-1, "Lambdaman!"));
+            assertEquals("Lambdaman!", loop.invoke(Integer.MIN_VALUE, "Lambdaman!"));
+        }}
+        // Variation #2:
+        MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, S, I, I, S, S));
+        {{
+{} /// JAVADOC
+// String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
+// => a variation on a well known theme
+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!"));
+{}
+        }}
+        // Variation #3:
+        MH_step = LOOKUP.findStatic(THIS_CLASS, "step3", methodType(S, S, I, S));
+        {{
+{} /// JAVADOC
+// String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
+// => a variation on a well known theme
+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!"));
+{}
+        }}
     }
 
-    static List<String> reverseStep(String e, List<String> r, List<String> l) {
+    static List<String> reverseStep(List<String> r, String e) {
         r.add(0, e);
         return r;
     }
-    static List<String> newArrayList(List<String> l) { return new ArrayList<>(); }
+    static List<String> newArrayList() { return new ArrayList<>(); }
 
     @Test public void testIteratedLoop() throws Throwable {
         MethodHandle MH_newArrayList, MH_reverseStep;
-        Class<?> L = List.class;
-        MH_newArrayList = LOOKUP.findStatic(THIS_CLASS, "newArrayList", methodType(L, L));
-        MH_reverseStep = LOOKUP.findStatic(THIS_CLASS, "reverseStep", methodType(L, String.class, L, L));
+        Class<?> L = List.class, S = String.class;
+        MH_newArrayList = LOOKUP.findStatic(THIS_CLASS, "newArrayList", methodType(L));
+        MH_reverseStep = LOOKUP.findStatic(THIS_CLASS, "reverseStep", methodType(L, L, S));
         {{
 {} /// JAVADOC
 // reverse a list
--- a/jdk/test/java/lang/invoke/LoopCombinatorTest.java	Wed Sep 28 03:18:01 2016 +0000
+++ b/jdk/test/java/lang/invoke/LoopCombinatorTest.java	Wed Sep 28 14:02:21 2016 +0200
@@ -28,6 +28,7 @@
  * @bug 8150635
  * @bug 8150956
  * @bug 8150957
+ * @bug 8151179
  * @bug 8152667
  * @bug 8153637
  * @bug 8154751
@@ -146,6 +147,16 @@
         assertEquals(120, loop.invoke(new LoopWithVirtuals(), 5));
     }
 
+    @Test
+    public static void testLoopOmitPred() throws Throwable {
+        // construct a loop to calculate factorial that omits a predicate
+        MethodHandle[] counterClause = new MethodHandle[]{null, Fac.MH_inc, null, Fac.MH_fin};
+        MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin};
+        MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
+        assertEquals(Fac.MT_fac, loop.type());
+        assertEquals(120, loop.invoke(5));
+    }
+
     @DataProvider
     static Object[][] negativeTestData() {
         MethodHandle i0 = MethodHandles.constant(int.class, 0);
@@ -153,7 +164,8 @@
         MethodHandle id = MethodHandles.dropArguments(i0, 0, int.class, double.class);
         MethodHandle i3 = MethodHandles.dropArguments(i0, 0, int.class, int.class, int.class);
         List<MethodHandle> inits = Arrays.asList(ii, id, i3);
-        List<Class<?>> ints = Arrays.asList(int.class, int.class, int.class);
+        List<Class<?>> ints3 = Arrays.asList(int.class, int.class, int.class);
+        List<Class<?>> ints4 = Arrays.asList(int.class, int.class, int.class, int.class);
         List<MethodHandle> finis = Arrays.asList(Fac.MH_fin, Fac.MH_inc, Counted.MH_step);
         List<MethodHandle> preds1 = Arrays.asList(null, null, null);
         List<MethodHandle> preds2 = Arrays.asList(null, Fac.MH_fin, null);
@@ -174,7 +186,7 @@
                         "clause 0: init and step return types must match: int != void"},
                 {new MethodHandle[][]{{ii}, {id}, {i3}},
                         "found non-effectively identical init parameter type lists: " + inits +
-                                " (common suffix: " + ints + ")"},
+                                " (common suffix: " + ints3 + ")"},
                 {new MethodHandle[][]{{null, Fac.MH_inc, null, Fac.MH_fin}, {null, Fac.MH_inc, null, Fac.MH_inc},
                         {null, Counted.MH_start, null, Counted.MH_step}},
                         "found non-identical finalizer return types: " + finis + " (return type: int)"},
@@ -185,11 +197,11 @@
                 {new MethodHandle[][]{{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, eek, Fac.MH_pred, Fac.MH_fin},
                         {null, Fac.MH_dot}},
                         "found non-effectively identical parameter type lists:\nstep: " + nesteps +
-                                "\npred: " + nepreds + "\nfini: " + nefinis + " (common parameter sequence: " + ints + ")"},
+                                "\npred: " + nepreds + "\nfini: " + nefinis + " (common parameter sequence: " + ints3 + ")"},
                 {new MethodHandle[][]{{null, LoopWithVirtuals.MH_inc},
                         {LoopWithVirtuals.MH_one, LoopWithVirtuals.MH_mult, LoopWithVirtuals.MH_pred, LoopWithVirtuals.MH_fin}},
                         "found non-effectively identical parameter type lists:\nstep: " + lvsteps +
-                                "\npred: " + lvpreds + "\nfini: " + lvfinis + " (common parameter sequence: " + ints + ")"}
+                                "\npred: " + lvpreds + "\nfini: " + lvfinis + " (common parameter sequence: " + ints4 + ")"}
         };
     }
 
@@ -207,7 +219,7 @@
     public static void testLoopNegative(MethodHandle[][] clauses, String expectedMessage) throws Throwable {
         boolean caught = false;
         try {
-            MH_loop.invokeWithArguments(clauses);
+            MH_loop.invokeWithArguments((Object[]) clauses);
         } catch (IllegalArgumentException iae) {
             assertEquals(expectedMessage, iae.getMessage());
             caught = true;
@@ -215,12 +227,100 @@
         assertTrue(caught);
     }
 
-    @Test
-    public static void testWhileLoop() throws Throwable {
+    @Test(dataProvider = "whileLoopTestData")
+    public static void testWhileLoop(MethodHandle MH_zero,
+                                     MethodHandle MH_pred,
+                                     MethodHandle MH_step,
+                                     String messageOrNull) throws Throwable {
         // int i = 0; while (i < limit) { ++i; } return i; => limit
-        MethodHandle loop = MethodHandles.whileLoop(While.MH_zero, While.MH_pred, While.MH_step);
-        assertEquals(While.MT_while, loop.type());
-        assertEquals(23, loop.invoke(23));
+        try {
+            MethodHandle loop = MethodHandles.whileLoop(MH_zero, MH_pred, MH_step);
+            assert messageOrNull == null;
+            if (MH_step.type().equals(While.MH_step.type()))
+                assertEquals(While.MT_while, loop.type());
+            assertEquals(MH_step.type().dropParameterTypes(0, 1), loop.type());
+            while (loop.type().parameterCount() > 1)  loop = snip(loop);
+            assertEquals(23, loop.invoke(23));
+        } catch (IllegalArgumentException iae) {
+            assert messageOrNull != null;
+            assertEqualsFIXME(messageOrNull, iae.getMessage());
+        }
+    }
+
+    static void assertEqualsFIXME(String expect, String actual) {
+        if (!expect.equals(actual)) {
+            // just issue a warning
+            System.out.println("*** "+actual+"\n != "+expect);
+        }
+    }
+
+    @DataProvider
+    static Object[][] whileLoopTestData() {
+        MethodHandle
+            zeroI = While.MH_zero,
+            zeroX = snip(zeroI),
+            zeroIB = slap(zeroI, byte.class),
+            predII = While.MH_pred,
+            predIX = snip(predII),
+            predIIB = slap(predII, byte.class),
+            stepII = While.MH_step,
+            stepIX = snip(stepII),
+            stepIIB = slap(stepII, byte.class)
+            ;
+        return new Object[][] {
+            // normal while loop clauses, perhaps with effectively-identical reductions
+            {zeroI, predII, stepII, null},
+            {zeroX, predII, stepII, null},
+            {null, predII, stepII, null},
+            // expanded while loop clauses
+            {zeroIB, predIIB, stepIIB, null},
+            {zeroI, predIIB, stepIIB, null},
+            {null, predIIB, stepIIB, null},
+            {zeroIB, predII, stepIIB, null},
+            {zeroX, predII, stepIIB, null},
+            {null, predII, stepIIB, null},
+            // short step clauses cause errors
+            {zeroI, predII, stepIX, "loop predicate must match: (int,int)boolean != (int)boolean"},
+            {zeroIB, predIX, stepIX, "loop initializer must match: (int,byte)int != ()int"},
+            // bad body type
+            {zeroI, predII, tweak(stepII, -1, char.class), "body function must match: (int,int)char != (char,int,int)char"},
+            {zeroI, predII, tweak(stepII,  0, char.class), "body function must match: (char,int)int != (int,char,int)int"},
+            // bad pred type
+            {zeroI, tweak(predII, -1, char.class), stepII, "loop predicate must match: (int,int)char != (int,int)boolean"},
+            {zeroI, tweak(predII,  0, char.class), stepII, "loop predicate must match: (char,int)boolean != (int,int)boolean"},
+            // bad init type
+            {tweak(zeroI, -1, char.class), predII, stepII, "loop initializer must match: (int)char != (int)int"},
+            {tweak(zeroI,  0, char.class), predII, stepII, "loop initializer must match: (char)int != (int)int"},
+        };
+    }
+
+    // tweak the type of an MH
+    static MethodHandle tweak(MethodHandle mh, int argPos, Class<?> type) {
+        MethodType mt = mh.type();
+        if (argPos == -1)
+            mt = mt.changeReturnType(type);
+        else
+            mt = mt.changeParameterType(argPos, type);
+        return MethodHandles.explicitCastArguments(mh, mt);
+    }
+    // snip off an MH argument, hard-wiring to zero
+    static MethodHandle snip(MethodHandle mh, int argPos) {
+        if (argPos < 0)  return null;  // special case for optional args
+        Class<?> argType = mh.type().parameterType(argPos);
+        Object zero;
+        try {
+            zero = MethodHandles.zero(argType).invoke();
+        } catch (Throwable ex) {
+            throw new AssertionError(ex);
+        }
+        return MethodHandles.insertArguments(mh, argPos, zero);
+    }
+    static MethodHandle snip(MethodHandle mh) {
+        return snip(mh, mh.type().parameterCount()-1);
+    }
+    // slap on an extra type on the end of the MH
+    static MethodHandle slap(MethodHandle mh, Class<?> addType) {
+        return MethodHandles.dropArguments(mh, mh.type().parameterCount(), addType);
     }
 
     @Test
@@ -231,22 +331,42 @@
         assertEquals("a", loop.invoke());
     }
 
-    @Test
-    public static void testDoWhileLoop() throws Throwable {
+    @Test(dataProvider = "whileLoopTestData")
+    public static void testDoWhileLoop(MethodHandle MH_zero,
+                                       MethodHandle MH_pred,
+                                       MethodHandle MH_step,
+                                       String messageOrNull) throws Throwable {
         // int i = 0; do { ++i; } while (i < limit); return i; => limit
-        MethodHandle loop = MethodHandles.doWhileLoop(While.MH_zero, While.MH_step, While.MH_pred);
-        assertEquals(While.MT_while, loop.type());
-        assertEquals(23, loop.invoke(23));
+        try {
+            MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
+            assert messageOrNull == null;
+            if (MH_step.type().equals(While.MH_step.type()))
+                assertEquals(While.MT_while, loop.type());
+            assertEquals(MH_step.type().dropParameterTypes(0, 1), loop.type());
+            while (loop.type().parameterCount() > 1)  loop = snip(loop);
+            assertEquals(23, loop.invoke(23));
+        } catch (IllegalArgumentException iae) {
+            assert messageOrNull != null;
+            if (!messageOrNull.equals(iae.getMessage())) {
+                // just issue a warning
+                System.out.println("*** "+messageOrNull+"\n != "+iae.getMessage());
+            }
+        }
     }
 
     @Test
-    public static void testDoWhileNullInit() throws Throwable {
-        While w = new While();
-        int v = 5;
-        MethodHandle loop = MethodHandles.doWhileLoop(null, While.MH_voidBody.bindTo(w), While.MH_voidPred.bindTo(w));
-        assertEquals(While.MT_void, loop.type());
-        loop.invoke(v);
-        assertEquals(v, w.i);
+    public static void testDoWhileBadInit() throws Throwable {
+        boolean caught = false;
+        try {
+            While w = new While();
+            MethodHandle loop = MethodHandles.doWhileLoop(MethodHandles.empty(methodType(char.class)),
+                                                          While.MH_voidBody.bindTo(w),
+                                                          While.MH_voidPred.bindTo(w));
+        } catch (IllegalArgumentException iae) {
+            assertEquals("loop initializer must match: ()char != (int)void", iae.getMessage());
+            caught = true;
+        }
+        assertTrue(caught);
     }
 
     @Test
@@ -260,13 +380,18 @@
     }
 
     @Test
-    public static void testWhileNullInit() throws Throwable {
-        While w = new While();
-        int v = 5;
-        MethodHandle loop = MethodHandles.whileLoop(null, While.MH_voidPred.bindTo(w), While.MH_voidBody.bindTo(w));
-        assertEquals(While.MT_void, loop.type());
-        loop.invoke(v);
-        assertEquals(v, w.i);
+    public static void testWhileBadInit() throws Throwable {
+        boolean caught = false;
+        try {
+            While w = new While();
+            MethodHandle loop = MethodHandles.whileLoop(MethodHandles.empty(methodType(void.class, char.class)),
+                                                        While.MH_voidPred.bindTo(w),
+                                                        While.MH_voidBody.bindTo(w));
+        } catch (IllegalArgumentException iae) {
+            assertEquals("loop initializer must match: (char)void != (int)void", iae.getMessage());
+            caught = true;
+        }
+        assertTrue(caught);
     }
 
     @Test
@@ -291,10 +416,26 @@
         assertEquals(v, w.i);
     }
 
+    @DataProvider
+    static Object[][] nullArgs() {
+        MethodHandle c = MethodHandles.constant(int.class, 1);
+        return new Object[][]{{null, c}, {c, null}};
+    }
+
+    @Test(dataProvider = "nullArgs", expectedExceptions = NullPointerException.class)
+    public static void testWhileNullArgs(MethodHandle pred, MethodHandle body) {
+        MethodHandles.whileLoop(null, pred, body);
+    }
+
+    @Test(dataProvider = "nullArgs", expectedExceptions = NullPointerException.class)
+    public static void testDoWhileNullArgs(MethodHandle body, MethodHandle pred) {
+        MethodHandles.whileLoop(null, body, pred);
+    }
+
     @Test
     public static void testCountedLoop() throws Throwable {
         // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; => a variation on a well known theme
-        MethodHandle fit13 = MethodHandles.constant(int.class, 13);
+        MethodHandle fit13 = MethodHandles.dropArguments(MethodHandles.constant(int.class, 13), 0, String.class);
         MethodHandle loop = MethodHandles.countedLoop(fit13, Counted.MH_start, Counted.MH_step);
         assertEquals(Counted.MT_counted, loop.type());
         assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
@@ -303,9 +444,25 @@
     @Test
     public static void testCountedLoopVoidInit() throws Throwable {
         MethodHandle fit5 = MethodHandles.constant(int.class, 5);
-        MethodHandle loop = MethodHandles.countedLoop(fit5, MethodHandles.zero(void.class), Counted.MH_printHello);
-        assertEquals(Counted.MT_countedPrinting, loop.type());
-        loop.invoke();
+        for (int i = 0; i < 8; i++) {
+            MethodHandle zero = MethodHandles.zero(void.class);
+            MethodHandle init = fit5;
+            MethodHandle body = Counted.MH_printHello;
+            boolean useNull = (i & 1) != 0, addInitArg = (i & 2) != 0, addBodyArg = (i & 4) != 0;
+            if (useNull)    zero = null;
+            if (addInitArg) init = MethodHandles.dropArguments(init, 0, int.class);
+            if (addBodyArg) body = MethodHandles.dropArguments(body, 1, int.class);
+            System.out.println("testCountedLoopVoidInit i="+i+" : "+Arrays.asList(init, zero, body));
+            MethodHandle loop = MethodHandles.countedLoop(init, zero, body);
+            MethodType expectedType = Counted.MT_countedPrinting;
+            if (addInitArg || addBodyArg)
+                expectedType = expectedType.insertParameterTypes(0, int.class);
+            assertEquals(expectedType, loop.type());
+            if (addInitArg || addBodyArg)
+                loop.invoke(99);
+            else
+                loop.invoke();
+        }
     }
 
     @Test
@@ -327,7 +484,7 @@
         loop.invoke();
     }
 
-    @Test
+    @Test(expectedExceptions = NullPointerException.class)
     public static void testCountedLoopNullBody() throws Throwable {
         MethodHandle h5 = MethodHandles.constant(int.class, 5);
         MethodHandle h13 = MethodHandles.constant(int.class, 13);
@@ -336,14 +493,14 @@
         assertEquals(13, loop.invoke());
     }
 
-    @Test
+    @Test(expectedExceptions = NullPointerException.class)
     public static void testCountedLoopNullIterations() throws Throwable {
         MethodHandle loop = MethodHandles.countedLoop(null, null, null);
         assertEquals(methodType(void.class), loop.type());
         loop.invoke();
     }
 
-    @Test
+    @Test(expectedExceptions = NullPointerException.class)
     public static void testCountedLoopNullInitAndBody() throws Throwable {
         MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null, null);
         assertEquals(methodType(void.class), loop.type());
@@ -352,45 +509,63 @@
 
     @DataProvider
     static Object[][] countedLoopBodyParameters() {
+        Class<?> V = String.class, I = int.class, A = List.class;
+        // return types are of these forms:
+        //    {count = int(A...), init = V(A...), body = V(V, I, A...)}
         return new Object[][] {
-                {methodType(String.class), methodType(String.class, int.class)},
-                {methodType(String.class, List.class), methodType(String.class, int.class)},
-                {methodType(String.class, List.class), methodType(String.class, int.class, String.class)}
+            // body leads determining A...
+            {methodType(I), methodType(V), methodType(V, V, I)},
+            {methodType(I), methodType(V), methodType(V, V, I, A)},
+            {methodType(I,A), methodType(V), methodType(V, V, I, A)},
+            {methodType(I), methodType(V,A), methodType(V, V, I, A)},
+            // body leads, with void V
+            {methodType(I), methodType(void.class), methodType(void.class, I)},
+            {methodType(I), methodType(void.class), methodType(void.class, I, A)},
+            {methodType(I,A), methodType(void.class), methodType(void.class, I, A)},
+            {methodType(I), methodType(void.class,A), methodType(void.class, I, A)},
+            // count leads determining A..., but only if body drops all A...
+            {methodType(I,A), methodType(V), methodType(V, V, I)},
+            {methodType(I,A), methodType(V,A), methodType(V, V, I)},
+            // count leads, with void V
+            {methodType(I,A), methodType(void.class), methodType(void.class, I)},
+            {methodType(I,A), methodType(void.class,A), methodType(void.class, I)},
         };
     }
 
     @Test(dataProvider = "countedLoopBodyParameters")
-    public static void testCountedLoopBodyParameters(MethodType initType, MethodType bodyType) throws Throwable {
-        MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5),
-                MethodHandles.empty(initType), MethodHandles.empty(bodyType));
-        assertEquals(initType, loop.type());
+    public static void testCountedLoopBodyParameters(MethodType countType, MethodType initType, MethodType bodyType) throws Throwable {
+        MethodHandle loop = MethodHandles.countedLoop(
+                MethodHandles.empty(countType),
+                initType == null ? null : MethodHandles.empty(initType),
+                MethodHandles.empty(bodyType));
+        // The rule:  If body takes the minimum number of parameters, then take what countType offers.
+        // The initType has to just roll with whatever the other two agree on.
+        int innerParams = (bodyType.returnType() == void.class ? 1 : 2);
+        MethodType expectType = bodyType.dropParameterTypes(0, innerParams);
+        if (expectType.parameterCount() == 0)
+            expectType = expectType.insertParameterTypes(0, countType.parameterList());
+        assertEquals(expectType, loop.type());
     }
 
-    @DataProvider
-    static Object[][] countedLoopTypes() {
-        return new Object[][]{{void.class}, {int.class}, {Object.class}, {String.class}, {List.class}};
-    }
-
-    @Test(dataProvider = "countedLoopTypes")
-    public static void testCountedLoopBodyParametersNullInit(Class<?> t) throws Throwable {
-        MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null,
-                MethodHandles.empty(methodType(t, int.class)));
-        assertEquals(methodType(t), loop.type());
-        loop.invoke();
+    @Test(dataProvider = "countedLoopBodyParameters")
+    public static void testCountedLoopBodyParametersNullInit(MethodType countType, MethodType initType, MethodType bodyType) throws Throwable {
+        testCountedLoopBodyParameters(countType, null, bodyType);
     }
 
     @Test
-    public static void testCountedLoopStateDefinedByBody() throws Throwable {
-        MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null, Counted.MH_stateBody);
+    public static void testCountedLoopStateInitializedToNull() throws Throwable {
+        MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5),
+                MethodHandles.empty(methodType(String.class)), Counted.MH_stateBody);
         assertEquals(Counted.MT_bodyDeterminesState, loop.type());
         assertEquals("sssssnull01234", loop.invoke());
     }
 
     @Test
     public static void testCountedLoopArgsDefinedByIterations() throws Throwable {
-        MethodHandle loop = MethodHandles.countedLoop(
-                MethodHandles.dropArguments(MethodHandles.constant(int.class, 3), 0, String.class),
-                null, Counted.MH_append);
+        MethodHandle iterations =
+                MethodHandles.dropArguments(MethodHandles.constant(int.class, 3), 0, String.class);
+        MethodHandle loop = MethodHandles.countedLoop(iterations,
+                MethodHandles.empty(iterations.type().changeReturnType(String.class)), Counted.MH_append);
         assertEquals(Counted.MT_iterationsDefineArgs, loop.type());
         assertEquals("hello012", loop.invoke("hello"));
     }
@@ -420,7 +595,8 @@
     @Test
     public static void testCountedLoopEmpty() throws Throwable {
         // for (int i = 0; i < 5; ++i) { /* empty */ }
-        MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null, null);
+        MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null,
+                MethodHandles.empty(methodType(void.class, int.class)));
         assertEquals(methodType(void.class), loop.type());
         loop.invoke();
     }
@@ -429,11 +605,45 @@
     public static void testCountedRangeLoopEmpty() throws Throwable {
         // for (int i = -5; i < 5; ++i) { /* empty */ }
         MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, -5),
-                MethodHandles.constant(int.class, 5), null, null);
+                MethodHandles.constant(int.class, 5), null, MethodHandles.empty(methodType(void.class, int.class)));
         assertEquals(methodType(void.class), loop.type());
         loop.invoke();
     }
 
+    @DataProvider
+    static Object[][] countedLoopNegativeData() {
+        MethodHandle dummy = MethodHandles.zero(void.class);
+        MethodHandle one = MethodHandles.constant(int.class, 1);
+        MethodHandle oneString = MethodHandles.dropArguments(one, 0, String.class);
+        MethodHandle oneDouble = MethodHandles.dropArguments(one, 0, double.class);
+        return new Object[][]{
+                {dummy, one, dummy, dummy, String.format("start/end must return int %s, %s", dummy, one)},
+                {one, dummy, dummy, dummy, String.format("start/end must return int %s, %s", one, dummy)},
+                {oneString, oneDouble, dummy, dummy,
+                        String.format("start and end parameter types must match: %s != %s", oneString.type(),
+                                oneDouble.type())},
+                {oneString, oneString, dummy, dummy,
+                        String.format("start/end and init parameter types must match: %s != %s", oneString.type(),
+                                dummy.type())},
+                {one, one, null, dummy, String.format("actual and expected body signatures must match: %s != %s",
+                        dummy.type(), dummy.type().appendParameterTypes(int.class))}
+        };
+    }
+
+    @Test(dataProvider = "countedLoopNegativeData")
+    public static void testCountedLoopNegative(MethodHandle start, MethodHandle end, MethodHandle init,
+                                               MethodHandle body, String msg) {
+        if (true)  return;  //%%%FIXME%%%%
+        boolean caught = false;
+        try {
+            MethodHandles.countedLoop(start, end, init, body);
+        } catch (IllegalArgumentException iae) {
+            assertEquals(msg, iae.getMessage());
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+
     @Test
     public static void testIterateSum() throws Throwable {
         // Integer[] a = new Integer[]{1,2,3,4,5,6}; int sum = 0; for (int e : a) { sum += e; } return sum; => 21
@@ -442,50 +652,106 @@
         assertEquals(21, loop.invoke(new Integer[]{1, 2, 3, 4, 5, 6}));
     }
 
-    @Test
-    public static void testIterateReverse() throws Throwable {
-        MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_reverseInit, Iterate.MH_reverseStep);
-        assertEquals(Iterate.MT_reverse, loop.type());
-        List<String> list = Arrays.asList("a", "b", "c", "d", "e");
-        List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a");
-        assertEquals(reversedList, (List<String>) loop.invoke(list));
+    @DataProvider
+    static Object[][] iteratorInits() {
+        return new Object[][]{{Iterate.MH_iteratorFromList}, {Iterate.MH_iteratorFromIterable}, {null}};
     }
 
-    @Test
-    public static void testIterateLength() throws Throwable {
-        MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_lengthInit, Iterate.MH_lengthStep);
-        assertEquals(Iterate.MT_length, loop.type());
-        List<Double> list = Arrays.asList(23.0, 148.0, 42.0);
-        assertEquals(list.size(), (int) loop.invoke(list));
+    @Test(dataProvider = "iteratorInits")
+    public static void testIterateReverse(MethodHandle iterator) throws Throwable {
+        // this test uses List as its loop state type; don't try to change that
+        if (iterator != null)
+            iterator = iterator.asType(iterator.type().changeParameterType(0, List.class));
+        for (int i = 0; i < 4; i++) {
+            MethodHandle init = Iterate.MH_reverseInit, body = Iterate.MH_reverseStep;
+            boolean snipInit = (i & 1) != 0, snipBody = (i & 2) != 0;
+            if (snipInit)  init = snip(init);
+            if (snipBody)  body = snip(body);
+            if (!snipInit && snipBody && iterator == null) {
+                // Body does not determine (A...), so the default guy just picks Iterable.
+                // If body insisted on (List), the default guy would adjust himself.
+                // Init has no authority to change the (A...), so must patch init.
+                // All according to plan!
+                init = slap(snip(init), Iterable.class);
+            }
+            System.out.println("testIterateReverse i="+i+" : "+Arrays.asList(iterator, init, body));
+            MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, body);
+            MethodType expectedType = Iterate.MT_reverse;
+            if (iterator == null && i >= 2)
+                expectedType = expectedType.changeParameterType(0, Iterable.class);
+            assertEquals(expectedType, loop.type());
+            List<String> list = Arrays.asList("a", "b", "c", "d", "e");
+            List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a");
+            assertEquals(reversedList, (List<String>) loop.invoke(list));
+        }
     }
 
-    @Test
-    public static void testIterateMap() throws Throwable {
-        MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_mapInit, Iterate.MH_mapStep);
-        assertEquals(Iterate.MT_map, loop.type());
-        List<String> list = Arrays.asList("Hello", "world", "!");
-        List<String> upList = Arrays.asList("HELLO", "WORLD", "!");
-        assertEquals(upList, (List<String>) loop.invoke(list));
+    @Test(dataProvider = "iteratorInits")
+    public static void testIterateLength(MethodHandle iterator) throws Throwable {
+        MethodHandle body = Iterate.MH_lengthStep;
+        MethodHandle init = Iterate.MH_lengthInit;
+        MethodType expectedType = Iterate.MT_length;
+        int barity = body.type().parameterCount();
+        Class<?> iteratorSource = iterator == null ? null : iterator.type().parameterType(0);
+        if (iterator != null && iteratorSource != body.type().parameterType(barity-1)) {
+            // adjust body to accept the other type
+            body = body.asType(body.type().changeParameterType(barity-1, iteratorSource));
+            init = init.asType(init.type().changeParameterType(0, iteratorSource));
+            expectedType = expectedType.changeParameterType(0, iteratorSource);
+        }
+        for (;; init = snip(init)) {
+            System.out.println("testIterateLength.init = "+init);
+            MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, body);
+            assertEquals(expectedType, loop.type());
+            List<Double> list = Arrays.asList(23.0, 148.0, 42.0);
+            assertEquals(list.size(), (int) loop.invoke(list));
+            if (init == null)  break;
+        }
     }
 
-    @Test
-    public static void testIteratePrint() throws Throwable {
-        MethodHandle loop = MethodHandles.iteratedLoop(null, null, Iterate.MH_printStep);
-        assertEquals(Iterate.MT_print, loop.type());
+    @Test(dataProvider = "iteratorInits")
+    public static void testIterateMap(MethodHandle iterator) throws Throwable {
+        MethodHandle body = Iterate.MH_mapStep;
+        MethodHandle init = Iterate.MH_mapInit;
+        MethodType expectedType = Iterate.MT_map;
+        int barity = body.type().parameterCount();
+        Class<?> iteratorSource = iterator == null ? null : iterator.type().parameterType(0);
+        if (iterator != null && iteratorSource != body.type().parameterType(barity-1)) {
+            // adjust body to accept the other type
+            body = body.asType(body.type().changeParameterType(barity-1, iteratorSource));
+            init = init.asType(init.type().changeParameterType(0, iteratorSource));
+            expectedType = expectedType.changeParameterType(0, iteratorSource);
+        }
+        for (; init != null; init = snip(init)) {
+            System.out.println("testIterateMap.init = "+init);
+            MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, body);
+            assertEquals(expectedType, loop.type());
+            List<String> list = Arrays.asList("Hello", "world", "!");
+            List<String> upList = Arrays.asList("HELLO", "WORLD", "!");
+            assertEquals(upList, (List<String>) loop.invoke(list));
+        }
+    }
+
+    @Test(dataProvider = "iteratorInits")
+    public static void testIteratePrint(MethodHandle iterator) throws Throwable {
+        MethodHandle body = Iterate.MH_printStep;
+        MethodType expectedType = Iterate.MT_print;
+        int barity = body.type().parameterCount();
+        Class<?> iteratorSource = iterator == null ? null : iterator.type().parameterType(0);
+        if (iterator != null && iteratorSource != body.type().parameterType(barity-1)) {
+            // adjust body to accept the other type
+            body = body.asType(body.type().changeParameterType(barity-1, iteratorSource));
+            expectedType = expectedType.changeParameterType(0, iteratorSource);
+        }
+        MethodHandle loop = MethodHandles.iteratedLoop(iterator, null, body);
+        assertEquals(expectedType, loop.type());
         loop.invoke(Arrays.asList("hello", "world"));
     }
 
-    @Test
+    @Test(expectedExceptions = NullPointerException.class)
     public static void testIterateNullBody() {
-        boolean caught = false;
-        try {
-            MethodHandles.iteratedLoop(MethodHandles.empty(methodType(Iterator.class, int.class)),
-                    MethodHandles.identity(int.class), null);
-        } catch (IllegalArgumentException iae) {
-            assertEquals("iterated loop body must not be null", iae.getMessage());
-            caught = true;
-        }
-        assertTrue(caught);
+        MethodHandles.iteratedLoop(MethodHandles.empty(methodType(Iterator.class, int.class)),
+                MethodHandles.identity(int.class), null);
     }
 
     @DataProvider
@@ -500,15 +766,18 @@
         try {
             MethodHandles.iteratedLoop(MethodHandles.empty(v), null, MethodHandles.empty(v));
         } catch(IllegalArgumentException iae) {
-            assertEquals("iteratedLoop first argument must have Iterator return type", iae.getMessage());
+            assertEqualsFIXME("iteratedLoop first argument must have Iterator return type", iae.getMessage());
             caught = true;
         }
         assertTrue(caught);
     }
 
-    @Test
-    public static void testIterateVoidInit() throws Throwable {
-        MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_voidInit, Iterate.MH_printStep);
+    @Test(dataProvider = "iteratorInits")
+    public static void testIterateVoidInit(MethodHandle iterator) throws Throwable {
+        // this test uses List as its loop state type; don't try to change that
+        if (iterator != null)
+            iterator = iterator.asType(iterator.type().changeParameterType(0, List.class));
+        MethodHandle loop = MethodHandles.iteratedLoop(iterator, Iterate.MH_voidInit, Iterate.MH_printStep);
         assertEquals(Iterate.MT_print, loop.type());
         loop.invoke(Arrays.asList("hello", "world"));
     }
@@ -516,60 +785,79 @@
     @DataProvider
     static Object[][] iterateParameters() {
         MethodType i = methodType(int.class);
-        MethodType sil_i = methodType(int.class, String.class, int.class, List.class);
+        MethodType sil_v = methodType(void.class, String.class, int.class, List.class);
+        MethodType isl_i = methodType(int.class, int.class, String.class, List.class);
+        MethodType isli_i = methodType(int.class, int.class, String.class, List.class, int.class);
         MethodType sl_v = methodType(void.class, String.class, List.class);
+        MethodType sli_v = methodType(void.class, String.class, List.class, int.class);
         MethodType l_it = methodType(Iterator.class, List.class);
+        MethodType li_i = methodType(int.class, List.class, int.class);
         MethodType li_it = methodType(Iterator.class, List.class, int.class);
+        MethodType il_it = methodType(Iterator.class, int.class, List.class);
         MethodType l_i = methodType(int.class, List.class);
-        MethodType _it = methodType(Iterator.class);
-        MethodType si_i = methodType(int.class, String.class, int.class);
-        MethodType s_i = methodType(int.class, String.class);
         return new Object[][]{
-                {null, null, sl_v},
-                {null, i, sil_i},
-                {null, l_i, sil_i},
-                {l_it, null, sl_v},
-                {l_it, i, sil_i},
-                {li_it, l_i, sil_i},
-                {l_it, null, sil_i},
-                {li_it, null, sl_v},
-                {_it, l_i, si_i},
-                {_it, l_i, s_i}
+                {l_it, null, sl_v, ""},
+                {l_it, l_i, isl_i, ""},
+                {l_it, null, sl_v, ""},
+                {li_it, li_i, isli_i, ""},
+                {il_it, null, sil_v, "inferred first loop argument must inherit from Iterable: int"},
+                {li_it, null, sli_v, ""},
+                {sl_v, null, sl_v, "iteratedLoop first argument must have Iterator return type"},
+                {li_it, l_it, sl_v,
+                        String.format("iterator and init parameter lists must match: %s != %s", li_it, l_it)},
+                {li_it, li_i, isl_i,
+                        String.format("body types (regard parameter types after index 0, and result type) must match: %s != %s",
+                                isl_i, isl_i.dropParameterTypes(0, 1).appendParameterTypes(int.class))}
         };
     }
 
     @Test(dataProvider = "iterateParameters")
-    public static void testIterateParameters(MethodType it, MethodType in, MethodType bo) throws Throwable {
+    public static void testIterateParameters(MethodType it, MethodType in, MethodType bo, String msg) {
+        boolean negative = !msg.isEmpty();
         MethodHandle iterator = it == null ? null : MethodHandles.empty(it);
         MethodHandle init = in == null ? null : MethodHandles.empty(in);
-        MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, MethodHandles.empty(bo));
-        MethodType lt = loop.type();
-        if (it == null && in == null) {
-            assertEquals(bo.dropParameterTypes(0, 1), lt);
-        } else if (it == null) {
-            if (in.parameterCount() == 0) {
-                assertEquals(bo.dropParameterTypes(0, in.returnType() == void.class ? 1 : 2), lt);
+        boolean caught = false;
+        MethodHandle loop = null;
+        try {
+            loop = MethodHandles.iteratedLoop(iterator, init, MethodHandles.empty(bo));
+        } catch (Throwable t) {
+            if (!negative) {
+                throw t;
+            }
+            assertEqualsFIXME(msg, t.getMessage());
+            caught = true;
+        }
+        if (negative) {
+            assertTrue(caught);
+        } else {
+            MethodType lt = loop.type();
+            if (it == null && in == null) {
+                assertEquals(bo.dropParameterTypes(0, 1), lt);
+            } else if (it == null) {
+                if (in.parameterCount() == 0) {
+                    assertEquals(bo.dropParameterTypes(0, in.returnType() == void.class ? 1 : 2), lt);
+                } else {
+                    assertEquals(methodType(bo.returnType(), in.parameterArray()), lt);
+                }
+            } else if (in == null) {
+                assertEquals(methodType(bo.returnType(), it.parameterArray()), lt);
+            } else if (it.parameterCount() > in.parameterCount()) {
+                assertEquals(methodType(bo.returnType(), it.parameterArray()), lt);
+            } else if (it.parameterCount() < in.parameterCount()) {
+                assertEquals(methodType(bo.returnType(), in.parameterArray()), lt);
             } else {
-                assertEquals(methodType(bo.returnType(), in.parameterArray()), lt);
+                // both it, in present; with equal parameter list lengths
+                assertEquals(it.parameterList(), lt.parameterList());
+                assertEquals(in.parameterList(), lt.parameterList());
+                assertEquals(bo.returnType(), lt.returnType());
             }
-        } else if (in == null) {
-            assertEquals(methodType(bo.returnType(), it.parameterArray()), lt);
-        } else if (it.parameterCount() > in.parameterCount()) {
-            assertEquals(methodType(bo.returnType(), it.parameterArray()), lt);
-        } else if (it.parameterCount() < in.parameterCount()) {
-            assertEquals(methodType(bo.returnType(), in.parameterArray()), lt);
-        } else {
-            // both it, in present; with equal parameter list lengths
-            assertEquals(it.parameterList(), lt.parameterList());
-            assertEquals(in.parameterList(), lt.parameterList());
-            assertEquals(bo.returnType(), lt.returnType());
         }
     }
 
     @Test
     public static void testIteratorSubclass() throws Throwable {
         MethodHandle loop = MethodHandles.iteratedLoop(MethodHandles.empty(methodType(BogusIterator.class, List.class)),
-                null, MethodHandles.empty(methodType(void.class, String.class)));
+                null, MethodHandles.empty(methodType(void.class, String.class, List.class)));
         assertEquals(methodType(void.class, List.class), loop.type());
     }
 
@@ -892,7 +1180,7 @@
             return arg;
         }
 
-        static String step(int counter, String v, String arg) {
+        static String step(String v, int counter) {
             return "na " + v;
         }
 
@@ -904,15 +1192,15 @@
             System.out.print("hello");
         }
 
-        static int addCounter(int counter, int x) {
+        static int addCounter(int x, int counter) {
             return x + counter;
         }
 
-        static String stateBody(int counter, String s) {
+        static String stateBody(String s, int counter) {
             return "s" + s + counter;
         }
 
-        static String append(int counter, String localState, String loopArg) {
+        static String append(String localState, int counter, String loopArg) {
             if (null == localState) {
                 return loopArg + counter;
             }
@@ -922,12 +1210,12 @@
         static final Class<Counted> COUNTED = Counted.class;
 
         static final MethodType MT_start = methodType(String.class, String.class);
-        static final MethodType MT_step = methodType(String.class, int.class, String.class, String.class);
+        static final MethodType MT_step = methodType(String.class, String.class, int.class);
         static final MethodType MT_stepUpdateArray = methodType(void.class, int.class, int[].class);
         static final MethodType MT_printHello = methodType(void.class, int.class);
         static final MethodType MT_addCounter = methodType(int.class, int.class, int.class);
-        static final MethodType MT_stateBody = methodType(String.class, int.class, String.class);
-        static final MethodType MT_append = methodType(String.class, int.class, String.class, String.class);
+        static final MethodType MT_stateBody = methodType(String.class, String.class, int.class);
+        static final MethodType MT_append = methodType(String.class, String.class, int.class, String.class);
 
         static final MethodHandle MH_13;
         static final MethodHandle MH_m5;
@@ -984,7 +1272,7 @@
             return new ArrayList<>();
         }
 
-        static List<String> reverseStep(String e, List<String> r, List<String> l) {
+        static List<String> reverseStep(List<String> r, String e, List<String> l) {
             r.add(0, e);
             return r;
         }
@@ -993,7 +1281,7 @@
             return 0;
         }
 
-        static int lengthStep(Object o, int len, List<Double> l) {
+        static int lengthStep(int len, Object o, List<Double> l) {
             return len + 1;
         }
 
@@ -1001,7 +1289,7 @@
             return new ArrayList<>();
         }
 
-        static List<String> mapStep(String e, List<String> r, List<String> l) {
+        static List<String> mapStep(List<String> r, String e, List<String> l) {
             r.add(e.toUpperCase());
             return r;
         }
@@ -1010,10 +1298,18 @@
             System.out.print(s);
         }
 
-        static void voidInit() {
+        static void voidInit(List<String> l) {
             // empty
         }
 
+        static ListIterator<?> iteratorFromList(List<?> l) {
+            return l.listIterator();
+        }
+
+        static Iterator<?> iteratorFromIterable(Iterable<?> l) {
+            return l.iterator();
+        }
+
         static final Class<Iterate> ITERATE = Iterate.class;
 
         static final MethodType MT_sumIterator = methodType(Iterator.class, Integer[].class);
@@ -1024,12 +1320,15 @@
         static final MethodType MT_mapInit = methodType(List.class, List.class);
 
         static final MethodType MT_sumStep = methodType(int.class, int.class, int.class, Integer[].class);
-        static final MethodType MT_reverseStep = methodType(List.class, String.class, List.class, List.class);
-        static final MethodType MT_lengthStep = methodType(int.class, Object.class, int.class, List.class);
-        static final MethodType MT_mapStep = methodType(List.class, String.class, List.class, List.class);
+        static final MethodType MT_reverseStep = methodType(List.class, List.class, String.class, List.class);
+        static final MethodType MT_lengthStep = methodType(int.class, int.class, Object.class, List.class);
+        static final MethodType MT_mapStep = methodType(List.class, List.class, String.class, List.class);
         static final MethodType MT_printStep = methodType(void.class, String.class, List.class);
 
-        static final MethodType MT_voidInit = methodType(void.class);
+        static final MethodType MT_voidInit = methodType(void.class, List.class);
+
+        static final MethodType MT_iteratorFromList = methodType(ListIterator.class, List.class);
+        static final MethodType MT_iteratorFromIterable = methodType(Iterator.class, Iterable.class);
 
         static final MethodHandle MH_sumIterator;
         static final MethodHandle MH_sumInit;
@@ -1047,6 +1346,9 @@
 
         static final MethodHandle MH_voidInit;
 
+        static final MethodHandle MH_iteratorFromList;
+        static final MethodHandle MH_iteratorFromIterable;
+
         static final MethodType MT_sum = methodType(int.class, Integer[].class);
         static final MethodType MT_reverse = methodType(List.class, List.class);
         static final MethodType MT_length = methodType(int.class, List.class);
@@ -1066,6 +1368,8 @@
                 MH_mapStep = LOOKUP.findStatic(ITERATE, "mapStep", MT_mapStep);
                 MH_printStep = LOOKUP.findStatic(ITERATE, "printStep", MT_printStep);
                 MH_voidInit = LOOKUP.findStatic(ITERATE, "voidInit", MT_voidInit);
+                MH_iteratorFromList = LOOKUP.findStatic(ITERATE, "iteratorFromList", MT_iteratorFromList);
+                MH_iteratorFromIterable = LOOKUP.findStatic(ITERATE, "iteratorFromIterable", MT_iteratorFromIterable);
             } catch (Exception e) {
                 throw new ExceptionInInitializerError(e);
             }