changeset 13109:957e4e29ff28

8139885: implement JEP 274: enhanced method handles Reviewed-by: jrose, psandoz, vlivanov
author mhaupt
date Fri, 20 Nov 2015 15:34:12 +0100
parents c071ebc7f3bf
children 445d56c343c7
files src/java.base/share/classes/java/lang/invoke/MethodHandle.java src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java src/java.base/share/classes/java/lang/invoke/MethodHandles.java src/java.base/share/classes/java/lang/invoke/MethodType.java test/java/lang/invoke/AccessControlTest.java test/java/lang/invoke/BigArityTest.java test/java/lang/invoke/FindClassSecurityManager.java test/java/lang/invoke/MethodHandlesTest.java test/java/lang/invoke/T8139885.java test/java/lang/invoke/findclass.security.policy
diffstat 10 files changed, 3296 insertions(+), 130 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Thu Nov 19 19:46:46 2015 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Fri Nov 20 15:34:12 2015 +0100
@@ -872,13 +872,54 @@
      * @see #asCollector
      */
     public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
-        MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength);
-        int arity = type().parameterCount();
-        int spreadArgPos = arity - arrayLength;
+        return asSpreader(type().parameterCount() - arrayLength, arrayType, arrayLength);
+    }
+
+    /**
+     * Makes an <em>array-spreading</em> method handle, which accepts an array argument at a given position and spreads
+     * its elements as positional arguments in place of the array. The new method handle adapts, as its <i>target</i>,
+     * the current method handle. The type of the adapter will be the same as the type of the target, except that the
+     * {@code arrayLength} parameters of the target's type, starting at the zero-based position {@code spreadArgPos},
+     * are replaced by a single array parameter of type {@code arrayType}.
+     * <p>
+     * This method behaves very much like {@link #asSpreader(Class, int)}, but accepts an additional {@code spreadArgPos}
+     * argument to indicate at which position in the parameter list the spreading should take place.
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+    MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
+    MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
+    Object[] ints = new Object[]{3, 9, 7, 7};
+    Comparator<Integer> cmp = (a, b) -> a - b;
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
+     * }</pre></blockquote>
+     * @param spreadArgPos the position (zero-based index) in the argument list at which spreading should start.
+     * @param arrayType usually {@code Object[]}, the type of the array argument from which to extract the spread arguments
+     * @param arrayLength the number of arguments to spread from an incoming array argument
+     * @return a new method handle which spreads an array argument at a given position,
+     *         before calling the original method handle
+     * @throws NullPointerException if {@code arrayType} is a null reference
+     * @throws IllegalArgumentException if {@code arrayType} is not an array type,
+     *         or if target does not have at least
+     *         {@code arrayLength} parameter types,
+     *         or if {@code arrayLength} is negative,
+     *         or if {@code spreadArgPos} has an illegal value (negative, or together with arrayLength exceeding the
+     *         number of arguments),
+     *         or if the resulting method handle's type would have
+     *         <a href="MethodHandle.html#maxarity">too many parameters</a>
+     * @throws WrongMethodTypeException if the implied {@code asType} call fails
+     *
+     * @see #asSpreader(Class, int)
+     * @since 9
+     */
+    public MethodHandle asSpreader(int spreadArgPos, Class<?> arrayType, int arrayLength) {
+        MethodType postSpreadType = asSpreaderChecks(arrayType, spreadArgPos, arrayLength);
         MethodHandle afterSpread = this.asType(postSpreadType);
         BoundMethodHandle mh = afterSpread.rebind();
         LambdaForm lform = mh.editor().spreadArgumentsForm(1 + spreadArgPos, arrayType, arrayLength);
-        MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, arity, arrayType);
+        MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, spreadArgPos + arrayLength, arrayType);
         return mh.copyWith(preSpreadType, lform);
     }
 
@@ -886,15 +927,18 @@
      * See if {@code asSpreader} can be validly called with the given arguments.
      * Return the type of the method handle call after spreading but before conversions.
      */
-    private MethodType asSpreaderChecks(Class<?> arrayType, int arrayLength) {
+    private MethodType asSpreaderChecks(Class<?> arrayType, int pos, int arrayLength) {
         spreadArrayChecks(arrayType, arrayLength);
         int nargs = type().parameterCount();
         if (nargs < arrayLength || arrayLength < 0)
             throw newIllegalArgumentException("bad spread array length");
+        if (pos < 0 || pos + arrayLength > nargs) {
+            throw newIllegalArgumentException("bad spread position");
+        }
         Class<?> arrayElement = arrayType.getComponentType();
         MethodType mtype = type();
         boolean match = true, fail = false;
-        for (int i = nargs - arrayLength; i < nargs; i++) {
+        for (int i = pos; i < arrayLength; i++) {
             Class<?> ptype = mtype.parameterType(i);
             if (ptype != arrayElement) {
                 match = false;
@@ -905,7 +949,7 @@
             }
         }
         if (match)  return mtype;
-        MethodType needType = mtype.asSpreaderType(arrayType, arrayLength);
+        MethodType needType = mtype.asSpreaderType(arrayType, pos, arrayLength);
         if (!fail)  return needType;
         // elicit an error:
         this.asType(needType);
@@ -998,10 +1042,53 @@
      * @see #asVarargsCollector
      */
     public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
-        asCollectorChecks(arrayType, arrayLength);
-        int collectArgPos = type().parameterCount() - 1;
+        return asCollector(type().parameterCount() - 1, arrayType, arrayLength);
+    }
+
+    /**
+     * Makes an <em>array-collecting</em> method handle, which accepts a given number of positional arguments starting
+     * at a given position, and collects them into an array argument. The new method handle adapts, as its
+     * <i>target</i>, the current method handle. The type of the adapter will be the same as the type of the target,
+     * except that the parameter at the position indicated by {@code collectArgPos} (usually of type {@code arrayType})
+     * is replaced by {@code arrayLength} parameters whose type is element type of {@code arrayType}.
+     * <p>
+     * This method behaves very much like {@link #asCollector(Class, int)}, but differs in that its {@code
+     * collectArgPos} argument indicates at which position in the parameter list arguments should be collected. This
+     * index is zero-based.
+     * <p>
+     * @apiNote Examples:
+     * <blockquote><pre>{@code
+    StringWriter swr = new StringWriter();
+    MethodHandle swWrite = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).bindTo(swr);
+    MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4);
+    swWrite4.invoke('A', 'B', 'C', 'D', 1, 2);
+    assertEquals("BC", swr.toString());
+    swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4);
+    assertEquals("BCPQRS", swr.toString());
+    swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
+    assertEquals("BCPQRSZ", swr.toString());
+     * }</pre></blockquote>
+     * @param collectArgPos the zero-based position in the parameter list at which to start collecting.
+     * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
+     * @param arrayLength the number of arguments to collect into a new array argument
+     * @return a new method handle which collects some arguments
+     *         into an array, before calling the original method handle
+     * @throws NullPointerException if {@code arrayType} is a null reference
+     * @throws IllegalArgumentException if {@code arrayType} is not an array type
+     *         or {@code arrayType} is not assignable to this method handle's array parameter type,
+     *         or {@code arrayLength} is not a legal array size,
+     *         or {@code collectArgPos} has an illegal value (negative, or greater than the number of arguments),
+     *         or the resulting method handle's type would have
+     *         <a href="MethodHandle.html#maxarity">too many parameters</a>
+     * @throws WrongMethodTypeException if the implied {@code asType} call fails
+     *
+     * @see #asCollector(Class, int)
+     * @since 9
+     */
+    public MethodHandle asCollector(int collectArgPos, Class<?> arrayType, int arrayLength) {
+        asCollectorChecks(arrayType, collectArgPos, arrayLength);
         BoundMethodHandle mh = rebind();
-        MethodType resultType = type().asCollectorType(arrayType, arrayLength);
+        MethodType resultType = type().asCollectorType(arrayType, collectArgPos, arrayLength);
         MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength);
         LambdaForm lform = mh.editor().collectArgumentArrayForm(1 + collectArgPos, newArray);
         if (lform != null) {
@@ -1015,15 +1102,18 @@
      * See if {@code asCollector} can be validly called with the given arguments.
      * Return false if the last parameter is not an exact match to arrayType.
      */
-    /*non-public*/ boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
+    /*non-public*/ boolean asCollectorChecks(Class<?> arrayType, int pos, int arrayLength) {
         spreadArrayChecks(arrayType, arrayLength);
         int nargs = type().parameterCount();
+        if (pos < 0 || pos >= nargs) {
+            throw newIllegalArgumentException("bad collect position");
+        }
         if (nargs != 0) {
-            Class<?> lastParam = type().parameterType(nargs-1);
-            if (lastParam == arrayType)  return true;
-            if (lastParam.isAssignableFrom(arrayType))  return false;
+            Class<?> param = type().parameterType(pos);
+            if (param == arrayType)  return true;
+            if (param.isAssignableFrom(arrayType))  return false;
         }
-        throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
+        throw newIllegalArgumentException("array type not assignable to argument", this, arrayType);
     }
 
     /**
@@ -1178,7 +1268,7 @@
      */
     public MethodHandle asVarargsCollector(Class<?> arrayType) {
         Objects.requireNonNull(arrayType);
-        boolean lastMatch = asCollectorChecks(arrayType, 0);
+        boolean lastMatch = asCollectorChecks(arrayType, type().parameterCount() - 1, 0);
         if (isVarargsCollector() && lastMatch)
             return this;
         return MethodHandleImpl.makeVarargsCollector(this, arrayType);
@@ -1341,7 +1431,6 @@
         // cannot be cracked into MethodHandleInfo.
         assert viewAsTypeChecks(newType, strict);
         BoundMethodHandle mh = rebind();
-        assert(!((MethodHandle)mh instanceof DirectMethodHandle));
         return mh.copyWith(newType, mh.form);
     }
 
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Nov 19 19:46:46 2015 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Fri Nov 20 15:34:12 2015 +0100
@@ -27,16 +27,17 @@
 
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import sun.invoke.empty.Empty;
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyType;
 import sun.invoke.util.Wrapper;
-import jdk.internal.HotSpotIntrinsicCandidate;
 import sun.reflect.CallerSensitive;
 import sun.reflect.Reflection;
 import static java.lang.invoke.LambdaForm.*;
@@ -1297,7 +1298,7 @@
         @Override
         public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
             if (intrinsicName == Intrinsic.IDENTITY) {
-                MethodType resultType = type().asCollectorType(arrayType, arrayLength);
+                MethodType resultType = type().asCollectorType(arrayType, type().parameterCount() - 1, arrayLength);
                 MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength);
                 return newArray.asType(resultType);
             }
@@ -1619,17 +1620,251 @@
         }
     }
 
+    /**
+     * Assembles a loop method handle from the given handles and type information. This works by binding and configuring
+     * the {@linkplain #looper(MethodHandle[], MethodHandle[], MethodHandle[], MethodHandle[], int, int, Object[]) "most
+     * generic loop"}.
+     *
+     * @param tloop the return type of the loop.
+     * @param targs types of the arguments to be passed to the loop.
+     * @param tvars types of loop-local variables.
+     * @param init sanitized array of initializers for loop-local variables.
+     * @param step sanitited array of loop bodies.
+     * @param pred sanitized array of predicates.
+     * @param fini sanitized array of loop finalizers.
+     *
+     * @return a handle that, when invoked, will execute the loop.
+     */
+    static MethodHandle makeLoop(Class<?> tloop, List<Class<?>> targs, List<Class<?>> tvars, List<MethodHandle> init,
+                                 List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini) {
+        MethodHandle[] ainit = toArrayArgs(init);
+        MethodHandle[] astep = toArrayArgs(step);
+        MethodHandle[] apred = toArrayArgs(pred);
+        MethodHandle[] afini = toArrayArgs(fini);
+
+        MethodHandle l = getConstantHandle(MH_looper);
+
+        // Bind the statically known arguments.
+        l = MethodHandles.insertArguments(l, 0, ainit, astep, apred, afini, tvars.size(), targs.size());
+
+        // Turn the args array into an argument list.
+        l = l.asCollector(Object[].class, targs.size());
+
+        // Finally, make loop type.
+        MethodType loopType = MethodType.methodType(tloop, targs);
+        l = l.asType(loopType);
+
+        return l;
+    }
+
+    /**
+     * Converts all handles in the {@code hs} array to handles that accept an array of arguments.
+     *
+     * @param hs method handles to be converted.
+     *
+     * @return the {@code hs} array, with all method handles therein converted.
+     */
+    static MethodHandle[] toArrayArgs(List<MethodHandle> hs) {
+        return hs.stream().map(h -> h.asSpreader(Object[].class, h.type().parameterCount())).toArray(MethodHandle[]::new);
+    }
+
+    /**
+     * This method embodies the most generic loop for use by {@link MethodHandles#loop(MethodHandle[][])}. A handle on
+     * it will be transformed into a handle on a concrete loop instantiation by {@link #makeLoop}.
+     *
+     * @param init loop-local variable initializers.
+     * @param step bodies.
+     * @param pred predicates.
+     * @param fini finalizers.
+     * @param varSize number of loop-local variables.
+     * @param nArgs number of arguments passed to the loop.
+     * @param args arguments to the loop invocation.
+     *
+     * @return the result of executing the loop.
+     */
+    static Object looper(MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini,
+                         int varSize, int nArgs, Object[] args) throws Throwable {
+        Object[] varsAndArgs = new Object[varSize + nArgs];
+        for (int i = 0, v = 0; i < init.length; ++i) {
+            if (init[i].type().returnType() == void.class) {
+                init[i].invoke(args);
+            } else {
+                varsAndArgs[v++] = init[i].invoke(args);
+            }
+        }
+        System.arraycopy(args, 0, varsAndArgs, varSize, nArgs);
+        final int nSteps = step.length;
+        for (; ; ) {
+            for (int i = 0, v = 0; i < nSteps; ++i) {
+                MethodHandle p = pred[i];
+                MethodHandle s = step[i];
+                MethodHandle f = fini[i];
+                if (s.type().returnType() == void.class) {
+                    s.invoke(varsAndArgs);
+                } else {
+                    varsAndArgs[v++] = s.invoke(varsAndArgs);
+                }
+                if (!(boolean) p.invoke(varsAndArgs)) {
+                    return f.invoke(varsAndArgs);
+                }
+            }
+        }
+    }
+
+    /**
+     * This method is bound as the predicate in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle,
+     * MethodHandle) counting loops}.
+     *
+     * @param counter the counter parameter, passed in during loop execution.
+     * @param limit the upper bound of the parameter, statically bound at loop creation time.
+     *
+     * @return whether the counter has reached the limit.
+     */
+    static boolean countedLoopPredicate(int counter, int limit) {
+        return counter <= limit;
+    }
+
+    /**
+     * This method is bound as the step function in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle,
+     * MethodHandle) counting loops} to increment the counter.
+     *
+     * @param counter the loop counter.
+     *
+     * @return the loop counter incremented by 1.
+     */
+    static int countedLoopStep(int counter, int limit) {
+        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.
+     *
+     * @return an {@link Iterator} over the argument's elements.
+     */
+    static Iterator<?> initIterator(Iterable<?> it) {
+        return it.iterator();
+    }
+
+    /**
+     * This method is bound as the predicate in {@linkplain MethodHandles#iteratedLoop iterating loops}.
+     *
+     * @param it the iterator to be checked.
+     *
+     * @return {@code true} iff there are more elements to iterate over.
+     */
+    static boolean iteratePredicate(Iterator<?> it) {
+        return it.hasNext();
+    }
+
+    /**
+     * This method is bound as the step for retrieving the current value from the iterator in {@linkplain
+     * MethodHandles#iteratedLoop iterating loops}.
+     *
+     * @param it the iterator.
+     *
+     * @return the next element from the iterator.
+     */
+    static Object iterateNext(Iterator<?> it) {
+        return it.next();
+    }
+
+    /**
+     * Makes a {@code try-finally} handle that conforms to the type constraints.
+     *
+     * @param target the target to execute in a {@code try-finally} block.
+     * @param cleanup the cleanup to execute in the {@code finally} block.
+     * @param type the result type of the entire construct.
+     * @param argTypes the types of the arguments.
+     *
+     * @return a handle on the constructed {@code try-finally} block.
+     */
+    static MethodHandle makeTryFinally(MethodHandle target, MethodHandle cleanup, Class<?> type, List<Class<?>> argTypes) {
+        MethodHandle tf = getConstantHandle(type == void.class ? MH_tryFinallyVoidExec : MH_tryFinallyExec);
+
+        // Bind the statically known arguments.
+        tf = MethodHandles.insertArguments(tf, 0, target, cleanup);
+
+        // Turn the args array into an argument list.
+        tf = tf.asCollector(Object[].class, argTypes.size());
+
+        // Finally, make try-finally type.
+        MethodType tfType = MethodType.methodType(type, argTypes);
+        tf = tf.asType(tfType);
+
+        return tf;
+    }
+
+    /**
+     * A method that will be bound during construction of a {@code try-finally} handle with non-{@code void} return type
+     * by {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}.
+     *
+     * @param target the handle to wrap in a {@code try-finally} block. This will be bound.
+     * @param cleanup the handle to run in any case before returning. This will be bound.
+     * @param args the arguments to the call. These will remain as the argument list.
+     *
+     * @return whatever the execution of the {@code target} returned (it may have been modified by the execution of
+     *         {@code cleanup}).
+     * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be
+     *         passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit.
+     */
+    static Object tryFinallyExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable {
+        Throwable t = null;
+        Object r = null;
+        try {
+            r = target.invoke(args);
+        } catch (Throwable thrown) {
+            t = thrown;
+            throw t;
+        } finally {
+            r = cleanup.invoke(t, r, args);
+        }
+        return r;
+    }
+
+    /**
+     * A method that will be bound during construction of a {@code try-finally} handle with {@code void} return type by
+     * {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}.
+     *
+     * @param target the handle to wrap in a {@code try-finally} block. This will be bound.
+     * @param cleanup the handle to run in any case before returning. This will be bound.
+     * @param args the arguments to the call. These will remain as the argument list.
+     *
+     * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be
+     *         passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit.
+     */
+    static void tryFinallyVoidExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable {
+        Throwable t = null;
+        try {
+            target.invoke(args);
+        } catch (Throwable thrown) {
+            t = thrown;
+            throw t;
+        } finally {
+            cleanup.invoke(t, args);
+        }
+    }
+
     // Indexes into constant method handles:
-    private static final int
+    static final int
             MH_cast                  =  0,
             MH_selectAlternative     =  1,
             MH_copyAsPrimitiveArray  =  2,
             MH_fillNewTypedArray     =  3,
             MH_fillNewArray          =  4,
             MH_arrayIdentity         =  5,
-            MH_LIMIT                 =  6;
+            MH_looper                =  6,
+            MH_countedLoopPred       =  7,
+            MH_countedLoopStep       =  8,
+            MH_iteratePred           =  9,
+            MH_initIterator          = 10,
+            MH_iterateNext           = 11,
+            MH_tryFinallyExec        = 12,
+            MH_tryFinallyVoidExec    = 13,
+            MH_LIMIT                 = 14;
 
-    private static MethodHandle getConstantHandle(int idx) {
+    static MethodHandle getConstantHandle(int idx) {
         MethodHandle handle = HANDLES[idx];
         if (handle != null) {
             return handle;
@@ -1672,6 +1907,31 @@
                     return makeIntrinsic(IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
                             MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)),
                         Intrinsic.SELECT_ALTERNATIVE);
+                case MH_looper:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "looper", MethodType.methodType(Object.class,
+                            MethodHandle[].class, MethodHandle[].class, MethodHandle[].class, MethodHandle[].class,
+                            int.class, int.class, Object[].class));
+                case MH_countedLoopPred:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopPredicate",
+                            MethodType.methodType(boolean.class, int.class, int.class));
+                case MH_countedLoopStep:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopStep",
+                            MethodType.methodType(int.class, int.class, int.class));
+                case MH_iteratePred:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate",
+                            MethodType.methodType(boolean.class, Iterator.class));
+                case MH_initIterator:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "initIterator",
+                            MethodType.methodType(Iterator.class, Iterable.class));
+                case MH_iterateNext:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iterateNext",
+                            MethodType.methodType(Object.class, Iterator.class));
+                case MH_tryFinallyExec:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyExecutor",
+                            MethodType.methodType(Object.class, MethodHandle.class, MethodHandle.class, Object[].class));
+                case MH_tryFinallyVoidExec:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyVoidExecutor",
+                            MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, Object[].class));
             }
         } catch (ReflectiveOperationException ex) {
             throw newInternalError(ex);
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Thu Nov 19 19:46:46 2015 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Fri Nov 20 15:34:12 2015 +0100
@@ -26,10 +26,7 @@
 package java.lang.invoke;
 
 import java.lang.reflect.*;
-import java.util.BitSet;
-import java.util.List;
-import java.util.Arrays;
-import java.util.Objects;
+import java.util.*;
 
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyAccess;
@@ -39,11 +36,13 @@
 import sun.reflect.misc.ReflectUtil;
 import sun.security.util.SecurityConstants;
 import java.lang.invoke.LambdaForm.BasicType;
-import static java.lang.invoke.LambdaForm.BasicType.*;
+
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandleImpl.Intrinsic;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * This class consists exclusively of static methods that operate on or return
@@ -176,7 +175,7 @@
      * equivalent of a particular <em>bytecode behavior</em>.
      * (Bytecode behaviors are described in section 5.4.3.5 of the Java Virtual Machine Specification.)
      * Here is a summary of the correspondence between these factory methods and
-     * the behavior the resulting method handles:
+     * the behavior of the resulting method handles:
      * <table border=1 cellpadding=5 summary="lookup method behaviors">
      * <tr>
      *     <th><a name="equiv"></a>lookup expression</th>
@@ -235,6 +234,10 @@
      *     <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}</td>
      *     <td>({@code static})?<br>{@code T m(A*);}</td><td>{@code (T) aMethod.invoke(thisOrNull, arg*);}</td>
      * </tr>
+     * <tr>
+     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#findClass lookup.findClass("C")}</td>
+     *     <td>{@code class C { ... }}</td><td>{@code C.class;}</td>
+     * </tr>
      * </table>
      *
      * Here, the type {@code C} is the class or interface being searched for a member,
@@ -255,6 +258,10 @@
      * The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand
      * for reflective objects corresponding to the given members.
      * <p>
+     * The bytecode behavior for a {@code findClass} operation is a load of a constant class,
+     * as if by {@code ldc CONSTANT_Class}.
+     * The behavior is represented, not as a method handle, but directly as a {@code Class} constant.
+     * <p>
      * In cases where the given member is of variable arity (i.e., a method or constructor)
      * the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}.
      * In all other cases, the returned method handle will be of fixed arity.
@@ -423,7 +430,7 @@
      * and the Core Reflection API
      * (as found on {@link java.lang.Class Class}).
      * <p>
-     * If a security manager is present, member lookups are subject to
+     * If a security manager is present, member and class lookups are subject to
      * additional checks.
      * From one to three calls are made to the security manager.
      * Any of these calls can refuse access by throwing a
@@ -433,6 +440,8 @@
      * {@code refc} as the containing class in which the member
      * is being sought, and {@code defc} as the class in which the
      * member is actually defined.
+     * (If a class or other type is being accessed,
+     * the {@code refc} and {@code defc} values are the class itself.)
      * The value {@code lookc} is defined as <em>not present</em>
      * if the current lookup object does not have
      * <a href="MethodHandles.Lookup.html#privacc">private access</a>.
@@ -444,11 +453,16 @@
      *     then {@link SecurityManager#checkPackageAccess
      *     smgr.checkPackageAccess(refcPkg)} is called,
      *     where {@code refcPkg} is the package of {@code refc}.
-     * <li><b>Step 2:</b>
+     * <li><b>Step 2a:</b>
      *     If the retrieved member is not public and
      *     {@code lookc} is not present, then
      *     {@link SecurityManager#checkPermission smgr.checkPermission}
      *     with {@code RuntimePermission("accessDeclaredMembers")} is called.
+     * <li><b>Step 2b:</b>
+     *     If the retrieved class has a {@code null} class loader,
+     *     and {@code lookc} is not present, then
+     *     {@link SecurityManager#checkPermission smgr.checkPermission}
+     *     with {@code RuntimePermission("getClassLoader")} is called.
      * <li><b>Step 3:</b>
      *     If the retrieved member is not public,
      *     and if {@code lookc} is not present,
@@ -458,9 +472,9 @@
      *     where {@code defcPkg} is the package of {@code defc}.
      * </ul>
      * Security checks are performed after other access checks have passed.
-     * Therefore, the above rules presuppose a member that is public,
+     * Therefore, the above rules presuppose a member or class that is public,
      * or else that is being accessed from a lookup class that has
-     * rights to access the member.
+     * rights to access the member or class.
      *
      * <h1><a name="callsens"></a>Caller sensitive methods</h1>
      * A small number of Java methods have a special property called caller sensitivity.
@@ -922,6 +936,49 @@
         }
 
         /**
+         * Looks up a class by name from the lookup context defined by this {@code Lookup} object. The static
+         * initializer of the class is not run.
+         *
+         * @param targetName the fully qualified name of the class to be looked up.
+         * @return the requested class.
+         * @exception SecurityException if a security manager is present and it
+         *            <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+         * @throws LinkageError if the linkage fails
+         * @throws ClassNotFoundException if the class does not exist.
+         * @throws IllegalAccessException if the class is not accessible, using the allowed access
+         * modes.
+         * @exception SecurityException if a security manager is present and it
+         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+         * @since 9
+         */
+        public Class<?> findClass(String targetName) throws ClassNotFoundException, IllegalAccessException {
+            Class<?> targetClass = Class.forName(targetName, false, lookupClass.getClassLoader());
+            return accessClass(targetClass);
+        }
+
+        /**
+         * Determines if a class can be accessed from the lookup context defined by this {@code Lookup} object. The
+         * static initializer of the class is not run.
+         *
+         * @param targetClass the class to be access-checked
+         *
+         * @return the class that has been access-checked
+         *
+         * @throws IllegalAccessException if the class is not accessible from the lookup class, using the allowed access
+         * modes.
+         * @exception SecurityException if a security manager is present and it
+         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+         * @since 9
+         */
+        public Class<?> accessClass(Class<?> targetClass) throws IllegalAccessException {
+            if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, allowedModes)) {
+                throw new MemberName(targetClass).makeAccessException("access violation", this);
+            }
+            checkSecurityManager(targetClass, null);
+            return targetClass;
+        }
+
+        /**
          * Produces an early-bound method handle for a virtual method.
          * It will bypass checks for overriding methods on the receiver,
          * <a href="MethodHandles.Lookup.html#equiv">as if called</a> from an {@code invokespecial}
@@ -995,7 +1052,7 @@
          */
         public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
                                         Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
-            checkSpecialCaller(specialCaller);
+            checkSpecialCaller(specialCaller, refc);
             Lookup specialLookup = this.in(specialCaller);
             MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type);
             return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, findBoundCallerClass(method));
@@ -1224,7 +1281,7 @@
          * @throws NullPointerException if any argument is null
          */
         public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
-            checkSpecialCaller(specialCaller);
+            checkSpecialCaller(specialCaller, null);
             Lookup specialLookup = this.in(specialCaller);
             MemberName method = new MemberName(m, true);
             assert(method.isMethod());
@@ -1444,7 +1501,15 @@
                 ReflectUtil.checkPackageAccess(refc);
             }
 
-            // Step 2:
+            if (m == null) {  // findClass or accessClass
+                // Step 2b:
+                if (!fullPowerLookup) {
+                    smgr.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
+                }
+                return;
+            }
+
+            // Step 2a:
             if (m.isPublic()) return;
             if (!fullPowerLookup) {
                 smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
@@ -1557,11 +1622,13 @@
 
         private static final boolean ALLOW_NESTMATE_ACCESS = false;
 
-        private void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
+        private void checkSpecialCaller(Class<?> specialCaller, Class<?> refc) throws IllegalAccessException {
             int allowedModes = this.allowedModes;
             if (allowedModes == TRUSTED)  return;
             if (!hasPrivateAccess()
                 || (specialCaller != lookupClass()
+                       // ensure non-abstract methods in superinterfaces can be special-invoked
+                    && !(refc != null && refc.isInterface() && refc.isAssignableFrom(specialCaller))
                     && !(ALLOW_NESTMATE_ACCESS &&
                          VerifyAccess.isSamePackageMember(specialCaller, lookupClass()))))
                 throw new MemberName(specialCaller).
@@ -1888,7 +1955,7 @@
     MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
         if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
             throw newIllegalArgumentException("bad argument count", leadingArgCount);
-        type = type.asSpreaderType(Object[].class, type.parameterCount() - leadingArgCount);
+        type = type.asSpreaderType(Object[].class, leadingArgCount, type.parameterCount() - leadingArgCount);
         return type.invokers().spreadInvoker(leadingArgCount);
     }
 
@@ -2924,19 +2991,7 @@
      */
     public static
     MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
-        int foldPos = 0;
-        MethodType targetType = target.type();
-        MethodType combinerType = combiner.type();
-        Class<?> rtype = foldArgumentChecks(foldPos, targetType, combinerType);
-        BoundMethodHandle result = target.rebind();
-        boolean dropResult = (rtype == void.class);
-        // Note:  This may cache too many distinct LFs. Consider backing off to varargs code.
-        LambdaForm lform = result.editor().foldArgumentsForm(1 + foldPos, dropResult, combinerType.basicType());
-        MethodType newType = targetType;
-        if (!dropResult)
-            newType = newType.dropParameterTypes(foldPos, foldPos + 1);
-        result = result.copyWithExtendL(newType, lform, combiner);
-        return result;
+        return foldArguments(target, 0, combiner);
     }
 
     private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
@@ -2949,7 +3004,7 @@
                     .equals(targetType.parameterList().subList(afterInsertPos,
                                                                afterInsertPos + foldArgs))))
             ok = false;
-        if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0))
+        if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos))
             ok = false;
         if (!ok)
             throw misMatchedTypes("target and combiner types", targetType, combinerType);
@@ -3011,7 +3066,7 @@
         return MethodHandleImpl.makeGuardWithTest(test, target, fallback);
     }
 
-    static RuntimeException misMatchedTypes(String what, MethodType t1, MethodType t2) {
+    static <T> RuntimeException misMatchedTypes(String what, T t1, T t2) {
         return newIllegalArgumentException(what + " must match: " + t1 + " != " + t2);
     }
 
@@ -3057,6 +3112,7 @@
      *          the given exception type, or if the method handle types do
      *          not match in their return types and their
      *          corresponding parameters
+     * @see MethodHandles#tryFinally(MethodHandle, MethodHandle)
      */
     public static
     MethodHandle catchException(MethodHandle target,
@@ -3100,4 +3156,913 @@
             throw new ClassCastException(exType.getName());
         return MethodHandleImpl.throwException(MethodType.methodType(returnType, exType));
     }
+
+    /**
+     * Constructs a method handle representing a loop with several loop variables that are updated and checked upon each
+     * 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
+     * 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.
+     * </ul>
+     * <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}.
+     * <p>
+     * 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.
+     * <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 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.
+     * <li>Clauses with all {@code null}s are disregarded.
+     * <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).
+     * <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".
+     * </ol>
+     * <p>
+     * <em>Step 1B: Determine loop parameters.</em><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>
+     * <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.
+     * </ol>
+     * <p>
+     * <em>Step 1D: Check other types.</em><ol type="a">
+     * <li>There must be at least one non-omitted pred function.
+     * <li>Every non-omitted pred function must have a {@code boolean} return type.
+     * </ol>
+     * <p>
+     * (Implementation Note: Steps 1A, 1B, 1C, 1D are logically independent of each other, and may be performed in any
+     * order.)
+     * <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.
+     * </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 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
+     * 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}.
+     * </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.
+     * </ol>
+     * <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
+     * 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>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>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.
+     * </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.
+     * <blockquote><pre>{@code
+     * V... init...(A...);
+     * boolean pred...(V..., A...);
+     * V... step...(V..., A...);
+     * R fini...(V..., A...);
+     * R loop(A... a) {
+     *   V... v... = init...(a...);
+     *   for (;;) {
+     *     for ((v, p, s, f) in (v..., pred..., step..., fini...)) {
+     *       v = s(v..., a...);
+     *       if (!p(v..., a...)) {
+     *         return f(v..., a...);
+     *       }
+     *     }
+     *   }
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+     * // iterative implementation of the factorial function as a loop handle
+     * static int one(int k) { return 1; }
+     * int inc(int i, int acc, int k) { return i + 1; }
+     * int mult(int i, int acc, int k) { return i * acc; }
+     * boolean pred(int i, int acc, int k) { return i < k; }
+     * int fin(int i, int acc, int k) { return acc; }
+     * // assume MH_one, MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
+     * // null initializer for counter, should initialize to 0
+     * MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
+     * MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
+     * MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
+     * assertEquals(120, loop.invoke(5));
+     * }</pre></blockquote>
+     *
+     * @param clauses an array of arrays (4-tuples) of {@link MethodHandle}s adhering to the rules described above.
+     *
+     * @return a method handle embodying the looping behavior as defined by the arguments.
+     *
+     * @throws IllegalArgumentException in case any of the constraints described above is violated.
+     *
+     * @see MethodHandles#whileLoop(MethodHandle, MethodHandle, MethodHandle)
+     * @see MethodHandles#doWhileLoop(MethodHandle, MethodHandle, MethodHandle)
+     * @see MethodHandles#countedLoop(MethodHandle, MethodHandle, MethodHandle)
+     * @see MethodHandles#iteratedLoop(MethodHandle, MethodHandle, MethodHandle)
+     * @since 9
+     */
+    public static MethodHandle loop(MethodHandle[]... clauses) {
+        // Step 0: determine clause structure.
+        checkLoop0(clauses);
+
+        List<MethodHandle> init = new ArrayList<>();
+        List<MethodHandle> step = new ArrayList<>();
+        List<MethodHandle> pred = new ArrayList<>();
+        List<MethodHandle> fini = new ArrayList<>();
+
+        Stream.of(clauses).filter(c -> Stream.of(c).anyMatch(Objects::nonNull)).forEach(clause -> {
+            init.add(clause[0]); // all clauses have at least length 1
+            step.add(clause.length <= 1 ? null : clause[1]);
+            pred.add(clause.length <= 2 ? null : clause[2]);
+            fini.add(clause.length <= 3 ? null : clause[3]);
+        });
+
+        assert Stream.of(init, step, pred, fini).map(List::size).distinct().count() == 1;
+        final int nclauses = init.size();
+
+        // Step 1A: determine iteration variables.
+        final List<Class<?>> iterationVariableTypes = new ArrayList<>();
+        for (int i = 0; i < nclauses; ++i) {
+            MethodHandle in = init.get(i);
+            MethodHandle st = step.get(i);
+            if (in == null && st == null) {
+                iterationVariableTypes.add(void.class);
+            } else if (in != null && st != null) {
+                checkLoop1a(i, in, st);
+                iterationVariableTypes.add(in.type().returnType());
+            } else {
+                iterationVariableTypes.add(in == null ? st.type().returnType() : in.type().returnType());
+            }
+        }
+        final List<Class<?>> commonPrefix = iterationVariableTypes.stream().filter(t -> t != void.class).
+                collect(Collectors.toList());
+
+        // Step 1B: determine loop parameters.
+        final List<Class<?>> empty = new ArrayList<>();
+        final List<Class<?>> commonSuffix = init.stream().filter(Objects::nonNull).map(MethodHandle::type).
+                map(MethodType::parameterList).reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty);
+        checkLoop1b(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);
+
+        // Step 2: determine parameter lists.
+        final List<Class<?>> commonParameterSequence = new ArrayList<>(commonPrefix);
+        commonParameterSequence.addAll(commonSuffix);
+        checkLoop2(step, pred, fini, commonParameterSequence);
+
+        // Step 3: fill in omitted functions.
+        for (int i = 0; i < nclauses; ++i) {
+            Class<?> t = iterationVariableTypes.get(i);
+            if (init.get(i) == null) {
+                init.set(i, zeroHandle(t));
+            }
+            if (step.get(i) == null) {
+                step.set(i, dropArguments(t == void.class ? zeroHandle(t) : identity(t), 0, commonPrefix.subList(0, i)));
+            }
+            if (pred.get(i) == null) {
+                pred.set(i, constant(boolean.class, true));
+            }
+            if (fini.get(i) == null) {
+                fini.set(i, zeroHandle(t));
+            }
+        }
+
+        // Step 4: fill in missing parameter types.
+        List<MethodHandle> finit = fillParameterTypes(init, commonSuffix);
+        List<MethodHandle> fstep = fillParameterTypes(step, commonParameterSequence);
+        List<MethodHandle> fpred = fillParameterTypes(pred, commonParameterSequence);
+        List<MethodHandle> ffini = fillParameterTypes(fini, commonParameterSequence);
+
+        assert finit.stream().map(MethodHandle::type).map(MethodType::parameterList).
+                allMatch(pl -> pl.equals(commonSuffix));
+        assert Stream.of(fstep, fpred, ffini).flatMap(List::stream).map(MethodHandle::type).map(MethodType::parameterList).
+                allMatch(pl -> pl.equals(commonParameterSequence));
+
+        return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, commonPrefix, finit, fstep, fpred, ffini);
+    }
+
+    private static List<MethodHandle> fillParameterTypes(List<MethodHandle> hs, final List<Class<?>> targetParams) {
+        return hs.stream().map(h -> {
+            int pc = h.type().parameterCount();
+            int tpsize = targetParams.size();
+            return pc < tpsize ? dropArguments(h, pc, targetParams.subList(pc, tpsize)) : h;
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * 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}.
+     * <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);
+     *   }
+     *   return v;
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+     * // implement the zip function for lists as a loop handle
+     * List<String> initZip(Iterator<String> a, Iterator<String> b) { return new ArrayList<>(); }
+     * boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { return a.hasNext() && b.hasNext(); }
+     * List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) {
+     *   zip.add(a.next());
+     *   zip.add(b.next());
+     *   return zip;
+     * }
+     * // assume MH_initZip, MH_zipPred, and MH_zipStep are handles to the above methods
+     * MethodHandle loop = MethodHandles.doWhileLoop(MH_initZip, MH_zipPred, MH_zipStep);
+     * List<String> a = Arrays.asList("a", "b", "c", "d");
+     * List<String> b = Arrays.asList("e", "f", "g", "h");
+     * List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
+     * assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator()));
+     * }</pre></blockquote>
+     *
+     * <p>
+     * @implSpec The implementation of this method is equivalent to:
+     * <blockquote><pre>{@code
+     * MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
+     *     MethodHandle[]
+     *         checkExit = {null, null, pred, identity(init.type().returnType())},
+     *         varBody = {init, body};
+     *     return loop(checkExit, varBody);
+     * }
+     * }</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}.
+     *
+     * @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[][])
+     * @since 9
+     */
+    public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
+        MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType());
+        MethodHandle[] checkExit = {null, null, pred, fin};
+        MethodHandle[] varBody = {init, body};
+        return loop(checkExit, varBody);
+    }
+
+    /**
+     * Constructs a {@code do-while} loop from an initializer, a body, and a predicate. This is a convenience wrapper
+     * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}.
+     * <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}.
+     * <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);
+     *   do {
+     *     v = body(v, a);
+     *   } while (pred(v, a));
+     *   return v;
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+     * // int i = 0; while (i < limit) { ++i; } return i; => limit
+     * int zero(int limit) { return 0; }
+     * int step(int i, int limit) { return i + 1; }
+     * boolean pred(int i, int limit) { return i < limit; }
+     * // assume MH_zero, MH_step, and MH_pred are handles to the above methods
+     * MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
+     * assertEquals(23, loop.invoke(23));
+     * }</pre></blockquote>
+     *
+     * <p>
+     * @implSpec The implementation of this method is equivalent to:
+     * <blockquote><pre>{@code
+     * MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
+     *     MethodHandle[] clause = { init, body, pred, identity(init.type().returnType()) };
+     *     return loop(clause);
+     * }
+     * }</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}.
+     *
+     * @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[][])
+     * @since 9
+     */
+    public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
+        MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType());
+        MethodHandle[] clause = {init, body, pred, fin};
+        return loop(clause);
+    }
+
+    /**
+     * Constructs a loop that runs a given number of iterations. The loop counter is an {@code int} initialized from the
+     * {@code iterations} handle evaluation result. The counter is passed to the {@code body} function, so that must
+     * accept an initial {@code int} argument. The result of the loop execution is the final value of the additional
+     * local state. This is a convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop
+     * combinator}.
+     * <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}.
+     * <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 iterations(A);
+     * V init(A);
+     * V body(int, V, A);
+     * V countedLoop(A a) {
+     *   int end = iterations(a);
+     *   V v = init(a);
+     *   for (int i = 0; i < end; ++i) {
+     *     v = body(i, v, a);
+     *   }
+     *   return v;
+     * }
+     * }</pre></blockquote>
+     * <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
+     * String start(String arg) { return arg; }
+     * String step(int counter, String v, String arg) { return "na " + v; }
+     * // assume MH_start and MH_step are handles to the two methods above
+     * MethodHandle loop = MethodHandles.countedLoop(13, MH_start, MH_step);
+     * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
+     * }</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
+     *     MethodHandle[]
+     *         indexVar = {start, MH_increment}, // i = start; i = i+1
+     *         loopLimit = {end, null, MH_lessThan, returnVar }, // i<end
+     *         bodyClause = {init, dropArguments(body, 1, int.class)};  // v = body(i, 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) {
+        MethodHandle returnVar = dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()),
+                0, int.class, int.class);
+        MethodHandle[] indexVar = {start, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep)};
+        MethodHandle[] loopLimit = {end, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred), returnVar};
+        MethodHandle[] bodyClause = {init, dropArguments(body, 1, int.class)};
+        return loop(indexVar, loopLimit, bodyClause);
+    }
+
+    /**
+     * Constructs a loop that ranges over the elements produced by an {@code Iterator<T>}.
+     * The iterator will be produced by the evaluation of the {@code iterator} handle.
+     * 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
+     * List<String> reverseStep(String e, List<String> r) {
+     *   r.add(0, e);
+     *   return r;
+     * }
+     * List<String> newArrayList() { return new ArrayList<>(); }
+     * // assume MH_reverseStep, 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:
+     * <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);
+     *     MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
+     *     MethodHandle[]
+     *         iterVar = {iterator, null, MH_hasNext, returnVar}, // it = iterator(); while (it.hasNext)
+     *         bodyClause = {init, filterArgument(body, 0, nextVal)};  // v = body(t, v, a);
+     *     return loop(iterVar, bodyClause);
+     * }
+     * }</pre></blockquote>
+     *
+     * @param iterator a handle to return the iterator to start the loop.
+     *             Passing {@code null} will make the loop call {@link Iterable#iterator()} on the first
+     *             incoming value.
+     * @param init initializer for additional loop state. This determines the loop's result type.
+     *             Passing {@code null} or a {@code void} init function will make the loop's result type
+     *             {@code void}.
+     * @param body the body of the loop, which must not be {@code null}.
+     *             It must accept an initial {@code T} parameter (for the iterated values), and then any
+     *             additional loop-local variable plus loop parameters.
+     *
+     * @return a method handle embodying the iteration loop functionality.
+     * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+     *
+     * @since 9
+     */
+    public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
+        checkIteratedLoop(body);
+
+        MethodHandle initit = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator);
+        MethodHandle initIterator = iterator == null ?
+                initit.asType(initit.type().changeParameterType(0, body.type().parameterType(init == null ? 1 : 2))) :
+                iterator;
+        Class<?> itype = initIterator.type().returnType();
+        Class<?> ttype = body.type().parameterType(0);
+
+        MethodHandle returnVar =
+                dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), 0, itype);
+        MethodHandle 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)};
+
+        return loop(iterVar, bodyClause);
+    }
+
+    /**
+     * Makes a method handle that adapts a {@code target} method handle by wrapping it in a {@code try-finally} block.
+     * Another method handle, {@code cleanup}, represents the functionality of the {@code finally} block. Any exception
+     * thrown during the execution of the {@code target} handle will be passed to the {@code cleanup} handle. The
+     * exception will be rethrown, unless {@code cleanup} handle throws an exception first.  The
+     * value returned from the {@code cleanup} handle's execution will be the result of the execution of the
+     * {@code try-finally} handle.
+     * <p>
+     * The {@code cleanup} handle will be passed one or two additional leading arguments.
+     * The first is the exception thrown during the
+     * execution of the {@code target} handle, or {@code null} if no exception was thrown.
+     * The second is the result of the execution of the {@code target} handle, or, if it throws an exception,
+     * a {@code null}, zero, or {@code false} value of the required type is supplied as a placeholder.
+     * The second argument is not present if the {@code target} handle has a {@code void} return type.
+     * (Note that, except for argument type conversions, combinators represent {@code void} values in parameter lists
+     * by omitting the corresponding paradoxical arguments, not by inserting {@code null} or zero values.)
+     * <p>
+     * The {@code target} and {@code cleanup} handles' return types must be the same. Their parameter type lists also
+     * must be the same, but the {@code cleanup} handle must accept one or two more leading parameters:<ul>
+     * <li>a {@code Throwable}, which will carry the exception thrown by the {@code target} handle (if any); and
+     * <li>a parameter of the same type as the return type of both {@code target} and {@code cleanup}, which will carry
+     * the result from the execution of the {@code target} handle.
+     * This parameter is not present if the {@code target} returns {@code void}.
+     * </ul>
+     * <p>
+     * The pseudocode for the resulting adapter looks as follows. In the code, {@code V} represents the result type of
+     * the {@code try/finally} construct; {@code A}/{@code a}, the types and values of arguments to the resulting
+     * handle consumed by the cleanup; and {@code B}/{@code b}, those of arguments to the resulting handle discarded by
+     * the cleanup.
+     * <blockquote><pre>{@code
+     * V target(A..., B...);
+     * V cleanup(Throwable, V, A...);
+     * V adapter(A... a, B... b) {
+     *   V result = (zero value for V);
+     *   Throwable throwable = null;
+     *   try {
+     *     result = target(a..., b...);
+     *   } catch (Throwable t) {
+     *     throwable = t;
+     *     throw t;
+     *   } finally {
+     *     result = cleanup(throwable, result, a...);
+     *   }
+     *   return result;
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * Note that the saved arguments ({@code a...} in the pseudocode) cannot
+     * be modified by execution of the target, and so are passed unchanged
+     * from the caller to the cleanup, if it is invoked.
+     * <p>
+     * The target and cleanup must return the same type, even if the cleanup
+     * always throws.
+     * To create such a throwing cleanup, compose the cleanup logic
+     * with {@link #throwException throwException},
+     * in order to create a method handle of the correct return type.
+     * <p>
+     * Note that {@code tryFinally} never converts exceptions into normal returns.
+     * In rare cases where exceptions must be converted in that way, first wrap
+     * the target with {@link #catchException(MethodHandle, Class, MethodHandle)}
+     * to capture an outgoing exception, and then wrap with {@code tryFinally}.
+     *
+     * @param target the handle whose execution is to be wrapped in a {@code try} block.
+     * @param cleanup the handle that is invoked in the finally block.
+     *
+     * @return a method handle embodying the {@code try-finally} block composed of the two arguments.
+     * @throws NullPointerException if any argument is null
+     * @throws IllegalArgumentException if {@code cleanup} does not accept
+     *          the required leading arguments, or if the method handle types do
+     *          not match in their return types and their
+     *          corresponding trailing parameters
+     *
+     * @see MethodHandles#catchException(MethodHandle, Class, MethodHandle)
+     * @since 9
+     */
+    public static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup) {
+        List<Class<?>> targetParamTypes = target.type().parameterList();
+        List<Class<?>> cleanupParamTypes = cleanup.type().parameterList();
+        Class<?> rtype = target.type().returnType();
+
+        checkTryFinally(target, cleanup);
+
+        // Match parameter lists: if the cleanup has a shorter parameter list than the target, add ignored arguments.
+        int tpSize = targetParamTypes.size();
+        int cpPrefixLength = rtype == void.class ? 1 : 2;
+        int cpSize = cleanupParamTypes.size();
+        MethodHandle aCleanup = cpSize - cpPrefixLength < tpSize ?
+                dropArguments(cleanup, cpSize, targetParamTypes.subList(tpSize - (cpSize - cpPrefixLength), tpSize)) :
+                cleanup;
+
+        MethodHandle aTarget = target.asSpreader(Object[].class, target.type().parameterCount());
+        aCleanup = aCleanup.asSpreader(Object[].class, tpSize);
+
+        return MethodHandleImpl.makeTryFinally(aTarget, aCleanup, 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:
+     * <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>
+     *
+     * @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;
+    }
+
+    /**
+     * Wrap creation of a proper zero handle for a given type.
+     *
+     * @param type the type.
+     *
+     * @return a zero value for the given type.
+     */
+    static MethodHandle zeroHandle(Class<?> type) {
+        return type.isPrimitive() ?  zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type);
+    }
+
+    private static void checkLoop0(MethodHandle[][] clauses) {
+        if (clauses == null || clauses.length == 0) {
+            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 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) {
+        if (Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).map(MethodHandle::type).
+                map(MethodType::parameterList).anyMatch(pl -> !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 body) {
+        if (null == body) {
+            throw newIllegalArgumentException("iterated loop body must not be null");
+        }
+    }
+
+    private static void checkTryFinally(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))) {
+            throw misMatchedTypes("cleanup first argument and Throwable", cleanup.type(), Throwable.class);
+        }
+        if (rtype != void.class && cleanupParamTypes.get(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;
+        if (!cleanupParamTypes.subList(cleanupArgIndex, cleanupParamTypes.size()).
+                equals(target.type().parameterList().subList(0, cleanupParamTypes.size() - cleanupArgIndex))) {
+            throw misMatchedTypes("cleanup parameters after (Throwable,result) and target parameter list prefix",
+                    cleanup.type(), target.type());
+        }
+    }
+
 }
--- a/src/java.base/share/classes/java/lang/invoke/MethodType.java	Thu Nov 19 19:46:46 2015 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodType.java	Fri Nov 20 15:34:12 2015 +0100
@@ -469,12 +469,13 @@
 
     /** Replace the last arrayLength parameter types with the component type of arrayType.
      * @param arrayType any array type
+     * @param pos position at which to spread
      * @param arrayLength the number of parameter types to change
      * @return the resulting type
      */
-    /*non-public*/ MethodType asSpreaderType(Class<?> arrayType, int arrayLength) {
+    /*non-public*/ MethodType asSpreaderType(Class<?> arrayType, int pos, int arrayLength) {
         assert(parameterCount() >= arrayLength);
-        int spreadPos = ptypes.length - arrayLength;
+        int spreadPos = pos;
         if (arrayLength == 0)  return this;  // nothing to change
         if (arrayType == Object[].class) {
             if (isGeneric())  return this;  // nothing to change
@@ -489,10 +490,10 @@
         }
         Class<?> elemType = arrayType.getComponentType();
         assert(elemType != null);
-        for (int i = spreadPos; i < ptypes.length; i++) {
+        for (int i = spreadPos; i < spreadPos + arrayLength; i++) {
             if (ptypes[i] != elemType) {
                 Class<?>[] fixedPtypes = ptypes.clone();
-                Arrays.fill(fixedPtypes, i, ptypes.length, elemType);
+                Arrays.fill(fixedPtypes, i, spreadPos + arrayLength, elemType);
                 return methodType(rtype, fixedPtypes);
             }
         }
@@ -512,12 +513,14 @@
 
     /** Delete the last parameter type and replace it with arrayLength copies of the component type of arrayType.
      * @param arrayType any array type
+     * @param pos position at which to insert parameters
      * @param arrayLength the number of parameter types to insert
      * @return the resulting type
      */
-    /*non-public*/ MethodType asCollectorType(Class<?> arrayType, int arrayLength) {
+    /*non-public*/ MethodType asCollectorType(Class<?> arrayType, int pos, int arrayLength) {
         assert(parameterCount() >= 1);
-        assert(lastParameterType().isAssignableFrom(arrayType));
+        assert(pos < ptypes.length);
+        assert(ptypes[pos].isAssignableFrom(arrayType));
         MethodType res;
         if (arrayType == Object[].class) {
             res = genericMethodType(arrayLength);
@@ -532,7 +535,11 @@
         if (ptypes.length == 1) {
             return res;
         } else {
-            return res.insertParameterTypes(0, parameterList().subList(0, ptypes.length-1));
+            // insert after (if need be), then before
+            if (pos < parameterList().size() - 1) {
+                res = res.insertParameterTypes(arrayLength, parameterList().subList(pos + 1, parameterList().size()));
+            }
+            return res.insertParameterTypes(0, parameterList().subList(0, pos));
         }
     }
 
--- a/test/java/lang/invoke/AccessControlTest.java	Thu Nov 19 19:46:46 2015 -0800
+++ b/test/java/lang/invoke/AccessControlTest.java	Fri Nov 20 15:34:12 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -225,6 +225,31 @@
                 System.out.println(this+" willAccess "+lc+" m1="+m1+" m2="+m2+" => "+((m2 & m1) != 0));
             return (m2 & m1) != 0;
         }
+
+        /** Predict the success or failure of accessing this class. */
+        public boolean willAccessClass(Class<?> c2, boolean load) {
+            Class<?> c1 = lookupClass();
+            if (load && c1.getClassLoader() == null) {
+                return false;
+            }
+            LookupCase lc = this.in(c2);
+            int m1 = lc.lookupModes();
+            boolean r = false;
+            if (m1 == 0) {
+                r = false;
+            } else {
+                int m2 = fixMods(c2.getModifiers());
+                if ((m2 & PUBLIC) != 0) {
+                    r = true;
+                } else if ((m1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage()) {
+                    r = true;
+                }
+            }
+            if (verbosity >= 2) {
+                System.out.println(this+" willAccessClass "+lc+" c1="+c1+" c2="+c2+" => "+r);
+            }
+            return r;
+        }
     }
 
     private static Class<?> topLevelClass(Class<?> cls) {
@@ -342,6 +367,8 @@
                 Method method = targetMethod(targetClass, targetAccess, methodType);
                 // Try to access target method from various contexts.
                 for (LookupCase sourceCase : CASES) {
+                    testOneAccess(sourceCase, method, "findClass");
+                    testOneAccess(sourceCase, method, "accessClass");
                     testOneAccess(sourceCase, method, "find");
                     testOneAccess(sourceCase, method, "unreflect");
                 }
@@ -356,11 +383,19 @@
         Class<?> targetClass = method.getDeclaringClass();
         String methodName = method.getName();
         MethodType methodType = methodType(method.getReturnType(), method.getParameterTypes());
-        boolean willAccess = sourceCase.willAccess(method);
+        boolean isFindOrAccessClass = "findClass".equals(kind) || "accessClass".equals(kind);
+        boolean willAccess = isFindOrAccessClass ?
+                sourceCase.willAccessClass(targetClass, "findClass".equals(kind)) : sourceCase.willAccess(method);
         boolean didAccess = false;
         ReflectiveOperationException accessError = null;
         try {
             switch (kind) {
+            case "accessClass":
+                sourceCase.lookup().accessClass(targetClass);
+                break;
+            case "findClass":
+                sourceCase.lookup().findClass(targetClass.getName());
+                break;
             case "find":
                 if ((method.getModifiers() & Modifier.STATIC) != 0)
                     sourceCase.lookup().findStatic(targetClass, methodName, methodType);
@@ -378,8 +413,8 @@
             accessError = ex;
         }
         if (willAccess != didAccess) {
-            System.out.println(sourceCase+" => "+targetClass.getSimpleName()+"."+methodName+methodType);
-            System.out.println("fail on "+method+" ex="+accessError);
+            System.out.println(sourceCase+" => "+targetClass.getSimpleName()+(isFindOrAccessClass?"":"."+methodName+methodType));
+            System.out.println("fail "+(isFindOrAccessClass?kind:"on "+method)+" ex="+accessError);
             assertEquals(willAccess, didAccess);
         }
         testCount++;
--- a/test/java/lang/invoke/BigArityTest.java	Thu Nov 19 19:46:46 2015 -0800
+++ b/test/java/lang/invoke/BigArityTest.java	Fri Nov 20 15:34:12 2015 +0100
@@ -58,6 +58,8 @@
         return x == null ? dflt : x;
     }
 
+    static final MethodType MT_A = MethodType.methodType(Object.class, Object.class, Object[].class, Object.class);
+
     static Object hashArguments(Object... args) {
         return Objects.hash(args);
     }
@@ -108,9 +110,36 @@
             }
         }
         // Sizes not in the above array are good:
-        target.asCollector(Object[].class, minbig-1);
+        target.asCollector(Object[].class, minbig - 1);
         for (int i = 2; i <= 10; i++)
-            target.asCollector(Object[].class, minbig-i);
+            target.asCollector(Object[].class, minbig - i);
+    }
+
+    static void asciae02target(Object[] a, Object b) {
+        // naught
+    }
+
+    @Test
+    public void asCollectorIAE02() throws ReflectiveOperationException {
+        final int[] INVALID_ARRAY_LENGTHS = {
+            Integer.MIN_VALUE, Integer.MIN_VALUE + 1, -2, -1, 254, 255, Integer.MAX_VALUE - 1, Integer.MAX_VALUE
+        };
+        MethodHandle target = MethodHandles.lookup().findStatic(BigArityTest.class, "asciae02target",
+                MethodType.methodType(void.class, Object[].class, Object.class));
+        int minbig = Integer.MAX_VALUE;
+        for (int invalidLength : INVALID_ARRAY_LENGTHS) {
+            if (minbig > invalidLength && invalidLength > 100) minbig = invalidLength;
+            try {
+                target.asCollector(0, Object[].class, invalidLength);
+                assert(false) : invalidLength;
+            } catch (IllegalArgumentException ex) {
+                System.out.println("OK: "+ex);
+            }
+        }
+        // Sizes not in the above array are good:
+        for (int i = 1; i <= 10; ++i) {
+            target.asCollector(0, Object[].class, minbig - i);
+        }
     }
 
     @Test
@@ -216,51 +245,86 @@
             Class<? extends Object[]> cls = (Class<? extends Object[]>) cls0;
             //Class<? extends Object[]> cls = Object[].class.asSubclass(cls0);
             int nargs = args.length, skip;
+            Object hr;
             MethodHandle smh = mh.asSpreader(cls, nargs - (skip = 0));
+            MethodHandle hsmh = mh.asSpreader(0, cls, nargs - skip);
             Object[] tail = Arrays.copyOfRange(args, skip, nargs, cls);
-            if (cls == Object[].class)
+            Object[] head = Arrays.copyOfRange(args, 0, nargs - skip, cls);
+            if (cls == Object[].class) {
                 r = smh.invokeExact(tail);
-            else if (cls == Integer[].class)
+                hr = hsmh.invokeExact(head);
+            } else if (cls == Integer[].class) {
                 r = smh.invokeExact((Integer[]) tail); //warning OK, see 8019340
-            else
+                hr = hsmh.invokeExact((Integer[]) head);
+            } else {
                 r = smh.invoke(tail);
+                hr = hsmh.invoke(head);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
             smh = mh.asSpreader(cls, nargs - (skip = 1));
+            hsmh = mh.asSpreader(0, cls, nargs - skip);
             tail = Arrays.copyOfRange(args, skip, nargs, cls);
-            if (cls == Object[].class)
+            head = Arrays.copyOfRange(args, 0, nargs - skip, cls);
+            if (cls == Object[].class) {
                 r = smh.invokeExact(args[0], tail);
-            else if (cls == Integer[].class)
+                hr = hsmh.invokeExact(head, args[2]);
+            } else if (cls == Integer[].class) {
                 r = smh.invokeExact(args[0], (Integer[]) tail);
-            else
+                hr = hsmh.invokeExact((Integer[]) head, args[2]);
+            } else {
                 r = smh.invoke(args[0], tail);
+                hr = hsmh.invoke(head, args[2]);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
             smh = mh.asSpreader(cls, nargs - (skip = 2));
+            hsmh = mh.asSpreader(0, cls, nargs - skip);
             tail = Arrays.copyOfRange(args, skip, nargs, cls);
-            if (cls == Object[].class)
+            head = Arrays.copyOfRange(args, 0, nargs - skip, cls);
+            if (cls == Object[].class) {
                 r = smh.invokeExact(args[0], args[1], tail);
-            else if (cls == Integer[].class)
+                hr = hsmh.invokeExact(head, args[1], args[2]);
+            } else if (cls == Integer[].class) {
                 r = smh.invokeExact(args[0], args[1], (Integer[]) tail);
-            else
+                hr = hsmh.invokeExact((Integer[]) head, args[1], args[2]);
+            } else {
                 r = smh.invoke(args[0], args[1], tail);
+                hr = hsmh.invoke(head, args[1], args[2]);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
             smh = mh.asSpreader(cls, nargs - (skip = 3));
+            hsmh = mh.asSpreader(0, cls, nargs - skip);
             tail = Arrays.copyOfRange(args, skip, nargs, cls);
-            if (cls == Object[].class)
+            head = Arrays.copyOfRange(args, 0, nargs - skip, cls);
+            if (cls == Object[].class) {
                 r = smh.invokeExact(args[0], args[1], args[2], tail);
-            else if (cls == Integer[].class)
+                hr = hsmh.invokeExact(head, args[0], args[1], args[2]);
+            } else if (cls == Integer[].class) {
                 r = smh.invokeExact(args[0], args[1], args[2], (Integer[]) tail);
-            else
+                hr = hsmh.invokeExact((Integer[]) head, args[0], args[1], args[2]);
+            } else {
                 r = smh.invoke(args[0], args[1], args[2], tail);
+                hr = hsmh.invoke(head, args[0], args[1], args[2]);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
             // Try null array in addition to zero-length array:
             tail = null;
-            if (cls == Object[].class)
+            head = null;
+            if (cls == Object[].class) {
                 r = smh.invokeExact(args[0], args[1], args[2], tail);
-            else if (cls == Integer[].class)
+                hr = hsmh.invokeExact(head, args[0], args[1], args[2]);
+            } else if (cls == Integer[].class) {
                 r = smh.invokeExact(args[0], args[1], args[2], (Integer[]) tail);
-            else
+                hr = hsmh.invokeExact((Integer[]) head, args[0], args[1], args[2]);
+            } else {
                 r = smh.invoke(args[0], args[1], args[2], tail);
+                hr = hsmh.invoke(head, args[0], args[1], args[2]);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
         }
     }
 
@@ -292,7 +356,7 @@
     @Test
     public void testArities() throws Throwable {
         System.out.println("testing spreaders and collectors on high arities...");
-            int iterations = ITERATION_COUNT;
+        int iterations = ITERATION_COUNT;
         testArities(Object[].class, MIN_ARITY-10, MIN_ARITY-1, iterations / 1000);
         testArities(Object[].class, MIN_ARITY, SLOW_ARITY-1, iterations);
         testArities(Object[].class, SLOW_ARITY, MAX_ARITY, iterations / 1000);
@@ -307,8 +371,13 @@
             Class<? extends Object[]> cls = (Class<? extends Object[]>) cls0;
             System.out.println("array class: "+cls.getSimpleName());
             int iterations = ITERATION_COUNT / 1000;
-            testArities(cls, MIN_ARITY, SLOW_ARITY-1, iterations);
-            testArities(cls, SLOW_ARITY, MAX_ARITY, iterations / 100);
+            try {
+                testArities(cls, MIN_ARITY, SLOW_ARITY - 1, iterations);
+                testArities(cls, SLOW_ARITY, MAX_ARITY, iterations / 100);
+            } catch (Throwable t) {
+                t.printStackTrace();
+                throw t;
+            }
         }
     }
 
@@ -321,11 +390,14 @@
             if (verbose)  System.out.println("arity="+arity);
             MethodHandle mh = MH_hashArguments(cls, arity);
             MethodHandle mh_VA = mh.asSpreader(cls, arity);
+            MethodHandle mh_VA_h = mh.asSpreader(0, cls, arity-1);
             assert(mh_VA.type().parameterType(0) == cls);
-            testArities(cls, arity, iterations, verbose, mh, mh_VA);
+            assert(mh_VA_h.type().parameterType(0) == cls);
+            testArities(cls, arity, iterations, verbose, mh, mh_VA, mh_VA_h);
             // mh_CA will collect arguments of a particular type and pass them to mh_VA
             MethodHandle mh_CA = mh_VA.asCollector(cls, arity);
             MethodHandle mh_VA2 = mh_CA.asSpreader(cls, arity);
+            MethodHandle mh_VA2_h = mh_CA.asSpreader(0, cls, arity-1);
             assert(mh_CA.type().equals(mh.type()));
             assert(mh_VA2.type().equals(mh_VA.type()));
             if (cls != Object[].class) {
@@ -336,7 +408,7 @@
                 }
             }
             int iterations_VA = iterations / 100;
-            testArities(cls, arity, iterations_VA, false, mh_CA, mh_VA2);
+            testArities(cls, arity, iterations_VA, false, mh_CA, mh_VA2, mh_VA2_h);
         }
     }
 
@@ -357,13 +429,16 @@
      * @param verbose are we printing extra output?
      * @param mh      a fixed-arity version of {@code hashArguments}
      * @param mh_VA   a variable-arity version of {@code hashArguments}, accepting the given array type {@code cls}
+     * @param mh_VA_h a version of {@code hashArguments} that has a leading {@code cls} array and one final {@code cls}
+    *                 argument
      */
     private void testArities(Class<? extends Object[]> cls,
                              int arity,
                              int iterations,
                              boolean verbose,
                              MethodHandle mh,
-                             MethodHandle mh_VA
+                             MethodHandle mh_VA,
+                             MethodHandle mh_VA_h
                              ) throws Throwable {
         if (iterations < 4)  iterations = 4;
         final int MAX_MH_ARITY      = MAX_JVM_ARITY - 1;  // mh.invoke(arg*[N])
@@ -373,6 +448,7 @@
             args = Arrays.copyOf(args, arity, cls);
         Object r0 = Objects.hash(args);
         Object r;
+        Object hr;
         MethodHandle ximh = null;
         MethodHandle gimh = null;
         if (arity <= MAX_INVOKER_ARITY) {
@@ -397,13 +473,18 @@
         Object[] mh_args = cat(mh, args);
         assert(arity <= MAX_MH_ARITY);
         for (int i = 0; i < iterations; ++i) {
-            if (cls == Object[].class)
+            if (cls == Object[].class) {
                 r = mh_VA.invokeExact(args);
-            else if (cls == Integer[].class)
-                r = mh_VA.invokeExact((Integer[])args); //warning OK, see 8019340
-            else
+                hr = mh_VA_h.invokeExact(Arrays.copyOfRange(args, 0, arity - 1), args[arity - 1]);
+            } else if (cls == Integer[].class) {
+                r = mh_VA.invokeExact((Integer[]) args); //warning OK, see 8019340
+                hr = mh_VA_h.invokeExact((Integer[]) Arrays.copyOfRange(args, 0, arity - 1), (Integer) args[arity - 1]);
+            } else {
                 r = mh_VA.invoke(args);
+                hr = mh_VA_h.invoke(Arrays.copyOfRange(args, 0, arity - 1), args[arity - 1]);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
             r = mh.invokeWithArguments(args);
             assertEquals(r0, r);
             if (ximh != null) {
@@ -473,6 +554,43 @@
     // </editor-fold>
     xF8, xF9, xFA, xFB);
     }
+    static Object hashArguments_252_a(Object x00, Object[] x01_FA, Object xFB) {
+        return Objects.hash(
+                // <editor-fold defaultstate="collapsed" desc="x00, x01_FA[0], x01_FA[1], x01_FA[2], ...">
+                x00, x01_FA[0], x01_FA[1], x01_FA[2], x01_FA[3], x01_FA[4], x01_FA[5], x01_FA[6], x01_FA[7], x01_FA[8],
+                x01_FA[9], x01_FA[10], x01_FA[11], x01_FA[12], x01_FA[13], x01_FA[14], x01_FA[15], x01_FA[16],
+                x01_FA[17], x01_FA[18], x01_FA[19], x01_FA[20], x01_FA[21], x01_FA[22], x01_FA[23], x01_FA[24],
+                x01_FA[25], x01_FA[26], x01_FA[27], x01_FA[28], x01_FA[29], x01_FA[30], x01_FA[31], x01_FA[32],
+                x01_FA[33], x01_FA[34], x01_FA[35], x01_FA[36], x01_FA[37], x01_FA[38], x01_FA[39], x01_FA[40],
+                x01_FA[41], x01_FA[42], x01_FA[43], x01_FA[44], x01_FA[45], x01_FA[46], x01_FA[47], x01_FA[48],
+                x01_FA[49], x01_FA[50], x01_FA[51], x01_FA[52], x01_FA[53], x01_FA[54], x01_FA[55], x01_FA[56],
+                x01_FA[57], x01_FA[58], x01_FA[59], x01_FA[60], x01_FA[61], x01_FA[62], x01_FA[63], x01_FA[64],
+                x01_FA[65], x01_FA[66], x01_FA[67], x01_FA[68], x01_FA[69], x01_FA[70], x01_FA[71], x01_FA[72],
+                x01_FA[73], x01_FA[74], x01_FA[75], x01_FA[76], x01_FA[77], x01_FA[78], x01_FA[79], x01_FA[80],
+                x01_FA[81], x01_FA[82], x01_FA[83], x01_FA[84], x01_FA[85], x01_FA[86], x01_FA[87], x01_FA[88],
+                x01_FA[89], x01_FA[90], x01_FA[91], x01_FA[92], x01_FA[93], x01_FA[94], x01_FA[95], x01_FA[96],
+                x01_FA[97], x01_FA[98], x01_FA[99], x01_FA[100], x01_FA[101], x01_FA[102], x01_FA[103], x01_FA[104],
+                x01_FA[105], x01_FA[106], x01_FA[107], x01_FA[108], x01_FA[109], x01_FA[110], x01_FA[111], x01_FA[112],
+                x01_FA[113], x01_FA[114], x01_FA[115], x01_FA[116], x01_FA[117], x01_FA[118], x01_FA[119], x01_FA[120],
+                x01_FA[121], x01_FA[122], x01_FA[123], x01_FA[124], x01_FA[125], x01_FA[126], x01_FA[127], x01_FA[128],
+                x01_FA[129], x01_FA[130], x01_FA[131], x01_FA[132], x01_FA[133], x01_FA[134], x01_FA[135], x01_FA[136],
+                x01_FA[137], x01_FA[138], x01_FA[139], x01_FA[140], x01_FA[141], x01_FA[142], x01_FA[143], x01_FA[144],
+                x01_FA[145], x01_FA[146], x01_FA[147], x01_FA[148], x01_FA[149], x01_FA[150], x01_FA[151], x01_FA[152],
+                x01_FA[153], x01_FA[154], x01_FA[155], x01_FA[156], x01_FA[157], x01_FA[158], x01_FA[159], x01_FA[160],
+                x01_FA[161], x01_FA[162], x01_FA[163], x01_FA[164], x01_FA[165], x01_FA[166], x01_FA[167], x01_FA[168],
+                x01_FA[169], x01_FA[170], x01_FA[171], x01_FA[172], x01_FA[173], x01_FA[174], x01_FA[175], x01_FA[176],
+                x01_FA[177], x01_FA[178], x01_FA[179], x01_FA[180], x01_FA[181], x01_FA[182], x01_FA[183], x01_FA[184],
+                x01_FA[185], x01_FA[186], x01_FA[187], x01_FA[188], x01_FA[189], x01_FA[190], x01_FA[191], x01_FA[192],
+                x01_FA[193], x01_FA[194], x01_FA[195], x01_FA[196], x01_FA[197], x01_FA[198], x01_FA[199], x01_FA[200],
+                x01_FA[201], x01_FA[202], x01_FA[203], x01_FA[204], x01_FA[205], x01_FA[206], x01_FA[207], x01_FA[208],
+                x01_FA[209], x01_FA[210], x01_FA[211], x01_FA[212], x01_FA[213], x01_FA[214], x01_FA[215], x01_FA[216],
+                x01_FA[217], x01_FA[218], x01_FA[219], x01_FA[220], x01_FA[221], x01_FA[222], x01_FA[223], x01_FA[224],
+                x01_FA[225], x01_FA[226], x01_FA[227], x01_FA[228], x01_FA[229], x01_FA[230], x01_FA[231], x01_FA[232],
+                x01_FA[233], x01_FA[234], x01_FA[235], x01_FA[236], x01_FA[237], x01_FA[238], x01_FA[239], x01_FA[240],
+                x01_FA[241], x01_FA[242], x01_FA[243], x01_FA[244], x01_FA[245], x01_FA[246], x01_FA[247], x01_FA[248],
+                // </editor-fold>
+                x01_FA[249], xFB);
+    }
 
     @Test
     public void test252() throws Throwable {
@@ -507,6 +625,8 @@
         test252(mh, a, r0);
         MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY);
         test252(mh_CA, a, r0);
+        MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2);
+        test252(mh_a, a, r0);
     }
     public void test252(MethodHandle mh, Object[] a, Object r0) throws Throwable {
         Object r;
@@ -686,6 +806,43 @@
     // </editor-fold>
     xF8, xF9, xFA, xFB, xFC);
     }
+    static Object hashArguments_253_a(Object x00, Object[] x01_FB, Object xFC) {
+        return Objects.hash(
+                // <editor-fold defaultstate="collapsed" desc="x00, x01_FB[0], x01_FB[1], x01_FB[2], ...">
+                x00, x01_FB[0], x01_FB[1], x01_FB[2], x01_FB[3], x01_FB[4], x01_FB[5], x01_FB[6], x01_FB[7], x01_FB[8],
+                x01_FB[9], x01_FB[10], x01_FB[11], x01_FB[12], x01_FB[13], x01_FB[14], x01_FB[15], x01_FB[16],
+                x01_FB[17], x01_FB[18], x01_FB[19], x01_FB[20], x01_FB[21], x01_FB[22], x01_FB[23], x01_FB[24],
+                x01_FB[25], x01_FB[26], x01_FB[27], x01_FB[28], x01_FB[29], x01_FB[30], x01_FB[31], x01_FB[32],
+                x01_FB[33], x01_FB[34], x01_FB[35], x01_FB[36], x01_FB[37], x01_FB[38], x01_FB[39], x01_FB[40],
+                x01_FB[41], x01_FB[42], x01_FB[43], x01_FB[44], x01_FB[45], x01_FB[46], x01_FB[47], x01_FB[48],
+                x01_FB[49], x01_FB[50], x01_FB[51], x01_FB[52], x01_FB[53], x01_FB[54], x01_FB[55], x01_FB[56],
+                x01_FB[57], x01_FB[58], x01_FB[59], x01_FB[60], x01_FB[61], x01_FB[62], x01_FB[63], x01_FB[64],
+                x01_FB[65], x01_FB[66], x01_FB[67], x01_FB[68], x01_FB[69], x01_FB[70], x01_FB[71], x01_FB[72],
+                x01_FB[73], x01_FB[74], x01_FB[75], x01_FB[76], x01_FB[77], x01_FB[78], x01_FB[79], x01_FB[80],
+                x01_FB[81], x01_FB[82], x01_FB[83], x01_FB[84], x01_FB[85], x01_FB[86], x01_FB[87], x01_FB[88],
+                x01_FB[89], x01_FB[90], x01_FB[91], x01_FB[92], x01_FB[93], x01_FB[94], x01_FB[95], x01_FB[96],
+                x01_FB[97], x01_FB[98], x01_FB[99], x01_FB[100], x01_FB[101], x01_FB[102], x01_FB[103], x01_FB[104],
+                x01_FB[105], x01_FB[106], x01_FB[107], x01_FB[108], x01_FB[109], x01_FB[110], x01_FB[111], x01_FB[112],
+                x01_FB[113], x01_FB[114], x01_FB[115], x01_FB[116], x01_FB[117], x01_FB[118], x01_FB[119], x01_FB[120],
+                x01_FB[121], x01_FB[122], x01_FB[123], x01_FB[124], x01_FB[125], x01_FB[126], x01_FB[127], x01_FB[128],
+                x01_FB[129], x01_FB[130], x01_FB[131], x01_FB[132], x01_FB[133], x01_FB[134], x01_FB[135], x01_FB[136],
+                x01_FB[137], x01_FB[138], x01_FB[139], x01_FB[140], x01_FB[141], x01_FB[142], x01_FB[143], x01_FB[144],
+                x01_FB[145], x01_FB[146], x01_FB[147], x01_FB[148], x01_FB[149], x01_FB[150], x01_FB[151], x01_FB[152],
+                x01_FB[153], x01_FB[154], x01_FB[155], x01_FB[156], x01_FB[157], x01_FB[158], x01_FB[159], x01_FB[160],
+                x01_FB[161], x01_FB[162], x01_FB[163], x01_FB[164], x01_FB[165], x01_FB[166], x01_FB[167], x01_FB[168],
+                x01_FB[169], x01_FB[170], x01_FB[171], x01_FB[172], x01_FB[173], x01_FB[174], x01_FB[175], x01_FB[176],
+                x01_FB[177], x01_FB[178], x01_FB[179], x01_FB[180], x01_FB[181], x01_FB[182], x01_FB[183], x01_FB[184],
+                x01_FB[185], x01_FB[186], x01_FB[187], x01_FB[188], x01_FB[189], x01_FB[190], x01_FB[191], x01_FB[192],
+                x01_FB[193], x01_FB[194], x01_FB[195], x01_FB[196], x01_FB[197], x01_FB[198], x01_FB[199], x01_FB[200],
+                x01_FB[201], x01_FB[202], x01_FB[203], x01_FB[204], x01_FB[205], x01_FB[206], x01_FB[207], x01_FB[208],
+                x01_FB[209], x01_FB[210], x01_FB[211], x01_FB[212], x01_FB[213], x01_FB[214], x01_FB[215], x01_FB[216],
+                x01_FB[217], x01_FB[218], x01_FB[219], x01_FB[220], x01_FB[221], x01_FB[222], x01_FB[223], x01_FB[224],
+                x01_FB[225], x01_FB[226], x01_FB[227], x01_FB[228], x01_FB[229], x01_FB[230], x01_FB[231], x01_FB[232],
+                x01_FB[233], x01_FB[234], x01_FB[235], x01_FB[236], x01_FB[237], x01_FB[238], x01_FB[239], x01_FB[240],
+                x01_FB[241], x01_FB[242], x01_FB[243], x01_FB[244], x01_FB[245], x01_FB[246], x01_FB[247], x01_FB[248],
+                // </editor-fold>
+                x01_FB[249], x01_FB[250], xFC);
+    }
 
     @Test
     public void test253() throws Throwable {
@@ -720,6 +877,8 @@
         test253(mh, a, r0);
         MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY);
         test253(mh_CA, a, r0);
+        MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2);
+        test253(mh_a, a, r0);
     }
     public void test253(MethodHandle mh, Object[] a, Object r0) throws Throwable {
         Object r;
@@ -899,6 +1058,43 @@
     // </editor-fold>
     xF8, xF9, xFA, xFB, xFC, xFD);
     }
+    static Object hashArguments_254_a(Object x00, Object[] x01_FC, Object xFD) {
+        return Objects.hash(
+                // <editor-fold defaultstate="collapsed" desc="x00, x01_FC[0], x01_FC[1], x01_FC[2], ...">
+                x00, x01_FC[0], x01_FC[1], x01_FC[2], x01_FC[3], x01_FC[4], x01_FC[5], x01_FC[6], x01_FC[7], x01_FC[8],
+                x01_FC[9], x01_FC[10], x01_FC[11], x01_FC[12], x01_FC[13], x01_FC[14], x01_FC[15], x01_FC[16],
+                x01_FC[17], x01_FC[18], x01_FC[19], x01_FC[20], x01_FC[21], x01_FC[22], x01_FC[23], x01_FC[24],
+                x01_FC[25], x01_FC[26], x01_FC[27], x01_FC[28], x01_FC[29], x01_FC[30], x01_FC[31], x01_FC[32],
+                x01_FC[33], x01_FC[34], x01_FC[35], x01_FC[36], x01_FC[37], x01_FC[38], x01_FC[39], x01_FC[40],
+                x01_FC[41], x01_FC[42], x01_FC[43], x01_FC[44], x01_FC[45], x01_FC[46], x01_FC[47], x01_FC[48],
+                x01_FC[49], x01_FC[50], x01_FC[51], x01_FC[52], x01_FC[53], x01_FC[54], x01_FC[55], x01_FC[56],
+                x01_FC[57], x01_FC[58], x01_FC[59], x01_FC[60], x01_FC[61], x01_FC[62], x01_FC[63], x01_FC[64],
+                x01_FC[65], x01_FC[66], x01_FC[67], x01_FC[68], x01_FC[69], x01_FC[70], x01_FC[71], x01_FC[72],
+                x01_FC[73], x01_FC[74], x01_FC[75], x01_FC[76], x01_FC[77], x01_FC[78], x01_FC[79], x01_FC[80],
+                x01_FC[81], x01_FC[82], x01_FC[83], x01_FC[84], x01_FC[85], x01_FC[86], x01_FC[87], x01_FC[88],
+                x01_FC[89], x01_FC[90], x01_FC[91], x01_FC[92], x01_FC[93], x01_FC[94], x01_FC[95], x01_FC[96],
+                x01_FC[97], x01_FC[98], x01_FC[99], x01_FC[100], x01_FC[101], x01_FC[102], x01_FC[103], x01_FC[104],
+                x01_FC[105], x01_FC[106], x01_FC[107], x01_FC[108], x01_FC[109], x01_FC[110], x01_FC[111], x01_FC[112],
+                x01_FC[113], x01_FC[114], x01_FC[115], x01_FC[116], x01_FC[117], x01_FC[118], x01_FC[119], x01_FC[120],
+                x01_FC[121], x01_FC[122], x01_FC[123], x01_FC[124], x01_FC[125], x01_FC[126], x01_FC[127], x01_FC[128],
+                x01_FC[129], x01_FC[130], x01_FC[131], x01_FC[132], x01_FC[133], x01_FC[134], x01_FC[135], x01_FC[136],
+                x01_FC[137], x01_FC[138], x01_FC[139], x01_FC[140], x01_FC[141], x01_FC[142], x01_FC[143], x01_FC[144],
+                x01_FC[145], x01_FC[146], x01_FC[147], x01_FC[148], x01_FC[149], x01_FC[150], x01_FC[151], x01_FC[152],
+                x01_FC[153], x01_FC[154], x01_FC[155], x01_FC[156], x01_FC[157], x01_FC[158], x01_FC[159], x01_FC[160],
+                x01_FC[161], x01_FC[162], x01_FC[163], x01_FC[164], x01_FC[165], x01_FC[166], x01_FC[167], x01_FC[168],
+                x01_FC[169], x01_FC[170], x01_FC[171], x01_FC[172], x01_FC[173], x01_FC[174], x01_FC[175], x01_FC[176],
+                x01_FC[177], x01_FC[178], x01_FC[179], x01_FC[180], x01_FC[181], x01_FC[182], x01_FC[183], x01_FC[184],
+                x01_FC[185], x01_FC[186], x01_FC[187], x01_FC[188], x01_FC[189], x01_FC[190], x01_FC[191], x01_FC[192],
+                x01_FC[193], x01_FC[194], x01_FC[195], x01_FC[196], x01_FC[197], x01_FC[198], x01_FC[199], x01_FC[200],
+                x01_FC[201], x01_FC[202], x01_FC[203], x01_FC[204], x01_FC[205], x01_FC[206], x01_FC[207], x01_FC[208],
+                x01_FC[209], x01_FC[210], x01_FC[211], x01_FC[212], x01_FC[213], x01_FC[214], x01_FC[215], x01_FC[216],
+                x01_FC[217], x01_FC[218], x01_FC[219], x01_FC[220], x01_FC[221], x01_FC[222], x01_FC[223], x01_FC[224],
+                x01_FC[225], x01_FC[226], x01_FC[227], x01_FC[228], x01_FC[229], x01_FC[230], x01_FC[231], x01_FC[232],
+                x01_FC[233], x01_FC[234], x01_FC[235], x01_FC[236], x01_FC[237], x01_FC[238], x01_FC[239], x01_FC[240],
+                x01_FC[241], x01_FC[242], x01_FC[243], x01_FC[244], x01_FC[245], x01_FC[246], x01_FC[247], x01_FC[248],
+                // </editor-fold>
+                x01_FC[249], x01_FC[250], x01_FC[251], xFD);
+    }
 
     @Test
     public void test254() throws Throwable {
@@ -933,6 +1129,8 @@
         test254(mh, a, r0);
         MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY);
         test254(mh_CA, a, r0);
+        MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2);
+        test254(mh_a, a, r0);
     }
     public void test254(MethodHandle mh, Object[] a, Object r0) throws Throwable {
         Object r;
@@ -1094,6 +1292,43 @@
     // </editor-fold>
     xF8, xF9, xFA, xFB, xFC, xFD, xFE);
     }
+    static Object hashArguments_255_a(Object x00, Object[] x01_FD, Object xFE) {
+        return Objects.hash(
+                // <editor-fold defaultstate="collapsed" desc="x00, x01_FD[0], x01_FD[1], x01_FD[2], ...">
+                x00, x01_FD[0], x01_FD[1], x01_FD[2], x01_FD[3], x01_FD[4], x01_FD[5], x01_FD[6], x01_FD[7], x01_FD[8],
+                x01_FD[9], x01_FD[10], x01_FD[11], x01_FD[12], x01_FD[13], x01_FD[14], x01_FD[15], x01_FD[16],
+                x01_FD[17], x01_FD[18], x01_FD[19], x01_FD[20], x01_FD[21], x01_FD[22], x01_FD[23], x01_FD[24],
+                x01_FD[25], x01_FD[26], x01_FD[27], x01_FD[28], x01_FD[29], x01_FD[30], x01_FD[31], x01_FD[32],
+                x01_FD[33], x01_FD[34], x01_FD[35], x01_FD[36], x01_FD[37], x01_FD[38], x01_FD[39], x01_FD[40],
+                x01_FD[41], x01_FD[42], x01_FD[43], x01_FD[44], x01_FD[45], x01_FD[46], x01_FD[47], x01_FD[48],
+                x01_FD[49], x01_FD[50], x01_FD[51], x01_FD[52], x01_FD[53], x01_FD[54], x01_FD[55], x01_FD[56],
+                x01_FD[57], x01_FD[58], x01_FD[59], x01_FD[60], x01_FD[61], x01_FD[62], x01_FD[63], x01_FD[64],
+                x01_FD[65], x01_FD[66], x01_FD[67], x01_FD[68], x01_FD[69], x01_FD[70], x01_FD[71], x01_FD[72],
+                x01_FD[73], x01_FD[74], x01_FD[75], x01_FD[76], x01_FD[77], x01_FD[78], x01_FD[79], x01_FD[80],
+                x01_FD[81], x01_FD[82], x01_FD[83], x01_FD[84], x01_FD[85], x01_FD[86], x01_FD[87], x01_FD[88],
+                x01_FD[89], x01_FD[90], x01_FD[91], x01_FD[92], x01_FD[93], x01_FD[94], x01_FD[95], x01_FD[96],
+                x01_FD[97], x01_FD[98], x01_FD[99], x01_FD[100], x01_FD[101], x01_FD[102], x01_FD[103], x01_FD[104],
+                x01_FD[105], x01_FD[106], x01_FD[107], x01_FD[108], x01_FD[109], x01_FD[110], x01_FD[111], x01_FD[112],
+                x01_FD[113], x01_FD[114], x01_FD[115], x01_FD[116], x01_FD[117], x01_FD[118], x01_FD[119], x01_FD[120],
+                x01_FD[121], x01_FD[122], x01_FD[123], x01_FD[124], x01_FD[125], x01_FD[126], x01_FD[127], x01_FD[128],
+                x01_FD[129], x01_FD[130], x01_FD[131], x01_FD[132], x01_FD[133], x01_FD[134], x01_FD[135], x01_FD[136],
+                x01_FD[137], x01_FD[138], x01_FD[139], x01_FD[140], x01_FD[141], x01_FD[142], x01_FD[143], x01_FD[144],
+                x01_FD[145], x01_FD[146], x01_FD[147], x01_FD[148], x01_FD[149], x01_FD[150], x01_FD[151], x01_FD[152],
+                x01_FD[153], x01_FD[154], x01_FD[155], x01_FD[156], x01_FD[157], x01_FD[158], x01_FD[159], x01_FD[160],
+                x01_FD[161], x01_FD[162], x01_FD[163], x01_FD[164], x01_FD[165], x01_FD[166], x01_FD[167], x01_FD[168],
+                x01_FD[169], x01_FD[170], x01_FD[171], x01_FD[172], x01_FD[173], x01_FD[174], x01_FD[175], x01_FD[176],
+                x01_FD[177], x01_FD[178], x01_FD[179], x01_FD[180], x01_FD[181], x01_FD[182], x01_FD[183], x01_FD[184],
+                x01_FD[185], x01_FD[186], x01_FD[187], x01_FD[188], x01_FD[189], x01_FD[190], x01_FD[191], x01_FD[192],
+                x01_FD[193], x01_FD[194], x01_FD[195], x01_FD[196], x01_FD[197], x01_FD[198], x01_FD[199], x01_FD[200],
+                x01_FD[201], x01_FD[202], x01_FD[203], x01_FD[204], x01_FD[205], x01_FD[206], x01_FD[207], x01_FD[208],
+                x01_FD[209], x01_FD[210], x01_FD[211], x01_FD[212], x01_FD[213], x01_FD[214], x01_FD[215], x01_FD[216],
+                x01_FD[217], x01_FD[218], x01_FD[219], x01_FD[220], x01_FD[221], x01_FD[222], x01_FD[223], x01_FD[224],
+                x01_FD[225], x01_FD[226], x01_FD[227], x01_FD[228], x01_FD[229], x01_FD[230], x01_FD[231], x01_FD[232],
+                x01_FD[233], x01_FD[234], x01_FD[235], x01_FD[236], x01_FD[237], x01_FD[238], x01_FD[239], x01_FD[240],
+                x01_FD[241], x01_FD[242], x01_FD[243], x01_FD[244], x01_FD[245], x01_FD[246], x01_FD[247], x01_FD[248],
+                // </editor-fold>
+                x01_FD[249], x01_FD[250], x01_FD[251], x01_FD[252], xFE);
+    }
 
     @Test
     public void test255() throws Throwable {
@@ -1163,5 +1398,38 @@
         } catch (IllegalArgumentException ex) {
             System.out.println("OK: "+ex);
         }
+        MethodHandle mh_a;
+        try {
+            mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2);
+            throw new AssertionError("should not create an arity 255 collector method handle");
+        } catch (IllegalArgumentException ex) {
+            System.out.println("OK: "+ex);
+            mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-3);
+        }
+        try {
+            r = mh_a.invokeExact(
+                    // <editor-fold defaultstate="collapsed" desc="a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], ...">
+                    a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], a[0x05], a[0x06], a[0x07], a[0x08], a[0x09], a[0x0A], a[0x0B], a[0x0C], a[0x0D], a[0x0E], a[0x0F],
+                    a[0x10], a[0x11], a[0x12], a[0x13], a[0x14], a[0x15], a[0x16], a[0x17], a[0x18], a[0x19], a[0x1A], a[0x1B], a[0x1C], a[0x1D], a[0x1E], a[0x1F],
+                    a[0x20], a[0x21], a[0x22], a[0x23], a[0x24], a[0x25], a[0x26], a[0x27], a[0x28], a[0x29], a[0x2A], a[0x2B], a[0x2C], a[0x2D], a[0x2E], a[0x2F],
+                    a[0x30], a[0x31], a[0x32], a[0x33], a[0x34], a[0x35], a[0x36], a[0x37], a[0x38], a[0x39], a[0x3A], a[0x3B], a[0x3C], a[0x3D], a[0x3E], a[0x3F],
+                    a[0x40], a[0x41], a[0x42], a[0x43], a[0x44], a[0x45], a[0x46], a[0x47], a[0x48], a[0x49], a[0x4A], a[0x4B], a[0x4C], a[0x4D], a[0x4E], a[0x4F],
+                    a[0x50], a[0x51], a[0x52], a[0x53], a[0x54], a[0x55], a[0x56], a[0x57], a[0x58], a[0x59], a[0x5A], a[0x5B], a[0x5C], a[0x5D], a[0x5E], a[0x5F],
+                    a[0x60], a[0x61], a[0x62], a[0x63], a[0x64], a[0x65], a[0x66], a[0x67], a[0x68], a[0x69], a[0x6A], a[0x6B], a[0x6C], a[0x6D], a[0x6E], a[0x6F],
+                    a[0x70], a[0x71], a[0x72], a[0x73], a[0x74], a[0x75], a[0x76], a[0x77], a[0x78], a[0x79], a[0x7A], a[0x7B], a[0x7C], a[0x7D], a[0x7E], a[0x7F],
+                    a[0x80], a[0x81], a[0x82], a[0x83], a[0x84], a[0x85], a[0x86], a[0x87], a[0x88], a[0x89], a[0x8A], a[0x8B], a[0x8C], a[0x8D], a[0x8E], a[0x8F],
+                    a[0x90], a[0x91], a[0x92], a[0x93], a[0x94], a[0x95], a[0x96], a[0x97], a[0x98], a[0x99], a[0x9A], a[0x9B], a[0x9C], a[0x9D], a[0x9E], a[0x9F],
+                    a[0xA0], a[0xA1], a[0xA2], a[0xA3], a[0xA4], a[0xA5], a[0xA6], a[0xA7], a[0xA8], a[0xA9], a[0xAA], a[0xAB], a[0xAC], a[0xAD], a[0xAE], a[0xAF],
+                    a[0xB0], a[0xB1], a[0xB2], a[0xB3], a[0xB4], a[0xB5], a[0xB6], a[0xB7], a[0xB8], a[0xB9], a[0xBA], a[0xBB], a[0xBC], a[0xBD], a[0xBE], a[0xBF],
+                    a[0xC0], a[0xC1], a[0xC2], a[0xC3], a[0xC4], a[0xC5], a[0xC6], a[0xC7], a[0xC8], a[0xC9], a[0xCA], a[0xCB], a[0xCC], a[0xCD], a[0xCE], a[0xCF],
+                    a[0xD0], a[0xD1], a[0xD2], a[0xD3], a[0xD4], a[0xD5], a[0xD6], a[0xD7], a[0xD8], a[0xD9], a[0xDA], a[0xDB], a[0xDC], a[0xDD], a[0xDE], a[0xDF],
+                    a[0xE0], a[0xE1], a[0xE2], a[0xE3], a[0xE4], a[0xE5], a[0xE6], a[0xE7], a[0xE8], a[0xE9], a[0xEA], a[0xEB], a[0xEC], a[0xED], a[0xEE], a[0xEF],
+                    a[0xF0], a[0xF1], a[0xF2], a[0xF3], a[0xF4], a[0xF5], a[0xF6], a[0xF7],
+                    // </editor-fold>
+                    a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC], a[0xFD], a[0xFE]);
+            throw new AssertionError("should not call an arity 255 collector method handle");
+        } catch (LinkageError ex) {
+            System.out.println("OK: "+ex);
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/FindClassSecurityManager.java	Fri Nov 20 15:34:12 2015 +0100
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @run main/othervm/policy=findclass.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.FindClassSecurityManager
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.invoke.MethodHandles;
+
+public class FindClassSecurityManager {
+    public static void main(String[] args) throws Throwable {
+        assert null != System.getSecurityManager();
+        Class<?> thisClass = FindClassSecurityManager.class;
+        MethodHandles.Lookup lookup = MethodHandles.lookup();
+        Class<?> lookedUp = lookup.findClass(thisClass.getName());
+        assert thisClass == lookedUp;
+        Class<?> accessed = lookup.accessClass(thisClass);
+        assert thisClass == accessed;
+    }
+}
--- a/test/java/lang/invoke/MethodHandlesTest.java	Thu Nov 19 19:46:46 2015 -0800
+++ b/test/java/lang/invoke/MethodHandlesTest.java	Fri Nov 20 15:34:12 2015 +0100
@@ -32,6 +32,7 @@
 
 import test.java.lang.invoke.remote.RemoteExample;
 import java.lang.invoke.*;
+import static java.lang.invoke.MethodType.methodType;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.*;
 import java.util.*;
@@ -448,6 +449,7 @@
     }
     public static interface IntExample {
         public void            v0();
+        public default void    vd() { called("vd", this); }
         public static class Impl implements IntExample {
             public void        v0()     { called("Int/v0", this); }
             final String name;
@@ -719,9 +721,10 @@
     public void testFindSpecial0() throws Throwable {
         if (CAN_SKIP_WORKING)  return;
         startTest("findSpecial");
-        testFindSpecial(SubExample.class, Example.class, void.class, "v0");
-        testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
-        testFindSpecial(RemoteExample.class, PubExample.class, void.class, "Pub/pro_v0");
+        testFindSpecial(SubExample.class, Example.class, void.class, false, "v0");
+        testFindSpecial(SubExample.class, Example.class, void.class, false, "pkg_v0");
+        testFindSpecial(RemoteExample.class, PubExample.class, void.class, false, "Pub/pro_v0");
+        testFindSpecial(Example.class, IntExample.class, void.class, true, "vd");
         // Do some negative testing:
         for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) {
             testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0");
@@ -729,11 +732,12 @@
             testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", int.class);
             testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", Void.class);
             testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0");
+            testFindSpecial(false, lookup, Example.class, IntExample.class, void.class, "v0");
         }
     }
 
     void testFindSpecial(Class<?> specialCaller,
-                         Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+                         Class<?> defc, Class<?> ret, boolean dflt, String name, Class<?>... params) throws Throwable {
         if (specialCaller == RemoteExample.class) {
             testFindSpecial(false, EXAMPLE,  specialCaller, defc, ret, name, params);
             testFindSpecial(false, PRIVATE,  specialCaller, defc, ret, name, params);
@@ -742,11 +746,11 @@
             testFindSpecial(false, PUBLIC,   specialCaller, defc, ret, name, params);
             return;
         }
-        testFindSpecial(true,  EXAMPLE,  specialCaller, defc, ret, name, params);
-        testFindSpecial(true,  PRIVATE,  specialCaller, defc, ret, name, params);
-        testFindSpecial(false, PACKAGE,  specialCaller, defc, ret, name, params);
-        testFindSpecial(false, SUBCLASS, specialCaller, defc, ret, name, params);
-        testFindSpecial(false, PUBLIC,   specialCaller, defc, ret, name, params);
+        testFindSpecial(true,          EXAMPLE,  specialCaller, defc, ret, name, params);
+        testFindSpecial(true,          PRIVATE,  specialCaller, defc, ret, name, params);
+        testFindSpecial(false || dflt, PACKAGE,  specialCaller, defc, ret, name, params);
+        testFindSpecial(false,         SUBCLASS, specialCaller, defc, ret, name, params);
+        testFindSpecial(false,         PUBLIC,   specialCaller, defc, ret, name, params);
     }
     void testFindSpecial(boolean positive, Lookup lookup, Class<?> specialCaller,
                          Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
@@ -1834,6 +1838,7 @@
     @Test // SLOW
     public void testSpreadArguments() throws Throwable {
         CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments0);
+        CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments1);
     }
 
     public void testSpreadArguments0() throws Throwable {
@@ -1842,26 +1847,97 @@
         for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
             if (verbosity >= 3)
                 System.out.println("spreadArguments "+argType);
+            Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
             for (int nargs = 0; nargs < 50; nargs++) {
                 if (CAN_TEST_LIGHTLY && nargs > 11)  break;
                 for (int pos = 0; pos <= nargs; pos++) {
                     if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2)  continue;
                     if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
                         continue;
-                    testSpreadArguments(argType, pos, nargs);
+                    testSpreadArguments(argType, arrayType, pos, nargs);
                 }
             }
         }
     }
-    public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
+    public void testSpreadArguments(Class<?> argType, Class<?> arrayType, int pos, int nargs) throws Throwable {
         countTest();
-        Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
         MethodHandle target2 = varargsArray(arrayType, nargs);
         MethodHandle target = target2.asType(target2.type().generic());
         if (verbosity >= 3)
             System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
         Object[] args = randomArgs(target2.type().parameterArray());
         // make sure the target does what we think it does:
+        checkTarget(argType, pos, nargs, target, args);
+        List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList());
+        {   // modify newParams in place
+            List<Class<?>> spreadParams = newParams.subList(pos, nargs);
+            spreadParams.clear(); spreadParams.add(arrayType);
+        }
+        MethodType newType = MethodType.methodType(arrayType, newParams);
+        MethodHandle result = target2.asSpreader(arrayType, nargs-pos);
+        assert(result.type() == newType) : Arrays.asList(result, newType);
+        result = result.asType(newType.generic());
+        Object returnValue;
+        if (pos == 0) {
+            Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
+            returnValue = result.invokeExact(args2);
+        } else {
+            Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
+            args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
+            returnValue = result.invokeWithArguments(args1);
+        }
+        checkReturnValue(argType, args, result, returnValue);
+    }
+    public void testSpreadArguments1() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("spreadArguments/pos");
+        for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
+            if (verbosity >= 3)
+                System.out.println("spreadArguments "+argType);
+            Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
+            for (int nargs = 0; nargs < 50; nargs++) {
+                if (CAN_TEST_LIGHTLY && nargs > 11)  break;
+                for (int pos = 0; pos <= nargs; pos++) {
+                    if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2)  continue;
+                    if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
+                        continue;
+                    for (int spr = 1; spr < nargs - pos; ++spr) {
+                        if (spr > 4 && spr != 7 && spr != 11 && spr != 20 && spr < nargs - pos - 4) continue;
+                        testSpreadArguments(argType, arrayType, pos, spr, nargs);
+                    }
+                }
+            }
+        }
+    }
+    public void testSpreadArguments(Class<?> argType, Class<?> arrayType, int pos, int spread, int nargs) throws Throwable {
+        countTest();
+        MethodHandle target2 = varargsArray(arrayType, nargs);
+        MethodHandle target = target2.asType(target2.type().generic());
+        if (verbosity >= 3)
+            System.out.println("spread into " + target2 + " [" + pos + ".." + (pos + spread) + "[");
+        Object[] args = randomArgs(target2.type().parameterArray());
+        // make sure the target does what we think it does:
+        checkTarget(argType, pos, nargs, target, args);
+        List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList());
+        {   // modify newParams in place
+            List<Class<?>> spreadParams = newParams.subList(pos, pos + spread);
+            spreadParams.clear();
+            spreadParams.add(arrayType);
+        }
+        MethodType newType = MethodType.methodType(arrayType, newParams);
+        MethodHandle result = target2.asSpreader(pos, arrayType, spread);
+        assert (result.type() == newType) : Arrays.asList(result, newType);
+        result = result.asType(newType.generic());
+        // args1 has nargs-spread entries, plus one for the to-be-spread array
+        int args1Length = nargs - (spread - 1);
+        Object[] args1 = new Object[args1Length];
+        System.arraycopy(args, 0, args1, 0, pos);
+        args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, pos + spread));
+        System.arraycopy(args, pos + spread, args1, pos + 1, nargs - spread - pos);
+        Object returnValue = result.invokeWithArguments(args1);
+        checkReturnValue(argType, args, result, returnValue);
+    }
+    private static void checkTarget(Class<?> argType, int pos, int nargs, MethodHandle target, Object[] args) throws Throwable {
         if (pos == 0 && nargs < 5 && !argType.isPrimitive()) {
             Object[] check = (Object[]) target.invokeWithArguments(args);
             assertArrayEquals(args, check);
@@ -1880,24 +1956,8 @@
                     break;
             }
         }
-        List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList());
-        {   // modify newParams in place
-            List<Class<?>> spreadParams = newParams.subList(pos, nargs);
-            spreadParams.clear(); spreadParams.add(arrayType);
-        }
-        MethodType newType = MethodType.methodType(arrayType, newParams);
-        MethodHandle result = target2.asSpreader(arrayType, nargs-pos);
-        assert(result.type() == newType) : Arrays.asList(result, newType);
-        result = result.asType(newType.generic());
-        Object returnValue;
-        if (pos == 0) {
-            Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
-            returnValue = result.invokeExact(args2);
-        } else {
-            Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
-            args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
-            returnValue = result.invokeWithArguments(args1);
-        }
+    }
+    private static void checkReturnValue(Class<?> argType, Object[] args, MethodHandle result, Object returnValue) {
         String argstr = Arrays.toString(args);
         if (!argType.isPrimitive()) {
             Object[] rv = (Object[]) returnValue;
@@ -1932,6 +1992,7 @@
     @Test // SLOW
     public void testAsCollector() throws Throwable {
         CodeCacheOverflowProcessor.runMHTest(this::testAsCollector0);
+        CodeCacheOverflowProcessor.runMHTest(this::testAsCollector1);
     }
 
     public void testAsCollector0() throws Throwable {
@@ -1974,6 +2035,51 @@
 //        collectedArgs[pos] = Arrays.asList((Object[]) collectedArgs[pos]);
         assertArrayEquals(collectedArgs, returnValue);
     }
+    public void testAsCollector1() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("asCollector/pos");
+        for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
+            if (verbosity >= 3)
+                System.out.println("asCollector/pos "+argType);
+            for (int nargs = 0; nargs < 50; nargs++) {
+                if (CAN_TEST_LIGHTLY && nargs > 11)  break;
+                for (int pos = 0; pos <= nargs; pos++) {
+                    if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2)  continue;
+                    if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
+                        continue;
+                    for (int coll = 1; coll < nargs - pos; ++coll) {
+                        if (coll > 4 && coll != 7 && coll != 11 && coll != 20 && coll < nargs - pos - 4) continue;
+                        testAsCollector(argType, pos, coll, nargs);
+                    }
+                }
+            }
+        }
+    }
+    public void testAsCollector(Class<?> argType, int pos, int collect, int nargs) throws Throwable {
+        countTest();
+        // fake up a MH with the same type as the desired adapter:
+        MethodHandle fake = varargsArray(nargs);
+        fake = changeArgTypes(fake, argType);
+        MethodType newType = fake.type();
+        Object[] args = randomArgs(newType.parameterArray());
+        // here is what should happen:
+        // new arg list has "collect" less arguments, but one extra for collected arguments array
+        int collectedLength = nargs-(collect-1);
+        Object[] collectedArgs = new Object[collectedLength];
+        System.arraycopy(args, 0, collectedArgs, 0, pos);
+        collectedArgs[pos] = Arrays.copyOfRange(args, pos, pos+collect);
+        System.arraycopy(args, pos+collect, collectedArgs, pos+1, args.length-(pos+collect));
+        // here is the MH which will witness the collected argument part (not tail!):
+        MethodHandle target = varargsArray(collectedLength);
+        target = changeArgTypes(target, 0, pos, argType);
+        target = changeArgTypes(target, pos, pos+1, Object[].class);
+        target = changeArgTypes(target, pos+1, collectedLength, argType);
+        if (verbosity >= 3)
+            System.out.println("collect "+collect+" from "+Arrays.asList(args)+" ["+pos+".."+(pos+collect)+"[");
+        MethodHandle result = target.asCollector(pos, Object[].class, collect).asType(newType);
+        Object[] returnValue = (Object[]) result.invokeWithArguments(args);
+        assertArrayEquals(collectedArgs, returnValue);
+    }
 
     @Test // SLOW
     public void testInsertArguments() throws Throwable {
@@ -2117,21 +2223,29 @@
     public void testCollectArguments0() throws Throwable {
         if (CAN_SKIP_WORKING)  return;
         startTest("collectArguments");
-        testFoldOrCollectArguments(true);
+        testFoldOrCollectArguments(true, false);
     }
 
     @Test
     public void testFoldArguments() throws Throwable {
         CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments0);
+        CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments1);
     }
 
     public void testFoldArguments0() throws Throwable {
         if (CAN_SKIP_WORKING)  return;
         startTest("foldArguments");
-        testFoldOrCollectArguments(false);
+        testFoldOrCollectArguments(false, false);
     }
 
-    void testFoldOrCollectArguments(boolean isCollect) throws Throwable {
+    public void testFoldArguments1() throws Throwable {
+        if (CAN_SKIP_WORKING) return;
+        startTest("foldArguments/pos");
+        testFoldOrCollectArguments(false, true);
+    }
+
+    void testFoldOrCollectArguments(boolean isCollect, boolean withFoldPos) throws Throwable {
+        assert !(isCollect && withFoldPos); // exclude illegal argument combination
         for (Class<?> lastType : new Class<?>[]{ Object.class, String.class, int.class }) {
             for (Class<?> collectType : new Class<?>[]{ Object.class, String.class, int.class, void.class }) {
                 int maxArity = 10;
@@ -2146,7 +2260,7 @@
                         if (!mixArgs(argTypes, mix, argTypesSeen))  continue;
                         for (int collect = 0; collect <= nargs; collect++) {
                             for (int pos = 0; pos <= nargs - collect; pos++) {
-                                testFoldOrCollectArguments(argTypes, pos, collect, collectType, lastType, isCollect);
+                                testFoldOrCollectArguments(argTypes, pos, collect, collectType, lastType, isCollect, withFoldPos);
                             }
                         }
                     }
@@ -2186,13 +2300,14 @@
                                     int pos, int fold, // position and length of the folded arguments
                                     Class<?> combineType, // type returned from the combiner
                                     Class<?> lastType,  // type returned from the target
-                                    boolean isCollect) throws Throwable {
+                                    boolean isCollect,
+                                    boolean withFoldPos) throws Throwable {
         int nargs = argTypes.size();
-        if (pos != 0 && !isCollect)  return;  // can fold only at pos=0 for now
+        if (pos != 0 && !isCollect && !withFoldPos)  return;  // test MethodHandles.foldArguments(MH,MH) only for pos=0
         countTest();
         List<Class<?>> combineArgTypes = argTypes.subList(pos, pos + fold);
         List<Class<?>> targetArgTypes = new ArrayList<>(argTypes);
-        if (isCollect)  // does targret see arg[pos..pos+cc-1]?
+        if (isCollect)  // does target see arg[pos..pos+cc-1]?
             targetArgTypes.subList(pos, pos + fold).clear();
         if (combineType != void.class)
             targetArgTypes.add(pos, combineType);
@@ -2205,7 +2320,7 @@
         if (isCollect)
             target2 = MethodHandles.collectArguments(target, pos, combine);
         else
-            target2 = MethodHandles.foldArguments(target, combine);
+            target2 = withFoldPos ? MethodHandles.foldArguments(target, pos, combine) : MethodHandles.foldArguments(target, combine);
         // Simulate expected effect of combiner on arglist:
         List<Object> expectedList = new ArrayList<>(argsToPass);
         List<Object> argsToFold = expectedList.subList(pos, pos + fold);
@@ -2541,6 +2656,203 @@
     }
 
     @Test
+    public void testGenericLoopCombinator() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testGenericLoopCombinator0);
+    }
+    public void testGenericLoopCombinator0() throws Throwable {
+        if (CAN_SKIP_WORKING) return;
+        startTest("loop");
+        // Test as follows:
+        // * Have an increasing number of loop-local state. Local state type diversity grows with the number.
+        // * Initializers set the starting value of loop-local state from the corresponding loop argument.
+        // * For each local state element, there is a predicate - for all state combinations, exercise all predicates.
+        // * Steps modify each local state element in each iteration.
+        // * Finalizers group all local state elements into a resulting array. Verify end values.
+        // * Exercise both pre- and post-checked loops.
+        // Local state types, start values, predicates, and steps:
+        // * int a, 0, a < 7, a = a + 1
+        // * double b, 7.0, b > 0.5, b = b / 2.0
+        // * String c, "start", c.length <= 9, c = c + a
+        final Class<?>[] argTypes = new Class<?>[] {int.class, double.class, String.class};
+        final Object[][] args = new Object[][] {
+            new Object[]{0              },
+            new Object[]{0, 7.0         },
+            new Object[]{0, 7.0, "start"}
+        };
+        // These are the expected final state tuples for argument type tuple / predicate combinations, for pre- and
+        // post-checked loops:
+        final Object[][] preCheckedResults = new Object[][] {
+            new Object[]{7                           }, // (int) / int
+            new Object[]{7, 0.0546875                }, // (int,double) / int
+            new Object[]{5, 0.4375                   }, // (int,double) / double
+            new Object[]{7, 0.0546875, "start1234567"}, // (int,double,String) / int
+            new Object[]{5, 0.4375,    "start1234"   }, // (int,double,String) / double
+            new Object[]{6, 0.109375,  "start12345"  }  // (int,double,String) / String
+        };
+        final Object[][] postCheckedResults = new Object[][] {
+            new Object[]{7                         }, // (int) / int
+            new Object[]{7, 0.109375               }, // (int,double) / int
+            new Object[]{4, 0.4375                 }, // (int,double) / double
+            new Object[]{7, 0.109375, "start123456"}, // (int,double,String) / int
+            new Object[]{4, 0.4375,   "start123"   }, // (int,double,String) / double
+            new Object[]{5, 0.21875,  "start12345" }  // (int,double,String) / String
+        };
+        final Lookup l = MethodHandles.lookup();
+        final Class<?> MHT = MethodHandlesTest.class;
+        final Class<?> B = boolean.class;
+        final Class<?> I = int.class;
+        final Class<?> D = double.class;
+        final Class<?> S = String.class;
+        final MethodHandle hip = l.findStatic(MHT, "loopIntPred", methodType(B, I));
+        final MethodHandle hdp = l.findStatic(MHT, "loopDoublePred", methodType(B, I, D));
+        final MethodHandle hsp = l.findStatic(MHT, "loopStringPred", methodType(B, I, D, S));
+        final MethodHandle his = l.findStatic(MHT, "loopIntStep", methodType(I, I));
+        final MethodHandle hds = l.findStatic(MHT, "loopDoubleStep", methodType(D, I, D));
+        final MethodHandle hss = l.findStatic(MHT, "loopStringStep", methodType(S, I, D, S));
+        final MethodHandle[] preds = new MethodHandle[] {hip, hdp, hsp};
+        final MethodHandle[] steps = new MethodHandle[] {his, hds, hss};
+        for (int nargs = 1, useResultsStart = 0; nargs <= argTypes.length; useResultsStart += nargs++) {
+            Class<?>[] useArgTypes = Arrays.copyOf(argTypes, nargs, Class[].class);
+            MethodHandle[] usePreds = Arrays.copyOf(preds, nargs, MethodHandle[].class);
+            MethodHandle[] useSteps = Arrays.copyOf(steps, nargs, MethodHandle[].class);
+            Object[] useArgs = args[nargs - 1];
+            Object[][] usePreCheckedResults = new Object[nargs][];
+            Object[][] usePostCheckedResults = new Object[nargs][];
+            System.arraycopy(preCheckedResults, useResultsStart, usePreCheckedResults, 0, nargs);
+            System.arraycopy(postCheckedResults, useResultsStart, usePostCheckedResults, 0, nargs);
+            testGenericLoopCombinator(nargs, useArgTypes, usePreds, useSteps, useArgs, usePreCheckedResults,
+                    usePostCheckedResults);
+        }
+    }
+    void testGenericLoopCombinator(int nargs, Class<?>[] argTypes, MethodHandle[] preds, MethodHandle[] steps,
+                                   Object[] args, Object[][] preCheckedResults, Object[][] postCheckedResults)
+            throws Throwable {
+        List<Class<?>> lArgTypes = Arrays.asList(argTypes);
+        // Predicate and step handles are passed in as arguments, initializer and finalizer handles are constructed here
+        // from the available information.
+        MethodHandle[] inits = new MethodHandle[nargs];
+        for (int i = 0; i < nargs; ++i) {
+            MethodHandle h;
+            // Initializers are meant to return whatever they are passed at a given argument position. This means that
+            // additional arguments may have to be appended and prepended.
+            h = MethodHandles.identity(argTypes[i]);
+            if (i < nargs - 1) {
+                h = MethodHandles.dropArguments(h, 1, lArgTypes.subList(i + 1, nargs));
+            }
+            if (i > 0) {
+                h = MethodHandles.dropArguments(h, 0, lArgTypes.subList(0, i));
+            }
+            inits[i] = h;
+        }
+        // Finalizers are all meant to collect all of the loop-local state in a single array and return that. Local
+        // state is passed before the loop args. Construct such a finalizer by first taking a varargsArray collector for
+        // the number of local state arguments, and then appending the loop args as to-be-dropped arguments.
+        MethodHandle[] finis = new MethodHandle[nargs];
+        MethodHandle genericFini = MethodHandles.dropArguments(
+                varargsArray(nargs).asType(methodType(Object[].class, lArgTypes)), nargs, lArgTypes);
+        Arrays.fill(finis, genericFini);
+        // The predicate and step handles' signatures need to be extended. They currently just accept local state args;
+        // append possibly missing local state args and loop args using dropArguments.
+        for (int i = 0; i < nargs; ++i) {
+            List<Class<?>> additionalLocalStateArgTypes = lArgTypes.subList(i + 1, nargs);
+            preds[i] = MethodHandles.dropArguments(
+                    MethodHandles.dropArguments(preds[i], i + 1, additionalLocalStateArgTypes), nargs, lArgTypes);
+            steps[i] = MethodHandles.dropArguments(
+                    MethodHandles.dropArguments(steps[i], i + 1, additionalLocalStateArgTypes), nargs, lArgTypes);
+        }
+        // Iterate over all of the predicates, using only one of them at a time.
+        for (int i = 0; i < nargs; ++i) {
+            MethodHandle[] usePreds;
+            if (nargs == 1) {
+                usePreds = preds;
+            } else {
+                // Create an all-null preds array, and only use one predicate in this iteration. The null entries will
+                // be substituted with true predicates by the loop combinator.
+                usePreds = new MethodHandle[nargs];
+                usePreds[i] = preds[i];
+            }
+            // Go for it.
+            if (verbosity >= 3) {
+                System.out.println("calling loop for argument types " + lArgTypes + " with predicate at index " + i);
+                if (verbosity >= 5) {
+                    System.out.println("predicates: " + Arrays.asList(usePreds));
+                }
+            }
+            MethodHandle[] preInits = new MethodHandle[nargs + 1];
+            MethodHandle[] prePreds = new MethodHandle[nargs + 1];
+            MethodHandle[] preSteps = new MethodHandle[nargs + 1];
+            MethodHandle[] preFinis = new MethodHandle[nargs + 1];
+            System.arraycopy(inits, 0, preInits, 1, nargs);
+            System.arraycopy(usePreds, 0, prePreds, 0, nargs); // preds are offset by 1 for pre-checked loops
+            System.arraycopy(steps, 0, preSteps, 1, nargs);
+            System.arraycopy(finis, 0, preFinis, 0, nargs); // finis are also offset by 1 for pre-checked loops
+            // Convert to clause-major form.
+            MethodHandle[][] preClauses = new MethodHandle[nargs+1][4];
+            MethodHandle[][] postClauses = new MethodHandle[nargs][4];
+            toClauseMajor(preClauses, preInits, preSteps, prePreds, preFinis);
+            toClauseMajor(postClauses, inits, steps, usePreds, finis);
+            MethodHandle pre = MethodHandles.loop(preClauses);
+            MethodHandle post = MethodHandles.loop(postClauses);
+            Object[] preResults = (Object[]) pre.invokeWithArguments(args);
+            if (verbosity >= 4) {
+                System.out.println("pre-checked: expected " + Arrays.asList(preCheckedResults[i]) + ", actual " +
+                        Arrays.asList(preResults));
+            }
+            Object[] postResults = (Object[]) post.invokeWithArguments(args);
+            if (verbosity >= 4) {
+                System.out.println("post-checked: expected " + Arrays.asList(postCheckedResults[i]) + ", actual " +
+                        Arrays.asList(postResults));
+            }
+            assertArrayEquals(preCheckedResults[i], preResults);
+            assertArrayEquals(postCheckedResults[i], postResults);
+        }
+    }
+    static void toClauseMajor(MethodHandle[][] clauses, MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini) {
+        for (int i = 0; i < clauses.length; ++i) {
+            clauses[i][0] = init[i];
+            clauses[i][1] = step[i];
+            clauses[i][2] = pred[i];
+            clauses[i][3] = fini[i];
+        }
+    }
+    static boolean loopIntPred(int a) {
+        if (verbosity >= 5) {
+            System.out.println("int pred " + a + " -> " + (a < 7));
+        }
+        return a < 7;
+    }
+    static boolean loopDoublePred(int a, double b) {
+        if (verbosity >= 5) {
+            System.out.println("double pred (a=" + a + ") " + b + " -> " + (b > 0.5));
+        }
+        return b > 0.5;
+    }
+    static boolean loopStringPred(int a, double b, String c) {
+        if (verbosity >= 5) {
+            System.out.println("String pred (a=" + a + ",b=" + b + ") " + c + " -> " + (c.length() <= 9));
+        }
+        return c.length() <= 9;
+    }
+    static int loopIntStep(int a) {
+        if (verbosity >= 5) {
+            System.out.println("int step " + a + " -> " + (a + 1));
+        }
+        return a + 1;
+    }
+    static double loopDoubleStep(int a, double b) {
+        if (verbosity >= 5) {
+            System.out.println("double step (a=" + a + ") " + b + " -> " + (b / 2.0));
+        }
+        return b / 2.0;
+    }
+    static String loopStringStep(int a, double b, String c) {
+        if (verbosity >= 5) {
+            System.out.println("String step (a=" + a + ",b=" + b + ") " + c + " -> " + (c + a));
+        }
+        return c + a;
+    }
+
+    @Test
     public void testThrowException() throws Throwable {
         CodeCacheOverflowProcessor.runMHTest(this::testThrowException0);
     }
@@ -2576,12 +2888,107 @@
     }
 
     @Test
+    public void testTryFinally() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testTryFinally0);
+    }
+    public void testTryFinally0() throws Throwable {
+        if (CAN_SKIP_WORKING) return;
+        startTest("tryFinally");
+        String inputMessage = "returned";
+        String augmentedMessage = "augmented";
+        String thrownMessage = "thrown";
+        String rethrownMessage = "rethrown";
+        // Test these cases:
+        // * target returns, cleanup passes through
+        // * target returns, cleanup augments
+        // * target throws, cleanup augments and returns
+        // * target throws, cleanup augments and rethrows
+        MethodHandle target = MethodHandles.identity(String.class);
+        MethodHandle targetThrow = MethodHandles.dropArguments(
+                MethodHandles.throwException(String.class, Exception.class).bindTo(new Exception(thrownMessage)), 0, String.class);
+        MethodHandle cleanupPassThrough = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0,
+                Throwable.class, String.class);
+        MethodHandle cleanupAugment = MethodHandles.dropArguments(MethodHandles.constant(String.class, augmentedMessage),
+                0, Throwable.class, String.class, String.class);
+        MethodHandle cleanupCatch = MethodHandles.dropArguments(MethodHandles.constant(String.class, thrownMessage), 0,
+                Throwable.class, String.class, String.class);
+        MethodHandle cleanupThrow = MethodHandles.dropArguments(MethodHandles.throwException(String.class, Exception.class).
+                bindTo(new Exception(rethrownMessage)), 0, Throwable.class, String.class, String.class);
+        testTryFinally(target, cleanupPassThrough, inputMessage, inputMessage, false);
+        testTryFinally(target, cleanupAugment, inputMessage, augmentedMessage, false);
+        testTryFinally(targetThrow, cleanupCatch, inputMessage, thrownMessage, true);
+        testTryFinally(targetThrow, cleanupThrow, inputMessage, rethrownMessage, true);
+        // Test the same cases as above for void targets and cleanups.
+        MethodHandles.Lookup lookup = MethodHandles.lookup();
+        Class<?> C = this.getClass();
+        MethodType targetType = methodType(void.class, String[].class);
+        MethodType cleanupType = methodType(void.class, Throwable.class, String[].class);
+        MethodHandle vtarget = lookup.findStatic(C, "vtarget", targetType);
+        MethodHandle vtargetThrow = lookup.findStatic(C, "vtargetThrow", targetType);
+        MethodHandle vcleanupPassThrough = lookup.findStatic(C, "vcleanupPassThrough", cleanupType);
+        MethodHandle vcleanupAugment = lookup.findStatic(C, "vcleanupAugment", cleanupType);
+        MethodHandle vcleanupCatch = lookup.findStatic(C, "vcleanupCatch", cleanupType);
+        MethodHandle vcleanupThrow = lookup.findStatic(C, "vcleanupThrow", cleanupType);
+        testTryFinally(vtarget, vcleanupPassThrough, inputMessage, inputMessage, false);
+        testTryFinally(vtarget, vcleanupAugment, inputMessage, augmentedMessage, false);
+        testTryFinally(vtargetThrow, vcleanupCatch, inputMessage, thrownMessage, true);
+        testTryFinally(vtargetThrow, vcleanupThrow, inputMessage, rethrownMessage, true);
+    }
+    void testTryFinally(MethodHandle target, MethodHandle cleanup, String input, String msg, boolean mustCatch)
+            throws Throwable {
+        countTest();
+        MethodHandle tf = MethodHandles.tryFinally(target, cleanup);
+        String result = null;
+        boolean isVoid = target.type().returnType() == void.class;
+        String[] argArray = new String[]{input};
+        try {
+            if (isVoid) {
+                tf.invoke(argArray);
+            } else {
+                result = (String) tf.invoke(input);
+            }
+        } catch (Throwable t) {
+            assertTrue(mustCatch);
+            assertEquals(msg, t.getMessage());
+            return;
+        }
+        assertFalse(mustCatch);
+        if (isVoid) {
+            assertEquals(msg, argArray[0]);
+        } else {
+            assertEquals(msg, result);
+        }
+    }
+    static void vtarget(String[] a) {
+        // naught, akin to identity
+    }
+    static void vtargetThrow(String[] a) throws Exception {
+        throw new Exception("thrown");
+    }
+    static void vcleanupPassThrough(Throwable t, String[] a) {
+        assertNull(t);
+        // naught, akin to identity
+    }
+    static void vcleanupAugment(Throwable t, String[] a) {
+        assertNull(t);
+        a[0] = "augmented";
+    }
+    static void vcleanupCatch(Throwable t, String[] a) {
+        assertNotNull(t);
+        a[0] = "caught";
+    }
+    static void vcleanupThrow(Throwable t, String[] a) throws Exception {
+        assertNotNull(t);
+        throw new Exception("rethrown");
+    }
+
+    @Test
     public void testInterfaceCast() throws Throwable {
         CodeCacheOverflowProcessor.runMHTest(this::testInterfaceCast0);
     }
 
     public void testInterfaceCast0() throws Throwable {
-        //if (CAN_SKIP_WORKING)  return;
+        if (CAN_SKIP_WORKING)  return;
         startTest("interfaceCast");
         assert( (((Object)"foo") instanceof CharSequence));
         assert(!(((Object)"foo") instanceof Iterable));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/T8139885.java	Fri Nov 20 15:34:12 2015 +0100
@@ -0,0 +1,1082 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @run testng/othervm -ea -esa test.java.lang.invoke.T8139885
+ */
+
+package test.java.lang.invoke;
+
+import java.io.StringWriter;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.util.*;
+
+import static java.lang.invoke.MethodType.methodType;
+
+import static org.testng.AssertJUnit.*;
+
+import org.testng.annotations.*;
+
+/**
+ * Example-scale and negative tests for JEP 274 extensions.
+ */
+public class T8139885 {
+
+    static final Lookup LOOKUP = MethodHandles.lookup();
+
+    //
+    // Tests.
+    //
+
+    @Test
+    public static void testLoopFac() throws Throwable {
+        MethodHandle[] counterClause = new MethodHandle[]{Fac.MH_zero, Fac.MH_inc};
+        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));
+    }
+
+    @Test
+    public static void testLoopFacNullInit() throws Throwable {
+        // null initializer for counter, should initialize to 0
+        MethodHandle[] counterClause = new MethodHandle[]{null, Fac.MH_inc};
+        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));
+    }
+
+    @Test
+    public static void testLoopVoid1() throws Throwable {
+        // construct a post-checked loop that only does one iteration and has a void body and void local state
+        MethodHandle loop = MethodHandles.loop(new MethodHandle[]{Empty.MH_f, Empty.MH_f, Empty.MH_pred, null});
+        assertEquals(MethodType.methodType(void.class), loop.type());
+        loop.invoke();
+    }
+
+    @Test
+    public static void testLoopVoid2() throws Throwable {
+        // construct a post-checked loop that only does one iteration and has a void body and void local state,
+        // initialized implicitly from the step type
+        MethodHandle loop = MethodHandles.loop(new MethodHandle[]{null, Empty.MH_f, Empty.MH_pred, null});
+        assertEquals(MethodType.methodType(void.class), loop.type());
+        loop.invoke();
+    }
+
+    @Test
+    public static void testLoopFacWithVoidState() throws Throwable {
+        // like testLoopFac, but with additional void state that outputs a dot
+        MethodHandle[] counterClause = new MethodHandle[]{Fac.MH_zero, Fac.MH_inc};
+        MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin};
+        MethodHandle[] dotClause = new MethodHandle[]{null, Fac.MH_dot};
+        MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause, dotClause);
+        assertEquals(Fac.MT_fac, loop.type());
+        assertEquals(120, loop.invoke(5));
+    }
+
+    @Test
+    public static void testLoopNegative() throws Throwable {
+        MethodHandle mh_loop =
+                LOOKUP.findStatic(MethodHandles.class, "loop", methodType(MethodHandle.class, MethodHandle[][].class));
+        MethodHandle i0 = MethodHandles.constant(int.class, 0);
+        MethodHandle ii = MethodHandles.dropArguments(i0, 0, int.class, int.class);
+        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<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);
+        MethodHandle eek = MethodHandles.dropArguments(i0, 0, int.class, int.class, double.class);
+        List<MethodHandle> nesteps = Arrays.asList(Fac.MH_inc, eek, Fac.MH_dot);
+        List<MethodHandle> nepreds = Arrays.asList(null, Fac.MH_pred, null);
+        List<MethodHandle> nefinis = Arrays.asList(null, Fac.MH_fin, null);
+        MethodHandle[][][] cases = {
+                null,
+                {},
+                {{null, Fac.MH_inc}, {Fac.MH_one, null, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}},
+                {{null, Fac.MH_inc}, null},
+                {{Fac.MH_zero, Fac.MH_dot}},
+                {{ii}, {id}, {i3}},
+                {{null, Fac.MH_inc, null, Fac.MH_fin}, {null, Fac.MH_inc, null, Fac.MH_inc},
+                        {null, Counted.MH_start, null, Counted.MH_step}},
+                {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, Fac.MH_mult, null, Fac.MH_fin}, {null, Fac.MH_dot}},
+                {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, Fac.MH_mult, Fac.MH_fin, Fac.MH_fin}, {null, Fac.MH_dot}},
+                {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, eek, Fac.MH_pred, Fac.MH_fin}, {null, Fac.MH_dot}}
+        };
+        String[] messages = {
+                "null or no clauses passed",
+                "null or no clauses passed",
+                "All loop clauses must be represented as MethodHandle arrays with at most 4 elements.",
+                "null clauses are not allowed",
+                "clause 0: init and step return types must match: int != void",
+                "found non-effectively identical init parameter type lists: " + inits + " (common suffix: " + ints + ")",
+                "found non-identical finalizer return types: " + finis + " (return type: int)",
+                "no predicate found: " + preds1,
+                "predicates must have boolean return type: " + preds2,
+                "found non-effectively identical parameter type lists:\nstep: " + nesteps + "\npred: " + nepreds +
+                        "\nfini: " + nefinis + " (common parameter sequence: " + ints + ")"
+        };
+        for (int i = 0; i < cases.length; ++i) {
+            boolean caught = false;
+            try {
+                mh_loop.invokeWithArguments(cases[i]);
+            } catch (IllegalArgumentException iae) {
+                assertEquals(messages[i], iae.getMessage());
+                caught = true;
+            }
+            assertTrue(caught);
+        }
+    }
+
+    @Test
+    public static void testWhileLoop() 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));
+    }
+
+    @Test
+    public static void testWhileLoopNoIteration() throws Throwable {
+        // a while loop that never executes its body because the predicate evaluates to false immediately
+        MethodHandle loop = MethodHandles.whileLoop(While.MH_initString, While.MH_predString, While.MH_stepString);
+        assertEquals(While.MT_string, loop.type());
+        assertEquals("a", loop.invoke());
+    }
+
+    @Test
+    public static void testDoWhileLoop() 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));
+    }
+
+    @Test
+    public static void testWhileZip() throws Throwable {
+        MethodHandle loop = MethodHandles.doWhileLoop(While.MH_zipInitZip, While.MH_zipStep, While.MH_zipPred);
+        assertEquals(While.MT_zip, loop.type());
+        List<String> a = Arrays.asList("a", "b", "c", "d");
+        List<String> b = Arrays.asList("e", "f", "g", "h");
+        List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
+        assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator()));
+    }
+
+    @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 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!"));
+    }
+
+    @Test
+    public static void testCountedArrayLoop() throws Throwable {
+        // int[] a = new int[]{0}; for (int i = 0; i < 13; ++i) { ++a[0]; } => a[0] == 13
+        MethodHandle fit13 = MethodHandles.dropArguments(MethodHandles.constant(int.class, 13), 0, int[].class);
+        MethodHandle loop = MethodHandles.countedLoop(fit13, null, Counted.MH_stepUpdateArray);
+        assertEquals(Counted.MT_arrayCounted, loop.type());
+        int[] a = new int[]{0};
+        loop.invoke(a);
+        assertEquals(13, a[0]);
+    }
+
+    @Test
+    public static void testCountedPrintingLoop() throws Throwable {
+        MethodHandle fit5 = MethodHandles.constant(int.class, 5);
+        MethodHandle loop = MethodHandles.countedLoop(fit5, null, Counted.MH_printHello);
+        assertEquals(Counted.MT_countedPrinting, loop.type());
+        loop.invoke();
+    }
+
+    @Test
+    public static void testCountedRangeLoop() throws Throwable {
+        // String s = "Lambdaman!"; for (int i = -5; i < 8; ++i) { s = "na " + s; } return s; => a well known theme
+        MethodHandle fitm5 = MethodHandles.dropArguments(Counted.MH_m5, 0, String.class);
+        MethodHandle fit8 = MethodHandles.dropArguments(Counted.MH_8, 0, String.class);
+        MethodHandle loop = MethodHandles.countedLoop(fitm5, fit8, 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!"));
+    }
+
+    @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
+        MethodHandle loop = MethodHandles.iteratedLoop(Iterate.MH_sumIterator, Iterate.MH_sumInit, Iterate.MH_sumStep);
+        assertEquals(Iterate.MT_sum, loop.type());
+        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));
+    }
+
+    @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
+    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
+    public static void testIteratePrint() throws Throwable {
+        MethodHandle loop = MethodHandles.iteratedLoop(null, null, Iterate.MH_printStep);
+        assertEquals(Iterate.MT_print, loop.type());
+        loop.invoke(Arrays.asList("hello", "world"));
+    }
+
+    @Test
+    public static void testIterateNullBody() {
+        boolean caught = false;
+        try {
+            MethodHandles.iteratedLoop(MethodHandles.identity(int.class), MethodHandles.identity(int.class), null);
+        } catch (IllegalArgumentException iae) {
+            assertEquals("iterated loop body must not be null", iae.getMessage());
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+
+    @Test
+    public static void testTryFinally() throws Throwable {
+        MethodHandle hello = MethodHandles.tryFinally(TryFinally.MH_greet, TryFinally.MH_exclaim);
+        assertEquals(TryFinally.MT_hello, hello.type());
+        assertEquals("Hello, world!", hello.invoke("world"));
+    }
+
+    @Test
+    public static void testTryFinallyVoid() throws Throwable {
+        MethodHandle tfVoid = MethodHandles.tryFinally(TryFinally.MH_print, TryFinally.MH_printMore);
+        assertEquals(TryFinally.MT_printHello, tfVoid.type());
+        tfVoid.invoke("world");
+    }
+
+    @Test
+    public static void testTryFinallySublist() throws Throwable {
+        MethodHandle helloMore = MethodHandles.tryFinally(TryFinally.MH_greetMore, TryFinally.MH_exclaimMore);
+        assertEquals(TryFinally.MT_moreHello, helloMore.type());
+        assertEquals("Hello, world and universe (but world first)!", helloMore.invoke("world", "universe"));
+    }
+
+    @Test
+    public static void testTryFinallyNegative() {
+        MethodHandle intid = MethodHandles.identity(int.class);
+        MethodHandle intco = MethodHandles.constant(int.class, 0);
+        MethodHandle errTarget = MethodHandles.dropArguments(intco, 0, int.class, double.class, String.class, int.class);
+        MethodHandle errCleanup = MethodHandles.dropArguments(MethodHandles.constant(int.class, 0), 0, Throwable.class,
+                int.class, double.class, Object.class);
+        MethodHandle[][] cases = {
+                {intid, MethodHandles.identity(double.class)},
+                {intid, MethodHandles.dropArguments(intid, 0, String.class)},
+                {intid, MethodHandles.dropArguments(intid, 0, Throwable.class, double.class)},
+                {errTarget, errCleanup}
+        };
+        String[] messages = {
+                "target and return types must match: double != int",
+                "cleanup first argument and Throwable must match: (String,int)int != class java.lang.Throwable",
+                "cleanup second argument and target return type must match: (Throwable,double,int)int != int",
+                "cleanup parameters after (Throwable,result) and target parameter list prefix must match: " +
+                        errCleanup.type() + " != " + errTarget.type()
+        };
+        for (int i = 0; i < cases.length; ++i) {
+            boolean caught = false;
+            try {
+                MethodHandles.tryFinally(cases[i][0], cases[i][1]);
+            } catch (IllegalArgumentException iae) {
+                assertEquals(messages[i], iae.getMessage());
+                caught = true;
+            }
+            assertTrue(caught);
+        }
+    }
+
+    @Test
+    public static void testFold0a() throws Throwable {
+        // equivalence to foldArguments(MethodHandle,MethodHandle)
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 0, Fold.MH_adder);
+        assertEquals(Fold.MT_folded1, fold.type());
+        assertEquals(720, (int) fold.invoke(3, 4, 5));
+    }
+
+    @Test
+    public static void testFold1a() throws Throwable {
+        // test foldArguments for folding position 1
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 1, Fold.MH_adder1);
+        assertEquals(Fold.MT_folded1, fold.type());
+        assertEquals(540, (int) fold.invoke(3, 4, 5));
+    }
+
+    @Test
+    public static void testFold0b() throws Throwable {
+        // test foldArguments equivalence with multiple types
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 0, Fold.MH_comb);
+        assertEquals(Fold.MT_folded2, fold.type());
+        assertEquals(23, (int) fold.invoke("true", true, 23));
+    }
+
+    @Test
+    public static void testFold1b() throws Throwable {
+        // test folgArguments for folding position 1, with multiple types
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 1, Fold.MH_comb2);
+        assertEquals(Fold.MT_folded3, fold.type());
+        assertEquals(1, (int) fold.invoke(true, true, 1));
+        assertEquals(-1, (int) fold.invoke(true, false, -1));
+    }
+
+    @Test
+    public static void testFoldArgumentsExample() throws Throwable {
+        // test the JavaDoc foldArguments-with-pos example
+        StringWriter swr = new StringWriter();
+        MethodHandle trace = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, String.class)).bindTo(swr);
+        MethodHandle cat = LOOKUP.findVirtual(String.class, "concat", methodType(String.class, String.class));
+        assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
+        MethodHandle catTrace = MethodHandles.foldArguments(cat, 1, trace);
+        assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+        assertEquals("jum", swr.toString());
+    }
+
+    @Test
+    public static void testAsSpreader() throws Throwable {
+        MethodHandle spreader = SpreadCollect.MH_forSpreading.asSpreader(1, int[].class, 3);
+        assertEquals(SpreadCollect.MT_spreader, spreader.type());
+        assertEquals("A456B", (String) spreader.invoke("A", new int[]{4, 5, 6}, "B"));
+    }
+
+    @Test
+    public static void testAsSpreaderExample() throws Throwable {
+        // test the JavaDoc asSpreader-with-pos example
+        MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
+        MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
+        Object[] ints = new Object[]{3, 9, 7, 7};
+        Comparator<Integer> cmp = (a, b) -> a - b;
+        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
+        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
+        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
+    }
+
+    @Test
+    public static void testAsSpreaderIllegalPos() throws Throwable {
+        int[] illegalPos = {-7, 3, 19};
+        int caught = 0;
+        for (int p : illegalPos) {
+            try {
+                SpreadCollect.MH_forSpreading.asSpreader(p, Object[].class, 3);
+            } catch (IllegalArgumentException iae) {
+                assertEquals("bad spread position", iae.getMessage());
+                ++caught;
+            }
+        }
+        assertEquals(illegalPos.length, caught);
+    }
+
+    @Test
+    public static void testAsCollector() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collector1, collector.type());
+        assertEquals("A4B", (String) collector.invoke("A", 4, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collector2, collector.type());
+        assertEquals("A45B", (String) collector.invoke("A", 4, 5, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collector3, collector.type());
+        assertEquals("A456B", (String) collector.invoke("A", 4, 5, 6, "B"));
+    }
+
+    @Test
+    public static void testAsCollectorInvokeWithArguments() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collector1, collector.type());
+        assertEquals("A4B", (String) collector.invokeWithArguments("A", 4, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collector2, collector.type());
+        assertEquals("A45B", (String) collector.invokeWithArguments("A", 4, 5, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collector3, collector.type());
+        assertEquals("A456B", (String) collector.invokeWithArguments("A", 4, 5, 6, "B"));
+    }
+
+    @Test
+    public static void testAsCollectorLeading() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collectorLeading1, collector.type());
+        assertEquals("7Q", (String) collector.invoke(7, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collectorLeading2, collector.type());
+        assertEquals("78Q", (String) collector.invoke(7, 8, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collectorLeading3, collector.type());
+        assertEquals("789Q", (String) collector.invoke(7, 8, 9, "Q"));
+    }
+
+    @Test
+    public static void testAsCollectorLeadingInvokeWithArguments() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collectorLeading1, collector.type());
+        assertEquals("7Q", (String) collector.invokeWithArguments(7, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collectorLeading2, collector.type());
+        assertEquals("78Q", (String) collector.invokeWithArguments(7, 8, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collectorLeading3, collector.type());
+        assertEquals("789Q", (String) collector.invokeWithArguments(7, 8, 9, "Q"));
+    }
+
+    @Test
+    public static void testAsCollectorNone() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 0);
+        assertEquals(SpreadCollect.MT_collector0, collector.type());
+        assertEquals("AB", (String) collector.invoke("A", "B"));
+    }
+
+    @Test
+    public static void testAsCollectorIllegalPos() throws Throwable {
+        int[] illegalPos = {-1, 17};
+        int caught = 0;
+        for (int p : illegalPos) {
+            try {
+                SpreadCollect.MH_forCollecting.asCollector(p, int[].class, 0);
+            } catch (IllegalArgumentException iae) {
+                assertEquals("bad collect position", iae.getMessage());
+                ++caught;
+            }
+        }
+        assertEquals(illegalPos.length, caught);
+    }
+
+    @Test
+    public static void testAsCollectorExample() throws Throwable {
+        // test the JavaDoc asCollector-with-pos example
+        StringWriter swr = new StringWriter();
+        MethodHandle swWrite = LOOKUP.
+                findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).
+                bindTo(swr);
+        MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4);
+        swWrite4.invoke('A', 'B', 'C', 'D', 1, 2);
+        assertEquals("BC", swr.toString());
+        swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4);
+        assertEquals("BCPQRS", swr.toString());
+        swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
+        assertEquals("BCPQRSZ", swr.toString());
+    }
+
+    @Test
+    public static void testFindSpecial() throws Throwable {
+        FindSpecial.C c = new FindSpecial.C();
+        assertEquals("I1.m", c.m());
+        MethodType t = MethodType.methodType(String.class);
+        MethodHandle ci1m = LOOKUP.findSpecial(FindSpecial.I1.class, "m", t, FindSpecial.C.class);
+        assertEquals("I1.m", (String) ci1m.invoke(c));
+    }
+
+    @Test
+    public static void testFindSpecialAbstract() throws Throwable {
+        FindSpecial.C c = new FindSpecial.C();
+        assertEquals("q", c.q());
+        MethodType t = MethodType.methodType(String.class);
+        boolean caught = false;
+        try {
+            MethodHandle ci3q = LOOKUP.findSpecial(FindSpecial.I3.class, "q", t, FindSpecial.C.class);
+        } catch (Throwable thrown) {
+            if (!(thrown instanceof IllegalAccessException) || !FindSpecial.ABSTRACT_ERROR.equals(thrown.getMessage())) {
+                throw new AssertionError(thrown.getMessage(), thrown);
+            }
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+
+    @Test
+    public static void testFindClassCNFE() throws Throwable {
+        boolean caught = false;
+        try {
+            LOOKUP.findClass("does.not.Exist");
+        } catch (ClassNotFoundException cnfe) {
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+
+    //
+    // Methods used to assemble tests.
+    //
+
+    static class Empty {
+
+        static void f() { }
+
+        static boolean pred() {
+            return false;
+        }
+
+        static final Class<Empty> EMPTY = Empty.class;
+
+        static final MethodType MT_f = methodType(void.class);
+        static final MethodType MT_pred = methodType(boolean.class);
+
+        static final MethodHandle MH_f;
+        static final MethodHandle MH_pred;
+
+        static {
+            try {
+                MH_f = LOOKUP.findStatic(EMPTY, "f", MT_f);
+                MH_pred = LOOKUP.findStatic(EMPTY, "pred", MT_pred);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+    }
+
+    static class Fac {
+
+        static int zero(int k) {
+            return 0;
+        }
+
+        static int one(int k) {
+            return 1;
+        }
+
+        static boolean pred(int i, int acc, int k) {
+            return i < k;
+        }
+
+        static int inc(int i, int acc, int k) {
+            return i + 1;
+        }
+
+        static int mult(int i, int acc, int k) {
+            return i * acc;
+        }
+
+        static void dot(int i, int acc, int k) {
+            System.out.print('.');
+        }
+
+        static int fin(int i, int acc, int k) {
+            return acc;
+        }
+
+        static final Class<Fac> FAC = Fac.class;
+
+        static final MethodType MT_init = methodType(int.class, int.class);
+        static final MethodType MT_fn = methodType(int.class, int.class, int.class, int.class);
+        static final MethodType MT_dot = methodType(void.class, int.class, int.class, int.class);
+        static final MethodType MT_pred = methodType(boolean.class, int.class, int.class, int.class);
+
+        static final MethodHandle MH_zero;
+        static final MethodHandle MH_one;
+        static final MethodHandle MH_pred;
+        static final MethodHandle MH_inc;
+        static final MethodHandle MH_mult;
+        static final MethodHandle MH_dot;
+        static final MethodHandle MH_fin;
+
+        static final MethodType MT_fac = methodType(int.class, int.class);
+
+        static {
+            try {
+                MH_zero = LOOKUP.findStatic(FAC, "zero", MT_init);
+                MH_one = LOOKUP.findStatic(FAC, "one", MT_init);
+                MH_pred = LOOKUP.findStatic(FAC, "pred", MT_pred);
+                MH_inc = LOOKUP.findStatic(FAC, "inc", MT_fn);
+                MH_mult = LOOKUP.findStatic(FAC, "mult", MT_fn);
+                MH_dot = LOOKUP.findStatic(FAC, "dot", MT_dot);
+                MH_fin = LOOKUP.findStatic(FAC, "fin", MT_fn);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class While {
+
+        static int zero(int limit) {
+            return 0;
+        }
+
+        static boolean pred(int i, int limit) {
+            return i < limit;
+        }
+
+        static int step(int i, int limit) {
+            return i + 1;
+        }
+
+        static String initString() {
+            return "a";
+        }
+
+        static boolean predString(String s) {
+            return s.length() != 1;
+        }
+
+        static String stepString(String s) {
+            return s + "a";
+        }
+
+        static List<String> zipInitZip(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) {
+            zip.add(a.next());
+            zip.add(b.next());
+            return zip;
+        }
+
+        static final Class<While> WHILE = While.class;
+
+        static final MethodType MT_zero = methodType(int.class, int.class);
+        static final MethodType MT_pred = methodType(boolean.class, int.class, int.class);
+        static final MethodType MT_fn = methodType(int.class, int.class, int.class);
+        static final MethodType MT_initString = methodType(String.class);
+        static final MethodType MT_predString = methodType(boolean.class, String.class);
+        static final MethodType MT_stepString = methodType(String.class, String.class);
+        static final MethodType MT_zipInitZip = methodType(List.class, Iterator.class, Iterator.class);
+        static final MethodType MT_zipPred = methodType(boolean.class, List.class, Iterator.class, Iterator.class);
+        static final MethodType MT_zipStep = methodType(List.class, List.class, Iterator.class, Iterator.class);
+
+        static final MethodHandle MH_zero;
+        static final MethodHandle MH_pred;
+        static final MethodHandle MH_step;
+        static final MethodHandle MH_initString;
+        static final MethodHandle MH_predString;
+        static final MethodHandle MH_stepString;
+        static final MethodHandle MH_zipInitZip;
+        static final MethodHandle MH_zipPred;
+        static final MethodHandle MH_zipStep;
+
+        static final MethodType MT_while = methodType(int.class, int.class);
+        static final MethodType MT_string = methodType(String.class);
+        static final MethodType MT_zip = methodType(List.class, Iterator.class, Iterator.class);
+
+        static {
+            try {
+                MH_zero = LOOKUP.findStatic(WHILE, "zero", MT_zero);
+                MH_pred = LOOKUP.findStatic(WHILE, "pred", MT_pred);
+                MH_step = LOOKUP.findStatic(WHILE, "step", MT_fn);
+                MH_initString = LOOKUP.findStatic(WHILE, "initString", MT_initString);
+                MH_predString = LOOKUP.findStatic(WHILE, "predString", MT_predString);
+                MH_stepString = LOOKUP.findStatic(WHILE, "stepString", MT_stepString);
+                MH_zipInitZip = LOOKUP.findStatic(WHILE, "zipInitZip", MT_zipInitZip);
+                MH_zipPred = LOOKUP.findStatic(WHILE, "zipPred", MT_zipPred);
+                MH_zipStep = LOOKUP.findStatic(WHILE, "zipStep", MT_zipStep);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class Counted {
+
+        static String start(String arg) {
+            return arg;
+        }
+
+        static String step(int counter, String v, String arg) {
+            return "na " + v;
+        }
+
+        static void stepUpdateArray(int counter, int[] a) {
+            ++a[0];
+        }
+
+        static void printHello(int counter) {
+            System.out.print("hello");
+        }
+
+        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_stepUpdateArray = methodType(void.class, int.class, int[].class);
+        static final MethodType MT_printHello = methodType(void.class, int.class);
+
+        static final MethodHandle MH_13;
+        static final MethodHandle MH_m5;
+        static final MethodHandle MH_8;
+        static final MethodHandle MH_start;
+        static final MethodHandle MH_step;
+        static final MethodHandle MH_stepUpdateArray;
+        static final MethodHandle MH_printHello;
+
+        static final MethodType MT_counted = methodType(String.class, String.class);
+        static final MethodType MT_arrayCounted = methodType(void.class, int[].class);
+        static final MethodType MT_countedPrinting = methodType(void.class);
+
+        static {
+            try {
+                MH_13 = MethodHandles.constant(int.class, 13);
+                MH_m5 = MethodHandles.constant(int.class, -5);
+                MH_8 = MethodHandles.constant(int.class, 8);
+                MH_start = LOOKUP.findStatic(COUNTED, "start", MT_start);
+                MH_step = LOOKUP.findStatic(COUNTED, "step", MT_step);
+                MH_stepUpdateArray = LOOKUP.findStatic(COUNTED, "stepUpdateArray", MT_stepUpdateArray);
+                MH_printHello = LOOKUP.findStatic(COUNTED, "printHello", MT_printHello);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class Iterate {
+
+        static Iterator<Integer> sumIterator(Integer[] a) {
+            return Arrays.asList(a).iterator();
+        }
+
+        static int sumInit(Integer[] a) {
+            return 0;
+        }
+
+        static int sumStep(int s, int e, Integer[] a) {
+            return s + e;
+        }
+
+        static List<String> reverseInit(List<String> l) {
+            return new ArrayList<>();
+        }
+
+        static List<String> reverseStep(String e, List<String> r, List<String> l) {
+            r.add(0, e);
+            return r;
+        }
+
+        static int lengthInit(List<Double> l) {
+            return 0;
+        }
+
+        static int lengthStep(Object o, int len, List<Double> l) {
+            return len + 1;
+        }
+
+        static List<String> mapInit(List<String> l) {
+            return new ArrayList<>();
+        }
+
+        static List<String> mapStep(String e, List<String> r, List<String> l) {
+            r.add(e.toUpperCase());
+            return r;
+        }
+
+        static void printStep(String s, List<String> l) {
+            System.out.print(s);
+        }
+
+        static final Class<Iterate> ITERATE = Iterate.class;
+
+        static final MethodType MT_sumIterator = methodType(Iterator.class, Integer[].class);
+
+        static final MethodType MT_sumInit = methodType(int.class, Integer[].class);
+        static final MethodType MT_reverseInit = methodType(List.class, List.class);
+        static final MethodType MT_lenghInit = methodType(int.class, List.class);
+        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_printStep = methodType(void.class, String.class, List.class);
+
+        static final MethodHandle MH_sumIterator;
+        static final MethodHandle MH_sumInit;
+        static final MethodHandle MH_sumStep;
+        static final MethodHandle MH_printStep;
+
+        static final MethodHandle MH_reverseInit;
+        static final MethodHandle MH_reverseStep;
+
+        static final MethodHandle MH_lengthInit;
+        static final MethodHandle MH_lengthStep;
+
+        static final MethodHandle MH_mapInit;
+        static final MethodHandle MH_mapStep;
+
+        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);
+        static final MethodType MT_map = methodType(List.class, List.class);
+        static final MethodType MT_print = methodType(void.class, List.class);
+
+        static {
+            try {
+                MH_sumIterator = LOOKUP.findStatic(ITERATE, "sumIterator", MT_sumIterator);
+                MH_sumInit = LOOKUP.findStatic(ITERATE, "sumInit", MT_sumInit);
+                MH_sumStep = LOOKUP.findStatic(ITERATE, "sumStep", MT_sumStep);
+                MH_reverseInit = LOOKUP.findStatic(ITERATE, "reverseInit", MT_reverseInit);
+                MH_reverseStep = LOOKUP.findStatic(ITERATE, "reverseStep", MT_reverseStep);
+                MH_lengthInit = LOOKUP.findStatic(ITERATE, "lengthInit", MT_lenghInit);
+                MH_lengthStep = LOOKUP.findStatic(ITERATE, "lengthStep", MT_lengthStep);
+                MH_mapInit = LOOKUP.findStatic(ITERATE, "mapInit", MT_mapInit);
+                MH_mapStep = LOOKUP.findStatic(ITERATE, "mapStep", MT_mapStep);
+                MH_printStep = LOOKUP.findStatic(ITERATE, "printStep", MT_printStep);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class TryFinally {
+
+        static String greet(String whom) {
+            return "Hello, " + whom;
+        }
+
+        static String exclaim(Throwable t, String r, String whom) {
+            return r + "!";
+        }
+
+        static void print(String what) {
+            System.out.print("Hello, " + what);
+        }
+
+        static void printMore(Throwable t, String what) {
+            System.out.println("!");
+        }
+
+        static String greetMore(String first, String second) {
+            return "Hello, " + first + " and " + second;
+        }
+
+        static String exclaimMore(Throwable t, String r, String first) {
+            return r + " (but " + first + " first)!";
+        }
+
+        static final Class<TryFinally> TRY_FINALLY = TryFinally.class;
+
+        static final MethodType MT_greet = methodType(String.class, String.class);
+        static final MethodType MT_exclaim = methodType(String.class, Throwable.class, String.class, String.class);
+        static final MethodType MT_print = methodType(void.class, String.class);
+        static final MethodType MT_printMore = methodType(void.class, Throwable.class, String.class);
+        static final MethodType MT_greetMore = methodType(String.class, String.class, String.class);
+        static final MethodType MT_exclaimMore = methodType(String.class, Throwable.class, String.class, String.class);
+
+        static final MethodHandle MH_greet;
+        static final MethodHandle MH_exclaim;
+        static final MethodHandle MH_print;
+        static final MethodHandle MH_printMore;
+        static final MethodHandle MH_greetMore;
+        static final MethodHandle MH_exclaimMore;
+
+        static final MethodType MT_hello = methodType(String.class, String.class);
+        static final MethodType MT_printHello = methodType(void.class, String.class);
+        static final MethodType MT_moreHello = methodType(String.class, String.class, String.class);
+
+        static {
+            try {
+                MH_greet = LOOKUP.findStatic(TRY_FINALLY, "greet", MT_greet);
+                MH_exclaim = LOOKUP.findStatic(TRY_FINALLY, "exclaim", MT_exclaim);
+                MH_print = LOOKUP.findStatic(TRY_FINALLY, "print", MT_print);
+                MH_printMore = LOOKUP.findStatic(TRY_FINALLY, "printMore", MT_printMore);
+                MH_greetMore = LOOKUP.findStatic(TRY_FINALLY, "greetMore", MT_greetMore);
+                MH_exclaimMore = LOOKUP.findStatic(TRY_FINALLY, "exclaimMore", MT_exclaimMore);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class Fold {
+
+        static int adder(int a, int b, int c) {
+            return a + b + c;
+        }
+
+        static int adder1(int a, int b) {
+            return a + b;
+        }
+
+        static int multer(int x, int q, int r, int s) {
+            return x * q * r * s;
+        }
+
+        static int str(boolean b1, String s, boolean b2, int x) {
+            return b1 && s.equals(String.valueOf(b2)) ? x : -x;
+        }
+
+        static boolean comb(String s, boolean b2) {
+            return !s.equals(b2);
+        }
+
+        static String comb2(boolean b2, int x) {
+            int ib = b2 ? 1 : 0;
+            return ib == x ? "true" : "false";
+        }
+
+        static final Class<Fold> FOLD = Fold.class;
+
+        static final MethodType MT_adder = methodType(int.class, int.class, int.class, int.class);
+        static final MethodType MT_adder1 = methodType(int.class, int.class, int.class);
+        static final MethodType MT_multer = methodType(int.class, int.class, int.class, int.class, int.class);
+        static final MethodType MT_str = methodType(int.class, boolean.class, String.class, boolean.class, int.class);
+        static final MethodType MT_comb = methodType(boolean.class, String.class, boolean.class);
+        static final MethodType MT_comb2 = methodType(String.class, boolean.class, int.class);
+
+        static final MethodHandle MH_adder;
+        static final MethodHandle MH_adder1;
+        static final MethodHandle MH_multer;
+        static final MethodHandle MH_str;
+        static final MethodHandle MH_comb;
+        static final MethodHandle MH_comb2;
+
+        static final MethodType MT_folded1 = methodType(int.class, int.class, int.class, int.class);
+        static final MethodType MT_folded2 = methodType(int.class, String.class, boolean.class, int.class);
+        static final MethodType MT_folded3 = methodType(int.class, boolean.class, boolean.class, int.class);
+
+        static {
+            try {
+                MH_adder = LOOKUP.findStatic(FOLD, "adder", MT_adder);
+                MH_adder1 = LOOKUP.findStatic(FOLD, "adder1", MT_adder1);
+                MH_multer = LOOKUP.findStatic(FOLD, "multer", MT_multer);
+                MH_str = LOOKUP.findStatic(FOLD, "str", MT_str);
+                MH_comb = LOOKUP.findStatic(FOLD, "comb", MT_comb);
+                MH_comb2 = LOOKUP.findStatic(FOLD, "comb2", MT_comb2);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+    }
+
+    static class SpreadCollect {
+
+        static String forSpreading(String s1, int i1, int i2, int i3, String s2) {
+            return s1 + i1 + i2 + i3 + s2;
+        }
+
+        static String forCollecting(String s1, int[] is, String s2) {
+            StringBuilder sb = new StringBuilder(s1);
+            for (int i : is) {
+                sb.append(i);
+            }
+            return sb.append(s2).toString();
+        }
+
+        static String forCollectingLeading(int[] is, String s) {
+            return forCollecting("", is, s);
+        }
+
+        static final Class<SpreadCollect> SPREAD_COLLECT = SpreadCollect.class;
+
+        static final MethodType MT_forSpreading = methodType(String.class, String.class, int.class, int.class, int.class, String.class);
+        static final MethodType MT_forCollecting = methodType(String.class, String.class, int[].class, String.class);
+        static final MethodType MT_forCollectingLeading = methodType(String.class, int[].class, String.class);
+
+        static final MethodHandle MH_forSpreading;
+        static final MethodHandle MH_forCollecting;
+        static final MethodHandle MH_forCollectingLeading;
+
+        static final MethodType MT_spreader = methodType(String.class, String.class, int[].class, String.class);
+        static final MethodType MT_collector0 = methodType(String.class, String.class, String.class);
+        static final MethodType MT_collector1 = methodType(String.class, String.class, int.class, String.class);
+        static final MethodType MT_collector2 = methodType(String.class, String.class, int.class, int.class, String.class);
+        static final MethodType MT_collector3 = methodType(String.class, String.class, int.class, int.class, int.class, String.class);
+        static final MethodType MT_collectorLeading1 = methodType(String.class, int.class, String.class);
+        static final MethodType MT_collectorLeading2 = methodType(String.class, int.class, int.class, String.class);
+        static final MethodType MT_collectorLeading3 = methodType(String.class, int.class, int.class, int.class, String.class);
+
+        static final String NONE_ERROR = "zero array length in MethodHandle.asCollector";
+
+        static {
+            try {
+                MH_forSpreading = LOOKUP.findStatic(SPREAD_COLLECT, "forSpreading", MT_forSpreading);
+                MH_forCollecting = LOOKUP.findStatic(SPREAD_COLLECT, "forCollecting", MT_forCollecting);
+                MH_forCollectingLeading = LOOKUP.findStatic(SPREAD_COLLECT, "forCollectingLeading", MT_forCollectingLeading);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class FindSpecial {
+
+        interface I1 {
+            default String m() {
+                return "I1.m";
+            }
+        }
+
+        interface I2 {
+            default String m() {
+                return "I2.m";
+            }
+        }
+
+        interface I3 {
+            String q();
+        }
+
+        static class C implements I1, I2, I3 {
+            public String m() {
+                return I1.super.m();
+            }
+            public String q() {
+                return "q";
+            }
+        }
+
+        static final String ABSTRACT_ERROR = "no such method: test.java.lang.invoke.T8139885$FindSpecial$I3.q()String/invokeSpecial";
+
+    }
+
+    //
+    // Auxiliary methods.
+    //
+
+    static MethodHandle[] mha(MethodHandle... mhs) {
+        return mhs;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/findclass.security.policy	Fri Nov 20 15:34:12 2015 +0100
@@ -0,0 +1,9 @@
+/*
+ * Security policy used by the FindClassSecurityManager test.
+ * Must allow file reads so that jtreg itself can run, and getting class loaders.
+ */
+
+grant {
+  permission java.io.FilePermission "*", "read";
+  permission java.lang.RuntimePermission "getClassLoader";
+};