8195717: test java/lang/invoke/MethodHandlesTest timed out running testAsCollector1
authorvtheeyarath
Thu, 03 May 2018 04:18:22 -0700
changeset 50037 92ec6aec6f06
parent 50036 e0dbf14885b8
child 50038 3db7884546a1
8195717: test java/lang/invoke/MethodHandlesTest timed out running testAsCollector1 Summary: refactor MethodHandlesTest Reviewed-by: psandoz
test/jdk/java/lang/invoke/MethodHandlesAsCollectorTest.java
test/jdk/java/lang/invoke/MethodHandlesCastFailureTest.java
test/jdk/java/lang/invoke/MethodHandlesGeneralTest.java
test/jdk/java/lang/invoke/MethodHandlesInsertArgumentsTest.java
test/jdk/java/lang/invoke/MethodHandlesInvokersTest.java
test/jdk/java/lang/invoke/MethodHandlesPermuteArgumentsTest.java
test/jdk/java/lang/invoke/MethodHandlesSpreadArgumentsTest.java
test/jdk/java/lang/invoke/MethodHandlesTest.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/MethodHandlesAsCollectorTest.java	Thu May 03 04:18:22 2018 -0700
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @library /lib/testlibrary /java/lang/invoke/common
+ * @compile MethodHandlesTest.java MethodHandlesAsCollectorTest.java remote/RemoteExample.java
+ * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions
+ *                                 -XX:-VerifyDependencies
+ *                                 -esa
+ *                                 test.java.lang.invoke.MethodHandlesAsCollectorTest
+ */
+
+package test.java.lang.invoke;
+
+import org.junit.*;
+import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class MethodHandlesAsCollectorTest extends MethodHandlesTest {
+
+    @Test // SLOW
+    public void testAsCollector() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testAsCollector0);
+        CodeCacheOverflowProcessor.runMHTest(this::testAsCollector1);
+    }
+
+    public void testAsCollector0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("asCollector");
+        for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
+            if (verbosity >= 3)
+                System.out.println("asCollector "+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;
+                    testAsCollector(argType, pos, nargs);
+                }
+            }
+        }
+    }
+
+    public void testAsCollector(Class<?> argType, int pos, 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:
+        Object[] collectedArgs = Arrays.copyOfRange(args, 0, pos+1);
+        collectedArgs[pos] = Arrays.copyOfRange(args, pos, args.length);
+        // here is the MH which will witness the collected argument tail:
+        MethodHandle target = varargsArray(pos+1);
+        target = changeArgTypes(target, 0, pos, argType);
+        target = changeArgTypes(target, pos, pos+1, Object[].class);
+        if (verbosity >= 3)
+            System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]");
+        MethodHandle result = target.asCollector(Object[].class, nargs-pos).asType(newType);
+        Object[] returnValue = (Object[]) result.invokeWithArguments(args);
+        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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/MethodHandlesCastFailureTest.java	Thu May 03 04:18:22 2018 -0700
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @library /lib/testlibrary /java/lang/invoke/common
+ * @compile MethodHandlesTest.java MethodHandlesCastFailureTest.java remote/RemoteExample.java
+ * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions
+ *                                 -XX:-VerifyDependencies
+ *                                 -esa
+ *                                 test.java.lang.invoke.MethodHandlesCastFailureTest
+ */
+
+package test.java.lang.invoke;
+
+import org.junit.*;
+import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import static org.junit.Assert.*;
+
+public class MethodHandlesCastFailureTest extends MethodHandlesTest {
+
+    @Test  // SLOW
+    public void testCastFailure() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testCastFailure0);
+    }
+
+    public void testCastFailure0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("testCastFailure");
+        testCastFailure("cast/argument", 11000);
+        if (CAN_TEST_LIGHTLY)  return;
+        testCastFailure("unbox/argument", 11000);
+        testCastFailure("cast/return", 11000);
+        testCastFailure("unbox/return", 11000);
+    }
+
+    static class Surprise {
+        public MethodHandle asMethodHandle() {
+            return VALUE.bindTo(this);
+        }
+        Object value(Object x) {
+            trace("value", x);
+            if (boo != null)  return boo;
+            return x;
+        }
+        Object boo;
+        void boo(Object x) { boo = x; }
+
+        static void trace(String x, Object y) {
+            if (verbosity > 8) System.out.println(x+"="+y);
+        }
+        static Object  refIdentity(Object x)  { trace("ref.x", x); return x; }
+        static Integer boxIdentity(Integer x) { trace("box.x", x); return x; }
+        static int     intIdentity(int x)     { trace("int.x", x); return x; }
+        static MethodHandle VALUE, REF_IDENTITY, BOX_IDENTITY, INT_IDENTITY;
+        static {
+            try {
+                VALUE = PRIVATE.findVirtual(
+                    Surprise.class, "value",
+                        MethodType.methodType(Object.class, Object.class));
+                REF_IDENTITY = PRIVATE.findStatic(
+                    Surprise.class, "refIdentity",
+                        MethodType.methodType(Object.class, Object.class));
+                BOX_IDENTITY = PRIVATE.findStatic(
+                    Surprise.class, "boxIdentity",
+                        MethodType.methodType(Integer.class, Integer.class));
+                INT_IDENTITY = PRIVATE.findStatic(
+                    Surprise.class, "intIdentity",
+                        MethodType.methodType(int.class, int.class));
+            } catch (NoSuchMethodException | IllegalAccessException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+    @SuppressWarnings("ConvertToStringSwitch")
+    void testCastFailure(String mode, int okCount) throws Throwable {
+        countTest(false);
+        if (verbosity > 2)  System.out.println("mode="+mode);
+        Surprise boo = new Surprise();
+        MethodHandle identity = Surprise.REF_IDENTITY, surprise0 = boo.asMethodHandle(), surprise = surprise0;
+        if (mode.endsWith("/return")) {
+            if (mode.equals("unbox/return")) {
+                // fail on return to ((Integer)surprise).intValue
+                surprise = surprise.asType(MethodType.methodType(int.class, Object.class));
+                identity = identity.asType(MethodType.methodType(int.class, Object.class));
+            } else if (mode.equals("cast/return")) {
+                // fail on return to (Integer)surprise
+                surprise = surprise.asType(MethodType.methodType(Integer.class, Object.class));
+                identity = identity.asType(MethodType.methodType(Integer.class, Object.class));
+            }
+        } else if (mode.endsWith("/argument")) {
+            MethodHandle callee = null;
+            if (mode.equals("unbox/argument")) {
+                // fail on handing surprise to int argument
+                callee = Surprise.INT_IDENTITY;
+            } else if (mode.equals("cast/argument")) {
+                // fail on handing surprise to Integer argument
+                callee = Surprise.BOX_IDENTITY;
+            }
+            if (callee != null) {
+                callee = callee.asType(MethodType.genericMethodType(1));
+                surprise = MethodHandles.filterArguments(callee, 0, surprise);
+                identity = MethodHandles.filterArguments(callee, 0, identity);
+            }
+        }
+        assertNotSame(mode, surprise, surprise0);
+        identity = identity.asType(MethodType.genericMethodType(1));
+        surprise = surprise.asType(MethodType.genericMethodType(1));
+        Object x = 42;
+        for (int i = 0; i < okCount; i++) {
+            Object y = identity.invokeExact(x);
+            assertEquals(x, y);
+            Object z = surprise.invokeExact(x);
+            assertEquals(x, z);
+        }
+        boo.boo("Boo!");
+        Object y = identity.invokeExact(x);
+        assertEquals(x, y);
+        try {
+            Object z = surprise.invokeExact(x);
+            System.out.println("Failed to throw; got z="+z);
+            assertTrue(false);
+        } catch (ClassCastException ex) {
+            if (verbosity > 2)
+                System.out.println("caught "+ex);
+            if (verbosity > 3)
+                ex.printStackTrace(System.out);
+            assertTrue(true);  // all is well
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/MethodHandlesGeneralTest.java	Thu May 03 04:18:22 2018 -0700
@@ -0,0 +1,2122 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @library /lib/testlibrary /java/lang/invoke/common
+ * @compile MethodHandlesTest.java MethodHandlesGeneralTest.java remote/RemoteExample.java
+ * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions
+ *                                 -XX:-VerifyDependencies
+ *                                 -esa
+ *                                 test.java.lang.invoke.MethodHandlesGeneralTest
+ */
+
+package test.java.lang.invoke;
+
+import org.junit.*;
+import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
+import test.java.lang.invoke.remote.RemoteExample;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandleProxies;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static java.lang.invoke.MethodType.methodType;
+import static org.junit.Assert.*;
+
+public class MethodHandlesGeneralTest extends MethodHandlesTest {
+
+    @Test
+    public void testFirst() throws Throwable {
+        verbosity += 9;
+        try {
+            // left blank for debugging
+        } finally { printCounts(); verbosity -= 9; }
+    }
+
+    @Test
+    public void testFindStatic() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFindStatic0);
+    }
+
+    public void testFindStatic0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("findStatic");
+        testFindStatic(PubExample.class, void.class, "s0");
+        testFindStatic(Example.class, void.class, "s0");
+        testFindStatic(Example.class, void.class, "pkg_s0");
+        testFindStatic(Example.class, void.class, "pri_s0");
+        testFindStatic(Example.class, void.class, "pro_s0");
+        testFindStatic(PubExample.class, void.class, "Pub/pro_s0");
+
+        testFindStatic(Example.class, Object.class, "s1", Object.class);
+        testFindStatic(Example.class, Object.class, "s2", int.class);
+        testFindStatic(Example.class, Object.class, "s3", long.class);
+        testFindStatic(Example.class, Object.class, "s4", int.class, int.class);
+        testFindStatic(Example.class, Object.class, "s5", long.class, int.class);
+        testFindStatic(Example.class, Object.class, "s6", int.class, long.class);
+        testFindStatic(Example.class, Object.class, "s7", float.class, double.class);
+
+        testFindStatic(false, PRIVATE, Example.class, void.class, "bogus");
+        testFindStatic(false, PRIVATE, Example.class, void.class, "<init>", int.class);
+        testFindStatic(false, PRIVATE, Example.class, void.class, "<init>", Void.class);
+        testFindStatic(false, PRIVATE, Example.class, void.class, "v0");
+    }
+
+    void testFindStatic(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        for (Object[] ac : accessCases(defc, name)) {
+            testFindStatic((Boolean)ac[0], (Lookup)ac[1], defc, ret, name, params);
+        }
+    }
+
+    void testFindStatic(Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        testFindStatic(true, lookup, defc, ret, name, params);
+    }
+
+    void testFindStatic(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        countTest(positive);
+        String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
+        MethodType type = MethodType.methodType(ret, params);
+        MethodHandle target = null;
+        Exception noAccess = null;
+        try {
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
+            target = maybeMoveIn(lookup, defc).findStatic(defc, methodName, type);
+        } catch (ReflectiveOperationException ex) {
+            noAccess = ex;
+            assertExceptionClass(
+                (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
+                ?   NoSuchMethodException.class
+                :   IllegalAccessException.class,
+                noAccess);
+            if (verbosity >= 5)  ex.printStackTrace(System.out);
+        }
+        if (verbosity >= 3)
+            System.out.println("findStatic "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
+                    +(noAccess == null ? "" : " !! "+noAccess));
+        if (positive && noAccess != null)  throw noAccess;
+        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
+        if (!positive)  return; // negative test failed as expected
+        assertEquals(type, target.type());
+        assertNameStringContains(target, methodName);
+        Object[] args = randomArgs(params);
+        printCalled(target, name, args);
+        target.invokeWithArguments(args);
+        assertCalled(name, args);
+        if (verbosity >= 1)
+            System.out.print(':');
+    }
+
+    @Test
+    public void testFindVirtual() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFindVirtual0);
+    }
+
+    public void testFindVirtual0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("findVirtual");
+        testFindVirtual(Example.class, void.class, "v0");
+        testFindVirtual(Example.class, void.class, "pkg_v0");
+        testFindVirtual(Example.class, void.class, "pri_v0");
+        testFindVirtual(Example.class, Object.class, "v1", Object.class);
+        testFindVirtual(Example.class, Object.class, "v2", Object.class, Object.class);
+        testFindVirtual(Example.class, Object.class, "v2", Object.class, int.class);
+        testFindVirtual(Example.class, Object.class, "v2", int.class, Object.class);
+        testFindVirtual(Example.class, Object.class, "v2", int.class, int.class);
+        testFindVirtual(Example.class, void.class, "pro_v0");
+        testFindVirtual(PubExample.class, void.class, "Pub/pro_v0");
+
+        testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "bogus");
+        testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "<init>", int.class);
+        testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "<init>", Void.class);
+        testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "s0");
+
+        // test dispatch
+        testFindVirtual(SubExample.class, SubExample.class, void.class, "Sub/v0");
+        testFindVirtual(SubExample.class, Example.class, void.class, "Sub/v0");
+        testFindVirtual(SubExample.class, IntExample.class, void.class, "Sub/v0");
+        testFindVirtual(SubExample.class, SubExample.class, void.class, "Sub/pkg_v0");
+        testFindVirtual(SubExample.class, Example.class, void.class, "Sub/pkg_v0");
+        testFindVirtual(Example.class, IntExample.class, void.class, "v0");
+        testFindVirtual(IntExample.Impl.class, IntExample.class, void.class, "Int/v0");
+    }
+
+    @Test
+    public void testFindVirtualClone() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFindVirtualClone0);
+    }
+
+    public void testFindVirtualClone0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        // test some ad hoc system methods
+        testFindVirtual(false, PUBLIC, Object.class, Object.class, "clone");
+
+        // ##### FIXME - disable tests for clone until we figure out how they should work with modules
+
+        /*
+        testFindVirtual(true, PUBLIC, Object[].class, Object.class, "clone");
+        testFindVirtual(true, PUBLIC, int[].class, Object.class, "clone");
+        for (Class<?> cls : new Class<?>[]{ boolean[].class, long[].class, float[].class, char[].class })
+            testFindVirtual(true, PUBLIC, cls, Object.class, "clone");
+         */
+    }
+
+    void testFindVirtual(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        Class<?> rcvc = defc;
+        testFindVirtual(rcvc, defc, ret, name, params);
+    }
+
+    void testFindVirtual(Class<?> rcvc, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        for (Object[] ac : accessCases(defc, name)) {
+            testFindVirtual((Boolean)ac[0], (Lookup)ac[1], rcvc, defc, ret, name, params);
+        }
+    }
+
+    void testFindVirtual(Lookup lookup, Class<?> rcvc, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        testFindVirtual(true, lookup, rcvc, defc, ret, name, params);
+    }
+
+    void testFindVirtual(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        testFindVirtual(positive, lookup, defc, defc, ret, name, params);
+    }
+
+    void testFindVirtual(boolean positive, Lookup lookup, Class<?> rcvc, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        countTest(positive);
+        String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
+        MethodType type = MethodType.methodType(ret, params);
+        MethodHandle target = null;
+        Exception noAccess = null;
+        try {
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
+            target = maybeMoveIn(lookup, defc).findVirtual(defc, methodName, type);
+        } catch (ReflectiveOperationException ex) {
+            noAccess = ex;
+            assertExceptionClass(
+                (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
+                ? NoSuchMethodException.class
+                : IllegalAccessException.class,
+                noAccess);
+            if (verbosity >= 5)  ex.printStackTrace(System.out);
+        }
+        if (verbosity >= 3)
+            System.out.println("findVirtual "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
+                    +(noAccess == null ? "" : " !! "+noAccess));
+        if (positive && noAccess != null)  throw noAccess;
+        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
+        if (!positive)  return; // negative test failed as expected
+        Class<?> selfc = defc;
+        // predict receiver type narrowing:
+        if (lookup == SUBCLASS &&
+                name.contains("pro_") &&
+                selfc.isAssignableFrom(lookup.lookupClass())) {
+            selfc = lookup.lookupClass();
+            if (name.startsWith("Pub/"))  name = "Rem/"+name.substring(4);
+        }
+        Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)selfc), params);
+        MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
+        assertEquals(typeWithSelf, target.type());
+        assertNameStringContains(target, methodName);
+        Object[] argsWithSelf = randomArgs(paramsWithSelf);
+        if (selfc.isAssignableFrom(rcvc) && rcvc != selfc)  argsWithSelf[0] = randomArg(rcvc);
+        printCalled(target, name, argsWithSelf);
+        Object res = target.invokeWithArguments(argsWithSelf);
+        if (Example.class.isAssignableFrom(defc) || IntExample.class.isAssignableFrom(defc)) {
+            assertCalled(name, argsWithSelf);
+        } else if (name.equals("clone")) {
+            // Ad hoc method call outside Example.  For Object[].clone.
+            printCalled(target, name, argsWithSelf);
+            assertEquals(MethodType.methodType(Object.class, rcvc), target.type());
+            Object orig = argsWithSelf[0];
+            assertEquals(orig.getClass(), res.getClass());
+            if (res instanceof Object[])
+                assertArrayEquals((Object[])res, (Object[])argsWithSelf[0]);
+            assert(Arrays.deepEquals(new Object[]{res}, new Object[]{argsWithSelf[0]}));
+        } else {
+            assert(false) : Arrays.asList(positive, lookup, rcvc, defc, ret, name, deepToString(params));
+        }
+        if (verbosity >= 1)
+            System.out.print(':');
+    }
+
+    @Test
+    public void testFindSpecial() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFindSpecial0);
+    }
+
+    public void testFindSpecial0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("findSpecial");
+        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");
+            testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus");
+            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, 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);
+            testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params);
+            testFindSpecial(true, SUBCLASS, specialCaller, defc, ret, name, params);
+            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 || 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 {
+        countTest(positive);
+        String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
+        MethodType type = MethodType.methodType(ret, params);
+        Lookup specialLookup = maybeMoveIn(lookup, specialCaller);
+        boolean specialAccessOK = (specialLookup.lookupClass() == specialCaller &&
+                                  (specialLookup.lookupModes() & Lookup.PRIVATE) != 0);
+        MethodHandle target = null;
+        Exception noAccess = null;
+        try {
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
+            if (verbosity >= 5)  System.out.println("  lookup => "+specialLookup);
+            target = specialLookup.findSpecial(defc, methodName, type, specialCaller);
+        } catch (ReflectiveOperationException ex) {
+            noAccess = ex;
+            assertExceptionClass(
+                (!specialAccessOK)  // this check should happen first
+                ? IllegalAccessException.class
+                : (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
+                ? NoSuchMethodException.class
+                : IllegalAccessException.class,
+                noAccess);
+            if (verbosity >= 5)  ex.printStackTrace(System.out);
+        }
+        if (verbosity >= 3)
+            System.out.println("findSpecial from "+specialCaller.getName()+" to "+defc.getName()+"."+name+"/"+type+" => "+target
+                               +(target == null ? "" : target.type())
+                               +(noAccess == null ? "" : " !! "+noAccess));
+        if (positive && noAccess != null)  throw noAccess;
+        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
+        if (!positive)  return; // negative test failed as expected
+        assertEquals(specialCaller, target.type().parameterType(0));
+        assertEquals(type, target.type().dropParameterTypes(0,1));
+        Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)specialCaller), params);
+        MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
+        assertNameStringContains(target, methodName);
+        Object[] args = randomArgs(paramsWithSelf);
+        printCalled(target, name, args);
+        target.invokeWithArguments(args);
+        assertCalled(name, args);
+    }
+
+    @Test
+    public void testFindConstructor() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFindConstructor0);
+    }
+
+    public void testFindConstructor0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("findConstructor");
+        testFindConstructor(true, EXAMPLE, Example.class);
+        testFindConstructor(true, EXAMPLE, Example.class, int.class);
+        testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class);
+        testFindConstructor(true, EXAMPLE, Example.class, int.class, long.class);
+        testFindConstructor(true, EXAMPLE, Example.class, int.class, float.class);
+        testFindConstructor(true, EXAMPLE, Example.class, int.class, double.class);
+        testFindConstructor(true, EXAMPLE, Example.class, String.class);
+        testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class, int.class);
+        testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class, int.class, int.class);
+    }
+
+    void testFindConstructor(boolean positive, Lookup lookup,
+                             Class<?> defc, Class<?>... params) throws Throwable {
+        countTest(positive);
+        MethodType type = MethodType.methodType(void.class, params);
+        MethodHandle target = null;
+        Exception noAccess = null;
+        try {
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" <init>"+type);
+            target = lookup.findConstructor(defc, type);
+        } catch (ReflectiveOperationException ex) {
+            noAccess = ex;
+            assertTrue(noAccess.getClass().getName(), noAccess instanceof IllegalAccessException);
+        }
+        if (verbosity >= 3)
+            System.out.println("findConstructor "+defc.getName()+".<init>/"+type+" => "+target
+                               +(target == null ? "" : target.type())
+                               +(noAccess == null ? "" : " !! "+noAccess));
+        if (positive && noAccess != null)  throw noAccess;
+        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
+        if (!positive)  return; // negative test failed as expected
+        assertEquals(type.changeReturnType(defc), target.type());
+        Object[] args = randomArgs(params);
+        printCalled(target, defc.getSimpleName(), args);
+        Object obj = target.invokeWithArguments(args);
+        if (!(defc == Example.class && params.length < 2))
+            assertCalled(defc.getSimpleName()+".<init>", args);
+        assertTrue("instance of "+defc.getName(), defc.isInstance(obj));
+    }
+
+    @Test
+    public void testBind() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testBind0);
+    }
+
+    public void testBind0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("bind");
+        testBind(Example.class, void.class, "v0");
+        testBind(Example.class, void.class, "pkg_v0");
+        testBind(Example.class, void.class, "pri_v0");
+        testBind(Example.class, Object.class, "v1", Object.class);
+        testBind(Example.class, Object.class, "v2", Object.class, Object.class);
+        testBind(Example.class, Object.class, "v2", Object.class, int.class);
+        testBind(Example.class, Object.class, "v2", int.class, Object.class);
+        testBind(Example.class, Object.class, "v2", int.class, int.class);
+        testBind(false, PRIVATE, Example.class, void.class, "bogus");
+        testBind(false, PRIVATE, Example.class, void.class, "<init>", int.class);
+        testBind(false, PRIVATE, Example.class, void.class, "<init>", Void.class);
+        testBind(SubExample.class, void.class, "Sub/v0");
+        testBind(SubExample.class, void.class, "Sub/pkg_v0");
+        testBind(IntExample.Impl.class, void.class, "Int/v0");
+    }
+
+    void testBind(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        for (Object[] ac : accessCases(defc, name)) {
+            testBind((Boolean)ac[0], (Lookup)ac[1], defc, ret, name, params);
+        }
+    }
+
+    void testBind(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        countTest(positive);
+        String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
+        MethodType type = MethodType.methodType(ret, params);
+        Object receiver = randomArg(defc);
+        MethodHandle target = null;
+        Exception noAccess = null;
+        try {
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
+            target = maybeMoveIn(lookup, defc).bind(receiver, methodName, type);
+        } catch (ReflectiveOperationException ex) {
+            noAccess = ex;
+            assertExceptionClass(
+                (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
+                ? NoSuchMethodException.class
+                : IllegalAccessException.class,
+                noAccess);
+            if (verbosity >= 5)  ex.printStackTrace(System.out);
+        }
+        if (verbosity >= 3)
+            System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target
+                    +(noAccess == null ? "" : " !! "+noAccess));
+        if (positive && noAccess != null)  throw noAccess;
+        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
+        if (!positive)  return; // negative test failed as expected
+        assertEquals(type, target.type());
+        Object[] args = randomArgs(params);
+        printCalled(target, name, args);
+        target.invokeWithArguments(args);
+        Object[] argsWithReceiver = cat(array(Object[].class, receiver), args);
+        assertCalled(name, argsWithReceiver);
+        if (verbosity >= 1)
+            System.out.print(':');
+    }
+
+    @Test
+    public void testUnreflect() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testUnreflect0);
+    }
+
+    public void testUnreflect0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("unreflect");
+        testUnreflect(Example.class, true, void.class, "s0");
+        testUnreflect(Example.class, true, void.class, "pro_s0");
+        testUnreflect(Example.class, true, void.class, "pkg_s0");
+        testUnreflect(Example.class, true, void.class, "pri_s0");
+
+        testUnreflect(Example.class, true, Object.class, "s1", Object.class);
+        testUnreflect(Example.class, true, Object.class, "s2", int.class);
+        testUnreflect(Example.class, true, Object.class, "s3", long.class);
+        testUnreflect(Example.class, true, Object.class, "s4", int.class, int.class);
+        testUnreflect(Example.class, true, Object.class, "s5", long.class, int.class);
+        testUnreflect(Example.class, true, Object.class, "s6", int.class, long.class);
+
+        testUnreflect(Example.class, false, void.class, "v0");
+        testUnreflect(Example.class, false, void.class, "pkg_v0");
+        testUnreflect(Example.class, false, void.class, "pri_v0");
+        testUnreflect(Example.class, false, Object.class, "v1", Object.class);
+        testUnreflect(Example.class, false, Object.class, "v2", Object.class, Object.class);
+        testUnreflect(Example.class, false, Object.class, "v2", Object.class, int.class);
+        testUnreflect(Example.class, false, Object.class, "v2", int.class, Object.class);
+        testUnreflect(Example.class, false, Object.class, "v2", int.class, int.class);
+
+        // Test a public final member in another package:
+        testUnreflect(RemoteExample.class, false, void.class, "Rem/fin_v0");
+    }
+
+    void testUnreflect(Class<?> defc, boolean isStatic, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        for (Object[] ac : accessCases(defc, name)) {
+            testUnreflectMaybeSpecial(null, (Boolean)ac[0], (Lookup)ac[1], defc, (isStatic ? null : defc), ret, name, params);
+        }
+    }
+
+    void testUnreflect(Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        for (Object[] ac : accessCases(defc, name)) {
+            testUnreflectMaybeSpecial(null, (Boolean)ac[0], (Lookup)ac[1], defc, rcvc, ret, name, params);
+        }
+    }
+
+    void testUnreflectMaybeSpecial(Class<?> specialCaller,
+                                   boolean positive, Lookup lookup,
+                                   Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        countTest(positive);
+        String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
+        MethodType type = MethodType.methodType(ret, params);
+        Lookup specialLookup = (specialCaller != null ? maybeMoveIn(lookup, specialCaller) : null);
+        boolean specialAccessOK = (specialCaller != null &&
+                                  specialLookup.lookupClass() == specialCaller &&
+                                  (specialLookup.lookupModes() & Lookup.PRIVATE) != 0);
+        Method rmethod = defc.getDeclaredMethod(methodName, params);
+        MethodHandle target = null;
+        Exception noAccess = null;
+        boolean isStatic = (rcvc == null);
+        boolean isSpecial = (specialCaller != null);
+        try {
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
+            if (isSpecial)
+                target = specialLookup.unreflectSpecial(rmethod, specialCaller);
+            else
+                target = maybeMoveIn(lookup, defc).unreflect(rmethod);
+        } catch (ReflectiveOperationException ex) {
+            noAccess = ex;
+            assertExceptionClass(
+                IllegalAccessException.class,  // NSME is impossible, since it was already reflected
+                noAccess);
+            if (verbosity >= 5)  ex.printStackTrace(System.out);
+        }
+        if (verbosity >= 3)
+            System.out.println("unreflect"+(isSpecial?"Special":"")+" "+defc.getName()+"."+name+"/"+type
+                               +(!isSpecial ? "" : " specialCaller="+specialCaller)
+                               +( isStatic  ? "" : " receiver="+rcvc)
+                               +" => "+target
+                               +(noAccess == null ? "" : " !! "+noAccess));
+        if (positive && noAccess != null)  throw noAccess;
+        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
+        if (!positive)  return; // negative test failed as expected
+        assertEquals(isStatic, Modifier.isStatic(rmethod.getModifiers()));
+        Class<?>[] paramsMaybeWithSelf = params;
+        if (!isStatic) {
+            paramsMaybeWithSelf = cat(array(Class[].class, (Class)rcvc), params);
+        }
+        MethodType typeMaybeWithSelf = MethodType.methodType(ret, paramsMaybeWithSelf);
+        if (isStatic) {
+            assertEquals(typeMaybeWithSelf, target.type());
+        } else {
+            if (isSpecial)
+                assertEquals(specialCaller, target.type().parameterType(0));
+            else
+                assertEquals(defc, target.type().parameterType(0));
+            assertEquals(typeMaybeWithSelf, target.type().changeParameterType(0, rcvc));
+        }
+        Object[] argsMaybeWithSelf = randomArgs(paramsMaybeWithSelf);
+        printCalled(target, name, argsMaybeWithSelf);
+        target.invokeWithArguments(argsMaybeWithSelf);
+        assertCalled(name, argsMaybeWithSelf);
+        if (verbosity >= 1)
+            System.out.print(':');
+    }
+
+    void testUnreflectSpecial(Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        for (Object[] ac : accessCases(defc, name, true)) {
+            Class<?> specialCaller = rcvc;
+            testUnreflectMaybeSpecial(specialCaller, (Boolean)ac[0], (Lookup)ac[1], defc, rcvc, ret, name, params);
+        }
+    }
+
+    @Test
+    public void testUnreflectSpecial() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testUnreflectSpecial0);
+    }
+
+    public void testUnreflectSpecial0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("unreflectSpecial");
+        testUnreflectSpecial(Example.class, Example.class, void.class, "v0");
+        testUnreflectSpecial(Example.class, SubExample.class, void.class, "v0");
+        testUnreflectSpecial(Example.class, Example.class, void.class, "pkg_v0");
+        testUnreflectSpecial(Example.class, SubExample.class, void.class, "pkg_v0");
+        testUnreflectSpecial(Example.class, Example.class, Object.class, "v2", int.class, int.class);
+        testUnreflectSpecial(Example.class, SubExample.class, Object.class, "v2", int.class, int.class);
+        testUnreflectMaybeSpecial(Example.class, false, PRIVATE, Example.class, Example.class, void.class, "s0");
+    }
+
+    @Test
+    public void testUnreflectGetter() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testUnreflectGetter0);
+    }
+
+    public void testUnreflectGetter0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("unreflectGetter");
+        testGetter(TEST_UNREFLECT);
+    }
+
+    @Test
+    public void testFindGetter() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFindGetter0);
+    }
+
+    public void testFindGetter0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("findGetter");
+        testGetter(TEST_FIND_FIELD);
+        testGetter(TEST_FIND_FIELD | TEST_BOUND);
+    }
+
+    @Test
+    public void testFindStaticGetter() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFindStaticGetter0);
+    }
+
+    public void testFindStaticGetter0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("findStaticGetter");
+        testGetter(TEST_FIND_STATIC);
+    }
+
+    public void testGetter(int testMode) throws Throwable {
+        Lookup lookup = PRIVATE;  // FIXME: test more lookups than this one
+        for (Object[] c : HasFields.CASES) {
+            boolean positive = (c[1] != Error.class);
+            testGetter(positive, lookup, c[0], c[1], testMode);
+            if (positive)
+                testGetter(positive, lookup, c[0], c[1], testMode | TEST_NPE);
+        }
+        testGetter(true, lookup,
+                   new Object[]{ true,  System.class, "out", java.io.PrintStream.class },
+                   System.out, testMode);
+        for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
+            testGetter(false, lookup,
+                       new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
+                       null, testMode);
+        }
+    }
+
+    public void testGetter(boolean positive, MethodHandles.Lookup lookup,
+                           Object fieldRef, Object value, int testMode) throws Throwable {
+        testAccessor(positive, lookup, fieldRef, value, testMode);
+    }
+
+    public void testAccessor(boolean positive0, MethodHandles.Lookup lookup,
+                             Object fieldRef, Object value, int testMode0) throws Throwable {
+        if (verbosity >= 4)
+            System.out.println("testAccessor"+Arrays.deepToString(new Object[]{positive0, lookup, fieldRef, value, testMode0}));
+        boolean isGetter = ((testMode0 & TEST_SETTER) == 0);
+        boolean doBound  = ((testMode0 & TEST_BOUND) != 0);
+        boolean testNPE  = ((testMode0 & TEST_NPE) != 0);
+        int testMode = testMode0 & ~(TEST_SETTER | TEST_BOUND | TEST_NPE);
+        boolean positive = positive0 && !testNPE;
+        boolean isStatic;
+        Class<?> fclass;
+        String   fname;
+        Class<?> ftype;
+        Field f = (fieldRef instanceof Field ? (Field)fieldRef : null);
+        if (f != null) {
+            isStatic = Modifier.isStatic(f.getModifiers());
+            fclass   = f.getDeclaringClass();
+            fname    = f.getName();
+            ftype    = f.getType();
+        } else {
+            Object[] scnt = (Object[]) fieldRef;
+            isStatic = (Boolean)  scnt[0];
+            fclass   = (Class<?>) scnt[1];
+            fname    = (String)   scnt[2];
+            ftype    = (Class<?>) scnt[3];
+            try {
+                f = fclass.getDeclaredField(fname);
+            } catch (ReflectiveOperationException ex) {
+                f = null;
+            }
+        }
+        if (!testModeMatches(testMode, isStatic))  return;
+        if (f == null && testMode == TEST_UNREFLECT)  return;
+        if (testNPE && isStatic)  return;
+        countTest(positive);
+        MethodType expType;
+        if (isGetter)
+            expType = MethodType.methodType(ftype, HasFields.class);
+        else
+            expType = MethodType.methodType(void.class, HasFields.class, ftype);
+        if (isStatic)  expType = expType.dropParameterTypes(0, 1);
+        Exception noAccess = null;
+        MethodHandle mh;
+        try {
+            switch (testMode0 & ~(TEST_BOUND | TEST_NPE)) {
+            case TEST_UNREFLECT:   mh = lookup.unreflectGetter(f);                      break;
+            case TEST_FIND_FIELD:  mh = lookup.findGetter(fclass, fname, ftype);        break;
+            case TEST_FIND_STATIC: mh = lookup.findStaticGetter(fclass, fname, ftype);  break;
+            case TEST_SETTER|
+                 TEST_UNREFLECT:   mh = lookup.unreflectSetter(f);                      break;
+            case TEST_SETTER|
+                 TEST_FIND_FIELD:  mh = lookup.findSetter(fclass, fname, ftype);        break;
+            case TEST_SETTER|
+                 TEST_FIND_STATIC: mh = lookup.findStaticSetter(fclass, fname, ftype);  break;
+            default:
+                throw new InternalError("testMode="+testMode);
+            }
+        } catch (ReflectiveOperationException ex) {
+            mh = null;
+            noAccess = ex;
+            assertExceptionClass(
+                (fname.contains("bogus"))
+                ?   NoSuchFieldException.class
+                :   IllegalAccessException.class,
+                noAccess);
+            if (verbosity >= 5)  ex.printStackTrace(System.out);
+        }
+        if (verbosity >= 3)
+            System.out.println("find"+(isStatic?"Static":"")+(isGetter?"Getter":"Setter")+" "+fclass.getName()+"."+fname+"/"+ftype
+                               +" => "+mh
+                               +(noAccess == null ? "" : " !! "+noAccess));
+        if (positive && !testNPE && noAccess != null)  throw new RuntimeException(noAccess);
+        assertEquals(positive0 ? "positive test" : "negative test erroneously passed", positive0, mh != null);
+        if (!positive && !testNPE)  return; // negative access test failed as expected
+        assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount());
+
+
+        assertSame(mh.type(), expType);
+        //assertNameStringContains(mh, fname);  // This does not hold anymore with LFs
+        HasFields fields = new HasFields();
+        HasFields fieldsForMH = fields;
+        if (testNPE)  fieldsForMH = null;  // perturb MH argument to elicit expected error
+        if (doBound)
+            mh = mh.bindTo(fieldsForMH);
+        Object sawValue;
+        Class<?> vtype = ftype;
+        if (ftype != int.class)  vtype = Object.class;
+        if (isGetter) {
+            mh = mh.asType(mh.type().generic()
+                           .changeReturnType(vtype));
+        } else {
+            int last = mh.type().parameterCount() - 1;
+            mh = mh.asType(mh.type().generic()
+                           .changeReturnType(void.class)
+                           .changeParameterType(last, vtype));
+        }
+        if (f != null && f.getDeclaringClass() == HasFields.class) {
+            assertEquals(f.get(fields), value);  // clean to start with
+        }
+        Throwable caughtEx = null;
+        if (isGetter) {
+            Object expValue = value;
+            for (int i = 0; i <= 1; i++) {
+                sawValue = null;  // make DA rules happy under try/catch
+                try {
+                    if (isStatic || doBound) {
+                        if (ftype == int.class)
+                            sawValue = (int) mh.invokeExact();  // do these exactly
+                        else
+                            sawValue = mh.invokeExact();
+                    } else {
+                        if (ftype == int.class)
+                            sawValue = (int) mh.invokeExact((Object) fieldsForMH);
+                        else
+                            sawValue = mh.invokeExact((Object) fieldsForMH);
+                    }
+                } catch (RuntimeException ex) {
+                    if (ex instanceof NullPointerException && testNPE) {
+                        caughtEx = ex;
+                        break;
+                    }
+                }
+                assertEquals(sawValue, expValue);
+                if (f != null && f.getDeclaringClass() == HasFields.class
+                    && !Modifier.isFinal(f.getModifiers())) {
+                    Object random = randomArg(ftype);
+                    f.set(fields, random);
+                    expValue = random;
+                } else {
+                    break;
+                }
+            }
+        } else {
+            for (int i = 0; i <= 1; i++) {
+                Object putValue = randomArg(ftype);
+                try {
+                    if (isStatic || doBound) {
+                        if (ftype == int.class)
+                            mh.invokeExact((int)putValue);  // do these exactly
+                        else
+                            mh.invokeExact(putValue);
+                    } else {
+                        if (ftype == int.class)
+                            mh.invokeExact((Object) fieldsForMH, (int)putValue);
+                        else
+                            mh.invokeExact((Object) fieldsForMH, putValue);
+                    }
+                } catch (RuntimeException ex) {
+                    if (ex instanceof NullPointerException && testNPE) {
+                        caughtEx = ex;
+                        break;
+                    }
+                }
+                if (f != null && f.getDeclaringClass() == HasFields.class) {
+                    assertEquals(f.get(fields), putValue);
+                }
+            }
+        }
+        if (f != null && f.getDeclaringClass() == HasFields.class) {
+            f.set(fields, value);  // put it back
+        }
+        if (testNPE) {
+            if (caughtEx == null || !(caughtEx instanceof NullPointerException))
+                throw new RuntimeException("failed to catch NPE exception"+(caughtEx == null ? " (caughtEx=null)" : ""), caughtEx);
+            caughtEx = null;  // nullify expected exception
+        }
+        if (caughtEx != null) {
+            throw new RuntimeException("unexpected exception", caughtEx);
+        }
+    }
+
+    @Test
+    public void testUnreflectSetter() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testUnreflectSetter0);
+    }
+
+    public void testUnreflectSetter0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("unreflectSetter");
+        testSetter(TEST_UNREFLECT);
+    }
+
+    @Test
+    public void testFindSetter() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFindSetter0);
+    }
+
+    public void testFindSetter0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("findSetter");
+        testSetter(TEST_FIND_FIELD);
+        testSetter(TEST_FIND_FIELD | TEST_BOUND);
+    }
+
+    @Test
+    public void testFindStaticSetter() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFindStaticSetter0);
+    }
+
+    public void testFindStaticSetter0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("findStaticSetter");
+        testSetter(TEST_FIND_STATIC);
+    }
+
+    public void testSetter(int testMode) throws Throwable {
+        Lookup lookup = PRIVATE;  // FIXME: test more lookups than this one
+        startTest("unreflectSetter");
+        for (Object[] c : HasFields.CASES) {
+            boolean positive = (c[1] != Error.class);
+            testSetter(positive, lookup, c[0], c[1], testMode);
+            if (positive)
+                testSetter(positive, lookup, c[0], c[1], testMode | TEST_NPE);
+        }
+        for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
+            testSetter(false, lookup,
+                       new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
+                       null, testMode);
+        }
+    }
+
+    public void testSetter(boolean positive, MethodHandles.Lookup lookup,
+                           Object fieldRef, Object value, int testMode) throws Throwable {
+        testAccessor(positive, lookup, fieldRef, value, testMode | TEST_SETTER);
+    }
+
+    @Test
+    public void testArrayElementGetter() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testArrayElementGetter0);
+    }
+
+    public void testArrayElementGetter0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("arrayElementGetter");
+        testArrayElementGetterSetter(false);
+    }
+
+    @Test
+    public void testArrayElementSetter() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testArrayElementSetter0);
+    }
+
+    public void testArrayElementSetter0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("arrayElementSetter");
+        testArrayElementGetterSetter(true);
+    }
+
+    private static final int TEST_ARRAY_NONE = 0, TEST_ARRAY_NPE = 1, TEST_ARRAY_OOB = 2, TEST_ARRAY_ASE = 3;
+
+    public void testArrayElementGetterSetter(boolean testSetter) throws Throwable {
+        testArrayElementGetterSetter(testSetter, TEST_ARRAY_NONE);
+    }
+
+    @Test
+    public void testArrayElementErrors() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testArrayElementErrors0);
+    }
+
+    public void testArrayElementErrors0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("arrayElementErrors");
+        testArrayElementGetterSetter(false, TEST_ARRAY_NPE);
+        testArrayElementGetterSetter(true, TEST_ARRAY_NPE);
+        testArrayElementGetterSetter(false, TEST_ARRAY_OOB);
+        testArrayElementGetterSetter(true, TEST_ARRAY_OOB);
+        testArrayElementGetterSetter(new Object[10], true, TEST_ARRAY_ASE);
+        testArrayElementGetterSetter(new Example[10], true, TEST_ARRAY_ASE);
+        testArrayElementGetterSetter(new IntExample[10], true, TEST_ARRAY_ASE);
+    }
+
+    public void testArrayElementGetterSetter(boolean testSetter, int negTest) throws Throwable {
+        testArrayElementGetterSetter(new String[10], testSetter, negTest);
+        testArrayElementGetterSetter(new Iterable<?>[10], testSetter, negTest);
+        testArrayElementGetterSetter(new Example[10], testSetter, negTest);
+        testArrayElementGetterSetter(new IntExample[10], testSetter, negTest);
+        testArrayElementGetterSetter(new Object[10], testSetter, negTest);
+        testArrayElementGetterSetter(new boolean[10], testSetter, negTest);
+        testArrayElementGetterSetter(new byte[10], testSetter, negTest);
+        testArrayElementGetterSetter(new char[10], testSetter, negTest);
+        testArrayElementGetterSetter(new short[10], testSetter, negTest);
+        testArrayElementGetterSetter(new int[10], testSetter, negTest);
+        testArrayElementGetterSetter(new float[10], testSetter, negTest);
+        testArrayElementGetterSetter(new long[10], testSetter, negTest);
+        testArrayElementGetterSetter(new double[10], testSetter, negTest);
+    }
+
+    public void testArrayElementGetterSetter(Object array, boolean testSetter, int negTest) throws Throwable {
+        boolean positive = (negTest == TEST_ARRAY_NONE);
+        int length = Array.getLength(array);
+        Class<?> arrayType = array.getClass();
+        Class<?> elemType = arrayType.getComponentType();
+        Object arrayToMH = array;
+        // this stanza allows negative tests to make argument perturbations:
+        switch (negTest) {
+        case TEST_ARRAY_NPE:
+            arrayToMH = null;
+            break;
+        case TEST_ARRAY_OOB:
+            assert(length > 0);
+            arrayToMH = Array.newInstance(elemType, 0);
+            break;
+        case TEST_ARRAY_ASE:
+            assert(testSetter && !elemType.isPrimitive());
+            if (elemType == Object.class)
+                arrayToMH = new StringBuffer[length];  // very random subclass of Object!
+            else if (elemType == Example.class)
+                arrayToMH = new SubExample[length];
+            else if (elemType == IntExample.class)
+                arrayToMH = new SubIntExample[length];
+            else
+                return;  // can't make an ArrayStoreException test
+            assert(arrayType.isInstance(arrayToMH))
+                : Arrays.asList(arrayType, arrayToMH.getClass(), testSetter, negTest);
+            break;
+        }
+        countTest(positive);
+        if (verbosity > 2)  System.out.println("array type = "+array.getClass().getComponentType().getName()+"["+length+"]"+(positive ? "" : " negative test #"+negTest+" using "+Arrays.deepToString(new Object[]{arrayToMH})));
+        MethodType expType = !testSetter
+                ? MethodType.methodType(elemType,   arrayType, int.class)
+                : MethodType.methodType(void.class, arrayType, int.class, elemType);
+        MethodHandle mh = !testSetter
+                ? MethodHandles.arrayElementGetter(arrayType)
+                : MethodHandles.arrayElementSetter(arrayType);
+        assertSame(mh.type(), expType);
+        if (elemType != int.class && elemType != boolean.class) {
+            MethodType gtype = mh.type().generic().changeParameterType(1, int.class);
+            if (testSetter)  gtype = gtype.changeReturnType(void.class);
+            mh = mh.asType(gtype);
+        }
+        Object sawValue, expValue;
+        List<Object> model = array2list(array);
+        Throwable caughtEx = null;
+        for (int i = 0; i < length; i++) {
+            // update array element
+            Object random = randomArg(elemType);
+            model.set(i, random);
+            if (testSetter) {
+                try {
+                    if (elemType == int.class)
+                        mh.invokeExact((int[]) arrayToMH, i, (int)random);
+                    else if (elemType == boolean.class)
+                        mh.invokeExact((boolean[]) arrayToMH, i, (boolean)random);
+                    else
+                        mh.invokeExact(arrayToMH, i, random);
+                } catch (RuntimeException ex) {
+                    caughtEx = ex;
+                    break;
+                }
+                assertEquals(model, array2list(array));
+            } else {
+                Array.set(array, i, random);
+            }
+            if (verbosity >= 5) {
+                List<Object> array2list = array2list(array);
+                System.out.println("a["+i+"]="+random+" => "+array2list);
+                if (!array2list.equals(model))
+                    System.out.println("***   != "+model);
+            }
+            // observe array element
+            sawValue = Array.get(array, i);
+            if (!testSetter) {
+                expValue = sawValue;
+                try {
+                    if (elemType == int.class)
+                        sawValue = (int) mh.invokeExact((int[]) arrayToMH, i);
+                    else if (elemType == boolean.class)
+                        sawValue = (boolean) mh.invokeExact((boolean[]) arrayToMH, i);
+                    else
+                        sawValue = mh.invokeExact(arrayToMH, i);
+                } catch (RuntimeException ex) {
+                    caughtEx = ex;
+                    break;
+                }
+                assertEquals(sawValue, expValue);
+                assertEquals(model, array2list(array));
+            }
+        }
+        if (!positive) {
+            if (caughtEx == null)
+                throw new RuntimeException("failed to catch exception for negTest="+negTest);
+            // test the kind of exception
+            Class<?> reqType = null;
+            switch (negTest) {
+            case TEST_ARRAY_ASE:  reqType = ArrayStoreException.class; break;
+            case TEST_ARRAY_OOB:  reqType = ArrayIndexOutOfBoundsException.class; break;
+            case TEST_ARRAY_NPE:  reqType = NullPointerException.class; break;
+            default:              assert(false);
+            }
+            if (reqType.isInstance(caughtEx)) {
+                caughtEx = null;  // nullify expected exception
+            }
+        }
+        if (caughtEx != null) {
+            throw new RuntimeException("unexpected exception", caughtEx);
+        }
+    }
+
+    List<Object> array2list(Object array) {
+        int length = Array.getLength(array);
+        ArrayList<Object> model = new ArrayList<>(length);
+        for (int i = 0; i < length; i++)
+            model.add(Array.get(array, i));
+        return model;
+    }
+
+    @Test
+    public void testConvertArguments() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testConvertArguments0);
+    }
+
+    public void testConvertArguments0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("convertArguments");
+        testConvert(Callee.ofType(1), null, "id", int.class);
+        testConvert(Callee.ofType(1), null, "id", String.class);
+        testConvert(Callee.ofType(1), null, "id", Integer.class);
+        testConvert(Callee.ofType(1), null, "id", short.class);
+        testConvert(Callee.ofType(1), null, "id", char.class);
+        testConvert(Callee.ofType(1), null, "id", byte.class);
+    }
+
+    void testConvert(MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
+        testConvert(true, id, rtype, name, params);
+    }
+
+    void testConvert(boolean positive,
+                     MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
+        countTest(positive);
+        MethodType idType = id.type();
+        if (rtype == null)  rtype = idType.returnType();
+        for (int i = 0; i < params.length; i++) {
+            if (params[i] == null)  params[i] = idType.parameterType(i);
+        }
+        // simulate the pairwise conversion
+        MethodType newType = MethodType.methodType(rtype, params);
+        Object[] args = randomArgs(newType.parameterArray());
+        Object[] convArgs = args.clone();
+        for (int i = 0; i < args.length; i++) {
+            Class<?> src = newType.parameterType(i);
+            Class<?> dst = idType.parameterType(i);
+            if (src != dst)
+                convArgs[i] = castToWrapper(convArgs[i], dst);
+        }
+        Object convResult = id.invokeWithArguments(convArgs);
+        {
+            Class<?> dst = newType.returnType();
+            Class<?> src = idType.returnType();
+            if (src != dst)
+                convResult = castToWrapper(convResult, dst);
+        }
+        MethodHandle target = null;
+        RuntimeException error = null;
+        try {
+            target = id.asType(newType);
+        } catch (WrongMethodTypeException ex) {
+            error = ex;
+        }
+        if (verbosity >= 3)
+            System.out.println("convert "+id+ " to "+newType+" => "+target
+                    +(error == null ? "" : " !! "+error));
+        if (positive && error != null)  throw error;
+        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
+        if (!positive)  return; // negative test failed as expected
+        assertEquals(newType, target.type());
+        printCalled(target, id.toString(), args);
+        Object result = target.invokeWithArguments(args);
+        assertCalled(name, convArgs);
+        assertEquals(convResult, result);
+        if (verbosity >= 1)
+            System.out.print(':');
+    }
+
+    @Test
+    public void testVarargsCollector() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testVarargsCollector0);
+    }
+
+    public void testVarargsCollector0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("varargsCollector");
+        MethodHandle vac0 = PRIVATE.findStatic(MethodHandlesTest.class, "called",
+                               MethodType.methodType(Object.class, String.class, Object[].class));
+        vac0 = vac0.bindTo("vac");
+        MethodHandle vac = vac0.asVarargsCollector(Object[].class);
+        testConvert(true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
+        testConvert(true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
+        for (Class<?> at : new Class<?>[] { Object.class, String.class, Integer.class }) {
+            testConvert(true, vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
+            testConvert(true, vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
+        }
+    }
+
+    @Test
+    public void testFilterReturnValue() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFilterReturnValue0);
+    }
+
+    public void testFilterReturnValue0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("filterReturnValue");
+        Class<?> classOfVCList = varargsList(1).invokeWithArguments(0).getClass();
+        assertTrue(List.class.isAssignableFrom(classOfVCList));
+        for (int nargs = 0; nargs <= 3; nargs++) {
+            for (Class<?> rtype : new Class<?>[] { Object.class,
+                                                   List.class,
+                                                   int.class,
+                                                   byte.class,
+                                                   long.class,
+                                                   CharSequence.class,
+                                                   String.class }) {
+                testFilterReturnValue(nargs, rtype);
+            }
+        }
+    }
+
+    void testFilterReturnValue(int nargs, Class<?> rtype) throws Throwable {
+        countTest();
+        MethodHandle target = varargsList(nargs, rtype);
+        MethodHandle filter;
+        if (List.class.isAssignableFrom(rtype) || rtype.isAssignableFrom(List.class))
+            filter = varargsList(1);  // add another layer of list-ness
+        else
+            filter = MethodHandles.identity(rtype);
+        filter = filter.asType(MethodType.methodType(target.type().returnType(), rtype));
+        Object[] argsToPass = randomArgs(nargs, Object.class);
+        if (verbosity >= 3)
+            System.out.println("filter "+target+" to "+rtype.getSimpleName()+" with "+filter);
+        MethodHandle target2 = MethodHandles.filterReturnValue(target, filter);
+        if (verbosity >= 4)
+            System.out.println("filtered target: "+target2);
+        // Simulate expected effect of filter on return value:
+        Object unfiltered = target.invokeWithArguments(argsToPass);
+        Object expected = filter.invokeWithArguments(unfiltered);
+        if (verbosity >= 4)
+            System.out.println("unfiltered: "+unfiltered+" : "+unfiltered.getClass().getSimpleName());
+        if (verbosity >= 4)
+            System.out.println("expected: "+expected+" : "+expected.getClass().getSimpleName());
+        Object result = target2.invokeWithArguments(argsToPass);
+        if (verbosity >= 3)
+            System.out.println("result: "+result+" : "+result.getClass().getSimpleName());
+        if (!expected.equals(result))
+            System.out.println("*** fail at n/rt = "+nargs+"/"+rtype.getSimpleName()+": "+
+                               Arrays.asList(argsToPass)+" => "+result+" != "+expected);
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testFilterArguments() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testFilterArguments0);
+    }
+
+    public void testFilterArguments0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("filterArguments");
+        for (int nargs = 1; nargs <= 6; nargs++) {
+            for (int pos = 0; pos < nargs; pos++) {
+                testFilterArguments(nargs, pos);
+            }
+        }
+    }
+
+    void testFilterArguments(int nargs, int pos) throws Throwable {
+        countTest();
+        MethodHandle target = varargsList(nargs);
+        MethodHandle filter = varargsList(1);
+        filter = filter.asType(filter.type().generic());
+        Object[] argsToPass = randomArgs(nargs, Object.class);
+        if (verbosity >= 3)
+            System.out.println("filter "+target+" at "+pos+" with "+filter);
+        MethodHandle target2 = MethodHandles.filterArguments(target, pos, filter);
+        // Simulate expected effect of filter on arglist:
+        Object[] filteredArgs = argsToPass.clone();
+        filteredArgs[pos] = filter.invokeExact(filteredArgs[pos]);
+        List<Object> expected = Arrays.asList(filteredArgs);
+        Object result = target2.invokeWithArguments(argsToPass);
+        if (verbosity >= 3)
+            System.out.println("result: "+result);
+        if (!expected.equals(result))
+            System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected);
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testCollectArguments() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testCollectArguments0);
+    }
+
+    public void testCollectArguments0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("collectArguments");
+        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, false);
+    }
+
+    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;
+                if (collectType != String.class)  maxArity = 5;
+                if (lastType != Object.class)  maxArity = 4;
+                for (int nargs = 0; nargs <= maxArity; nargs++) {
+                    ArrayList<Class<?>> argTypes = new ArrayList<>(Collections.nCopies(nargs, Object.class));
+                    int maxMix = 20;
+                    if (collectType != Object.class)  maxMix = 0;
+                    Map<Object,Integer> argTypesSeen = new HashMap<>();
+                    for (int mix = 0; mix <= maxMix; mix++) {
+                        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, withFoldPos);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    boolean mixArgs(List<Class<?>> argTypes, int mix, Map<Object,Integer> argTypesSeen) {
+        assert(mix >= 0);
+        if (mix == 0)  return true;  // no change
+        if ((mix >>> argTypes.size()) != 0)  return false;
+        for (int i = 0; i < argTypes.size(); i++) {
+            if (i >= 31)  break;
+            boolean bit = (mix & (1 << i)) != 0;
+            if (bit) {
+                Class<?> type = argTypes.get(i);
+                if (type == Object.class)
+                    type = String.class;
+                else if (type == String.class)
+                    type = int.class;
+                else
+                    type = Object.class;
+                argTypes.set(i, type);
+            }
+        }
+        Integer prev = argTypesSeen.put(new ArrayList<>(argTypes), mix);
+        if (prev != null) {
+            if (verbosity >= 4)  System.out.println("mix "+prev+" repeated "+mix+": "+argTypes);
+            return false;
+        }
+        if (verbosity >= 3)  System.out.println("mix "+mix+" = "+argTypes);
+        return true;
+    }
+
+    void testFoldOrCollectArguments(List<Class<?>> argTypes,  // argument types minus the inserted combineType
+                                    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,
+                                    boolean withFoldPos) throws Throwable {
+        int nargs = argTypes.size();
+        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 target see arg[pos..pos+cc-1]?
+            targetArgTypes.subList(pos, pos + fold).clear();
+        if (combineType != void.class)
+            targetArgTypes.add(pos, combineType);
+        MethodHandle target = varargsList(targetArgTypes, lastType);
+        MethodHandle combine = varargsList(combineArgTypes, combineType);
+        List<Object> argsToPass = Arrays.asList(randomArgs(argTypes));
+        if (verbosity >= 3)
+            System.out.println((isCollect ? "collect" : "fold")+" "+target+" with "+combine);
+        MethodHandle target2;
+        if (isCollect)
+            target2 = MethodHandles.collectArguments(target, pos, combine);
+        else
+            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);
+        if (verbosity >= 3)
+            System.out.println((isCollect ? "collect" : "fold")+": "+argsToFold+" into "+target2);
+        Object foldedArgs = combine.invokeWithArguments(argsToFold);
+        if (isCollect)
+            argsToFold.clear();
+        if (combineType != void.class)
+            argsToFold.add(0, foldedArgs);
+        Object result = target2.invokeWithArguments(argsToPass);
+        if (verbosity >= 3)
+            System.out.println("result: "+result);
+        Object expected = target.invokeWithArguments(expectedList);
+        if (!expected.equals(result))
+            System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result+" != "+expected);
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testDropArguments() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testDropArguments0);
+    }
+
+    public void testDropArguments0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("dropArguments");
+        for (int nargs = 0; nargs <= 4; nargs++) {
+            for (int drop = 1; drop <= 4; drop++) {
+                for (int pos = 0; pos <= nargs; pos++) {
+                    testDropArguments(nargs, pos, drop);
+                }
+            }
+        }
+    }
+
+    void testDropArguments(int nargs, int pos, int drop) throws Throwable {
+        countTest();
+        MethodHandle target = varargsArray(nargs);
+        Object[] args = randomArgs(target.type().parameterArray());
+        MethodHandle target2 = MethodHandles.dropArguments(target, pos,
+                Collections.nCopies(drop, Object.class).toArray(new Class<?>[0]));
+        List<Object> resList = Arrays.asList(args);
+        List<Object> argsToDrop = new ArrayList<>(resList);
+        for (int i = drop; i > 0; i--) {
+            argsToDrop.add(pos, "blort#"+i);
+        }
+        Object res2 = target2.invokeWithArguments(argsToDrop);
+        Object res2List = Arrays.asList((Object[])res2);
+        //if (!resList.equals(res2List))
+        //    System.out.println("*** fail at n/p/d = "+nargs+"/"+pos+"/"+drop+": "+argsToDrop+" => "+res2List);
+        assertEquals(resList, res2List);
+    }
+
+    @Test
+    public void testGuardWithTest() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testGuardWithTest0);
+    }
+
+    public void testGuardWithTest0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("guardWithTest");
+        for (int nargs = 0; nargs <= 50; nargs++) {
+            if (CAN_TEST_LIGHTLY && nargs > 7)  break;
+            testGuardWithTest(nargs, Object.class);
+            testGuardWithTest(nargs, String.class);
+        }
+    }
+
+    void testGuardWithTest(int nargs, Class<?> argClass) throws Throwable {
+        testGuardWithTest(nargs, 0, argClass);
+        if (nargs <= 5 || nargs % 10 == 3) {
+            for (int testDrops = 1; testDrops <= nargs; testDrops++)
+                testGuardWithTest(nargs, testDrops, argClass);
+        }
+    }
+
+    void testGuardWithTest(int nargs, int testDrops, Class<?> argClass) throws Throwable {
+        countTest();
+        int nargs1 = Math.min(3, nargs);
+        MethodHandle test = PRIVATE.findVirtual(Object.class, "equals", MethodType.methodType(boolean.class, Object.class));
+        MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "targetIfEquals", MethodType.genericMethodType(nargs1));
+        MethodHandle fallback = PRIVATE.findStatic(MethodHandlesTest.class, "fallbackIfNotEquals", MethodType.genericMethodType(nargs1));
+        while (test.type().parameterCount() > nargs)
+            // 0: test = constant(MISSING_ARG.equals(MISSING_ARG))
+            // 1: test = lambda (_) MISSING_ARG.equals(_)
+            test = MethodHandles.insertArguments(test, 0, MISSING_ARG);
+        if (argClass != Object.class) {
+            test = changeArgTypes(test, argClass);
+            target = changeArgTypes(target, argClass);
+            fallback = changeArgTypes(fallback, argClass);
+        }
+        int testArgs = nargs - testDrops;
+        assert(testArgs >= 0);
+        test = addTrailingArgs(test, Math.min(testArgs, nargs), argClass);
+        target = addTrailingArgs(target, nargs, argClass);
+        fallback = addTrailingArgs(fallback, nargs, argClass);
+        Object[][] argLists = {
+            { },
+            { "foo" }, { MethodHandlesTest.MISSING_ARG },
+            { "foo", "foo" }, { "foo", "bar" },
+            { "foo", "foo", "baz" }, { "foo", "bar", "baz" }
+        };
+        for (Object[] argList : argLists) {
+            Object[] argList1 = argList;
+            if (argList.length != nargs) {
+                if (argList.length != nargs1)  continue;
+                argList1 = Arrays.copyOf(argList, nargs);
+                Arrays.fill(argList1, nargs1, nargs, MethodHandlesTest.MISSING_ARG_2);
+            }
+            MethodHandle test1 = test;
+            if (test1.type().parameterCount() > testArgs) {
+                int pc = test1.type().parameterCount();
+                test1 = MethodHandles.insertArguments(test, testArgs, Arrays.copyOfRange(argList1, testArgs, pc));
+            }
+            MethodHandle mh = MethodHandles.guardWithTest(test1, target, fallback);
+            assertEquals(target.type(), mh.type());
+            boolean equals;
+            switch (nargs) {
+            case 0:   equals = true; break;
+            case 1:   equals = MethodHandlesTest.MISSING_ARG.equals(argList[0]); break;
+            default:  equals = argList[0].equals(argList[1]); break;
+            }
+            String willCall = (equals ? "targetIfEquals" : "fallbackIfNotEquals");
+            if (verbosity >= 3)
+                System.out.println(logEntry(willCall, argList));
+            Object result = mh.invokeWithArguments(argList1);
+            assertCalled(willCall, argList);
+        }
+    }
+
+    @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);
+            if (verbosity >= 6) {
+                System.out.println("pre-handle: " + pre);
+            }
+            Object[] preResults = (Object[]) pre.invokeWithArguments(args);
+            if (verbosity >= 4) {
+                System.out.println("pre-checked: expected " + Arrays.asList(preCheckedResults[i]) + ", actual " +
+                        Arrays.asList(preResults));
+            }
+            if (verbosity >= 6) {
+                System.out.println("post-handle: " + post);
+            }
+            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];
+        }
+    }
+
+    @Test
+    public void testThrowException() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testThrowException0);
+    }
+
+    public void testThrowException0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("throwException");
+        testThrowException(int.class, new ClassCastException("testing"));
+        testThrowException(void.class, new java.io.IOException("testing"));
+        testThrowException(String.class, new LinkageError("testing"));
+    }
+
+    void testThrowException(Class<?> returnType, Throwable thrown) throws Throwable {
+        countTest();
+        Class<? extends Throwable> exType = thrown.getClass();
+        MethodHandle target = MethodHandles.throwException(returnType, exType);
+        //System.out.println("throwing with "+target+" : "+thrown);
+        MethodType expectedType = MethodType.methodType(returnType, exType);
+        assertEquals(expectedType, target.type());
+        target = target.asType(target.type().generic());
+        Throwable caught = null;
+        try {
+            Object res = target.invokeExact((Object) thrown);
+            fail("got "+res+" instead of throwing "+thrown);
+        } catch (Throwable ex) {
+            if (ex != thrown) {
+                if (ex instanceof Error)  throw (Error)ex;
+                if (ex instanceof RuntimeException)  throw (RuntimeException)ex;
+            }
+            caught = ex;
+        }
+        assertSame(thrown, caught);
+    }
+
+    @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);
+        }
+    }
+
+    @Test
+    public void testAsInterfaceInstance() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testAsInterfaceInstance0);
+    }
+
+    public void testAsInterfaceInstance0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("asInterfaceInstance");
+        Lookup lookup = MethodHandles.lookup();
+        // test typical case:  Runnable.run
+        {
+            countTest();
+            if (verbosity >= 2)  System.out.println("Runnable");
+            MethodType mt = MethodType.methodType(void.class);
+            MethodHandle mh = lookup.findStatic(MethodHandlesGeneralTest.class, "runForRunnable", mt);
+            Runnable proxy = MethodHandleProxies.asInterfaceInstance(Runnable.class, mh);
+            proxy.run();
+            assertCalled("runForRunnable");
+        }
+        // well known single-name overloaded interface:  Appendable.append
+        {
+            countTest();
+            if (verbosity >= 2)  System.out.println("Appendable");
+            ArrayList<List<?>> appendResults = new ArrayList<>();
+            MethodHandle append = lookup.bind(appendResults, "add", MethodType.methodType(boolean.class, Object.class));
+            append = append.asType(MethodType.methodType(void.class, List.class)); // specialize the type
+            MethodHandle asList = lookup.findStatic(Arrays.class, "asList", MethodType.methodType(List.class, Object[].class));
+            MethodHandle mh = MethodHandles.filterReturnValue(asList, append).asVarargsCollector(Object[].class);
+            Appendable proxy = MethodHandleProxies.asInterfaceInstance(Appendable.class, mh);
+            proxy.append("one");
+            proxy.append("two", 3, 4);
+            proxy.append('5');
+            assertEquals(Arrays.asList(Arrays.asList("one"),
+                                       Arrays.asList("two", 3, 4),
+                                       Arrays.asList('5')),
+                         appendResults);
+            if (verbosity >= 3)  System.out.println("appendResults="+appendResults);
+            appendResults.clear();
+            Formatter formatter = new Formatter(proxy);
+            String fmt = "foo str=%s char='%c' num=%d";
+            Object[] fmtArgs = { "str!", 'C', 42 };
+            String expect = String.format(fmt, fmtArgs);
+            formatter.format(fmt, fmtArgs);
+            String actual = "";
+            if (verbosity >= 3)  System.out.println("appendResults="+appendResults);
+            for (List<?> l : appendResults) {
+                Object x = l.get(0);
+                switch (l.size()) {
+                case 1:  actual += x; continue;
+                case 3:  actual += ((String)x).substring((int)(Object)l.get(1), (int)(Object)l.get(2)); continue;
+                }
+                actual += l;
+            }
+            if (verbosity >= 3)  System.out.println("expect="+expect);
+            if (verbosity >= 3)  System.out.println("actual="+actual);
+            assertEquals(expect, actual);
+        }
+        // test case of an single name which is overloaded:  Fooable.foo(...)
+        {
+            if (verbosity >= 2)  System.out.println("Fooable");
+            MethodHandle mh = lookup.findStatic(MethodHandlesGeneralTest.class, "fooForFooable",
+                                                MethodType.methodType(Object.class, String.class, Object[].class));
+            Fooable proxy = MethodHandleProxies.asInterfaceInstance(Fooable.class, mh);
+            for (Method m : Fooable.class.getDeclaredMethods()) {
+                countTest();
+                assertSame("foo", m.getName());
+                if (verbosity > 3)
+                    System.out.println("calling "+m);
+                MethodHandle invoker = lookup.unreflect(m);
+                MethodType mt = invoker.type();
+                Class<?>[] types = mt.parameterArray();
+                types[0] = int.class;  // placeholder
+                Object[] args = randomArgs(types);
+                args[0] = proxy;
+                if (verbosity > 3)
+                    System.out.println("calling "+m+" on "+Arrays.asList(args));
+                Object result = invoker.invokeWithArguments(args);
+                if (verbosity > 4)
+                    System.out.println("result = "+result);
+                String name = "fooForFooable/"+args[1];
+                Object[] argTail = Arrays.copyOfRange(args, 2, args.length);
+                assertCalled(name, argTail);
+                assertEquals(result, logEntry(name, argTail));
+            }
+        }
+        // test processing of thrown exceptions:
+        for (Throwable ex : new Throwable[] { new NullPointerException("ok"),
+                                              new InternalError("ok"),
+                                              new Throwable("fail"),
+                                              new Exception("fail"),
+                                              new MyCheckedException()
+                                            }) {
+            MethodHandle mh = MethodHandles.throwException(void.class, Throwable.class);
+            mh = MethodHandles.insertArguments(mh, 0, ex);
+            WillThrow proxy = MethodHandleProxies.asInterfaceInstance(WillThrow.class, mh);
+            try {
+                countTest();
+                proxy.willThrow();
+                System.out.println("Failed to throw: "+ex);
+                assertTrue(false);
+            } catch (Throwable ex1) {
+                if (verbosity > 3) {
+                    System.out.println("throw "+ex);
+                    System.out.println("catch "+(ex == ex1 ? "UNWRAPPED" : ex1));
+                }
+                if (ex instanceof RuntimeException ||
+                    ex instanceof Error) {
+                    assertSame("must pass unchecked exception out without wrapping", ex, ex1);
+                } else if (ex instanceof MyCheckedException) {
+                    assertSame("must pass declared exception out without wrapping", ex, ex1);
+                } else {
+                    assertNotSame("must pass undeclared checked exception with wrapping", ex, ex1);
+                    if (!(ex1 instanceof UndeclaredThrowableException) || ex1.getCause() != ex) {
+                        ex1.printStackTrace(System.out);
+                    }
+                    assertSame(ex, ex1.getCause());
+                    UndeclaredThrowableException utex = (UndeclaredThrowableException) ex1;
+                }
+            }
+        }
+        // Test error checking on bad interfaces:
+        for (Class<?> nonSMI : new Class<?>[] { Object.class,
+                                             String.class,
+                                             CharSequence.class,
+                                             java.io.Serializable.class,
+                                             PrivateRunnable.class,
+                                             Example.class }) {
+            if (verbosity > 2)  System.out.println(nonSMI.getName());
+            try {
+                countTest(false);
+                MethodHandleProxies.asInterfaceInstance(nonSMI, varargsArray(0));
+                assertTrue("Failed to throw on "+nonSMI.getName(), false);
+            } catch (IllegalArgumentException ex) {
+                if (verbosity > 2)  System.out.println(nonSMI.getSimpleName()+": "+ex);
+                // Object: java.lang.IllegalArgumentException:
+                //     not a public interface: java.lang.Object
+                // String: java.lang.IllegalArgumentException:
+                //     not a public interface: java.lang.String
+                // CharSequence: java.lang.IllegalArgumentException:
+                //     not a single-method interface: java.lang.CharSequence
+                // Serializable: java.lang.IllegalArgumentException:
+                //     not a single-method interface: java.io.Serializable
+                // PrivateRunnable: java.lang.IllegalArgumentException:
+                //     not a public interface: test.java.lang.invoke.MethodHandlesTest$PrivateRunnable
+                // Example: java.lang.IllegalArgumentException:
+                //     not a public interface: test.java.lang.invoke.MethodHandlesTest$Example
+            }
+        }
+        // Test error checking on interfaces with the wrong method type:
+        for (Class<?> intfc : new Class<?>[] { Runnable.class /*arity 0*/,
+                                            Fooable.class /*arity 1 & 2*/ }) {
+            int badArity = 1;  // known to be incompatible
+            if (verbosity > 2)  System.out.println(intfc.getName());
+            try {
+                countTest(false);
+                MethodHandleProxies.asInterfaceInstance(intfc, varargsArray(badArity));
+                assertTrue("Failed to throw on "+intfc.getName(), false);
+            } catch (WrongMethodTypeException ex) {
+                if (verbosity > 2)  System.out.println(intfc.getSimpleName()+": "+ex);
+                // Runnable: java.lang.invoke.WrongMethodTypeException:
+                //     cannot convert MethodHandle(Object)Object[] to ()void
+                // Fooable: java.lang.invoke.WrongMethodTypeException:
+                //     cannot convert MethodHandle(Object)Object[] to (Object,String)Object
+            }
+        }
+    }
+
+    @Test
+    public void testInterfaceCast() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testInterfaceCast0);
+    }
+
+    public void testInterfaceCast0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("interfaceCast");
+        assert( (((Object)"foo") instanceof CharSequence));
+        assert(!(((Object)"foo") instanceof Iterable));
+        for (MethodHandle mh : new MethodHandle[]{
+            MethodHandles.identity(String.class),
+            MethodHandles.identity(CharSequence.class),
+            MethodHandles.identity(Iterable.class)
+        }) {
+            if (verbosity > 0)  System.out.println("-- mh = "+mh);
+            for (Class<?> ctype : new Class<?>[]{
+                Object.class, String.class, CharSequence.class,
+                Number.class, Iterable.class
+            }) {
+                if (verbosity > 0)  System.out.println("---- ctype = "+ctype.getName());
+                //                           doret  docast
+                testInterfaceCast(mh, ctype, false, false);
+                testInterfaceCast(mh, ctype, true,  false);
+                testInterfaceCast(mh, ctype, false, true);
+                testInterfaceCast(mh, ctype, true,  true);
+            }
+        }
+    }
+
+    private static Class<?> i2o(Class<?> c) {
+        return (c.isInterface() ? Object.class : c);
+    }
+
+    public void testInterfaceCast(MethodHandle mh, Class<?> ctype,
+                                                   boolean doret, boolean docast) throws Throwable {
+        MethodHandle mh0 = mh;
+        if (verbosity > 1)
+            System.out.println("mh="+mh+", ctype="+ctype.getName()+", doret="+doret+", docast="+docast);
+        String normalRetVal = "normal return value";
+        MethodType mt = mh.type();
+        MethodType mt0 = mt;
+        if (doret)  mt = mt.changeReturnType(ctype);
+        else        mt = mt.changeParameterType(0, ctype);
+        if (docast) mh = MethodHandles.explicitCastArguments(mh, mt);
+        else        mh = mh.asType(mt);
+        assertEquals(mt, mh.type());
+        MethodType mt1 = mt;
+        // this bit is needed to make the interface types disappear for invokeWithArguments:
+        mh = MethodHandles.explicitCastArguments(mh, mt.generic());
+        Class<?>[] step = {
+            mt1.parameterType(0),  // param as passed to mh at first
+            mt0.parameterType(0),  // param after incoming cast
+            mt0.returnType(),      // return value before cast
+            mt1.returnType(),      // return value after outgoing cast
+        };
+        // where might a checkCast occur?
+        boolean[] checkCast = new boolean[step.length];
+        // the string value must pass each step without causing an exception
+        if (!docast) {
+            if (!doret) {
+                if (step[0] != step[1])
+                    checkCast[1] = true;  // incoming value is cast
+            } else {
+                if (step[2] != step[3])
+                    checkCast[3] = true;  // outgoing value is cast
+            }
+        }
+        boolean expectFail = false;
+        for (int i = 0; i < step.length; i++) {
+            Class<?> c = step[i];
+            if (!checkCast[i])  c = i2o(c);
+            if (!c.isInstance(normalRetVal)) {
+                if (verbosity > 3)
+                    System.out.println("expect failure at step "+i+" in "+Arrays.toString(step)+Arrays.toString(checkCast));
+                expectFail = true;
+                break;
+            }
+        }
+        countTest(!expectFail);
+        if (verbosity > 2)
+            System.out.println("expectFail="+expectFail+", mt="+mt);
+        Object res;
+        try {
+            res = mh.invokeWithArguments(normalRetVal);
+        } catch (Exception ex) {
+            res = ex;
+        }
+        boolean sawFail = !(res instanceof String);
+        if (sawFail != expectFail) {
+            System.out.println("*** testInterfaceCast: mh0 = "+mh0);
+            System.out.println("  retype using "+(docast ? "explicitCastArguments" : "asType")+" to "+mt+" => "+mh);
+            System.out.println("  call returned "+res);
+            System.out.println("  expected "+(expectFail ? "an exception" : normalRetVal));
+        }
+        if (!expectFail) {
+            assertFalse(res.toString(), sawFail);
+            assertEquals(normalRetVal, res);
+        } else {
+            assertTrue(res.toString(), sawFail);
+        }
+    }
+
+    static Example userMethod(Object o, String s, int i) {
+        called("userMethod", o, s, i);
+        return null;
+    }
+
+    @Test
+    public void testUserClassInSignature() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testUserClassInSignature0);
+    }
+
+    public void testUserClassInSignature0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("testUserClassInSignature");
+        Lookup lookup = MethodHandles.lookup();
+        String name; MethodType mt; MethodHandle mh;
+        Object[] args;
+
+        // Try a static method.
+        name = "userMethod";
+        mt = MethodType.methodType(Example.class, Object.class, String.class, int.class);
+        mh = lookup.findStatic(lookup.lookupClass(), name, mt);
+        assertEquals(mt, mh.type());
+        assertEquals(Example.class, mh.type().returnType());
+        args = randomArgs(mh.type().parameterArray());
+        mh.invokeWithArguments(args);
+        assertCalled(name, args);
+
+        // Try a virtual method.
+        name = "v2";
+        mt = MethodType.methodType(Object.class, Object.class, int.class);
+        mh = lookup.findVirtual(Example.class, name, mt);
+        assertEquals(mt, mh.type().dropParameterTypes(0,1));
+        assertTrue(mh.type().parameterList().contains(Example.class));
+        args = randomArgs(mh.type().parameterArray());
+        mh.invokeWithArguments(args);
+        assertCalled(name, args);
+    }
+
+    static void runForRunnable() {
+        called("runForRunnable");
+    }
+
+    public interface Fooable {
+        // overloads:
+        Object  foo(Object x, String y);
+        List<?> foo(String x, int y);
+        Object  foo(String x);
+    }
+
+    static Object fooForFooable(String x, Object... y) {
+        return called("fooForFooable/"+x, y);
+    }
+
+    @SuppressWarnings("serial")  // not really a public API, just a test case
+    public static class MyCheckedException extends Exception {
+    }
+
+    public interface WillThrow {
+        void willThrow() throws MyCheckedException;
+    }
+
+    /*non-public*/ interface PrivateRunnable {
+        public void run();
+    }
+
+    @Test
+    public void testRunnableProxy() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testRunnableProxy0);
+    }
+
+    public void testRunnableProxy0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("testRunnableProxy");
+        MethodHandles.Lookup lookup = MethodHandles.lookup();
+        MethodHandle run = lookup.findStatic(lookup.lookupClass(), "runForRunnable", MethodType.methodType(void.class));
+        Runnable r = MethodHandleProxies.asInterfaceInstance(Runnable.class, run);
+        testRunnableProxy(r);
+        assertCalled("runForRunnable");
+    }
+
+    private static void testRunnableProxy(Runnable r) {
+        //7058630: JSR 292 method handle proxy violates contract for Object methods
+        r.run();
+        Object o = r;
+        r = null;
+        boolean eq = (o == o);
+        int     hc = System.identityHashCode(o);
+        String  st = o.getClass().getName() + "@" + Integer.toHexString(hc);
+        Object expect = Arrays.asList(st, eq, hc);
+        if (verbosity >= 2)  System.out.println("expect st/eq/hc = "+expect);
+        Object actual = Arrays.asList(o.toString(), o.equals(o), o.hashCode());
+        if (verbosity >= 2)  System.out.println("actual st/eq/hc = "+actual);
+        assertEquals(expect, actual);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/MethodHandlesInsertArgumentsTest.java	Thu May 03 04:18:22 2018 -0700
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @library /lib/testlibrary /java/lang/invoke/common
+ * @compile MethodHandlesTest.java MethodHandlesInsertArgumentsTest.java remote/RemoteExample.java
+ * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions
+ *                                 -XX:-VerifyDependencies
+ *                                 -esa
+ *                                 test.java.lang.invoke.MethodHandlesInsertArgumentsTest
+ */
+
+package test.java.lang.invoke;
+
+import org.junit.*;
+import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class MethodHandlesInsertArgumentsTest extends MethodHandlesTest {
+
+    @Test // SLOW
+    public void testInsertArguments() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testInsertArguments0);
+    }
+
+    public void testInsertArguments0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("insertArguments");
+        for (int nargs = 0; nargs < 50; nargs++) {
+            if (CAN_TEST_LIGHTLY && nargs > 11)  break;
+            for (int ins = 0; ins <= nargs; ins++) {
+                if (nargs > 10 && ins > 4 && ins < nargs-4 && ins % 10 != 3)
+                    continue;
+                for (int pos = 0; pos <= nargs; pos++) {
+                    if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
+                        continue;
+                    if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2)  continue;
+                    testInsertArguments(nargs, pos, ins);
+                }
+            }
+        }
+    }
+
+    void testInsertArguments(int nargs, int pos, int ins) throws Throwable {
+        countTest();
+        MethodHandle target = varargsArray(nargs + ins);
+        Object[] args = randomArgs(target.type().parameterArray());
+        List<Object> resList = Arrays.asList(args);
+        List<Object> argsToPass = new ArrayList<>(resList);
+        List<Object> argsToInsert = argsToPass.subList(pos, pos + ins);
+        if (verbosity >= 3)
+            System.out.println("insert: "+argsToInsert+" @"+pos+" into "+target);
+        @SuppressWarnings("cast")  // cast to spread Object... is helpful
+        MethodHandle target2 = MethodHandles.insertArguments(target, pos,
+                (Object[]/*...*/) argsToInsert.toArray());
+        argsToInsert.clear();  // remove from argsToInsert
+        Object res2 = target2.invokeWithArguments(argsToPass);
+        Object res2List = Arrays.asList((Object[])res2);
+        if (verbosity >= 3)
+            System.out.println("result: "+res2List);
+        assertEquals(resList, res2List);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/MethodHandlesInvokersTest.java	Thu May 03 04:18:22 2018 -0700
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @library /lib/testlibrary /java/lang/invoke/common
+ * @compile MethodHandlesTest.java MethodHandlesInvokersTest.java remote/RemoteExample.java
+ * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions
+ *                                 -XX:-VerifyDependencies
+ *                                 -esa
+ *                                 test.java.lang.invoke.MethodHandlesInvokersTest
+ */
+
+package test.java.lang.invoke;
+
+import org.junit.*;
+import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MutableCallSite;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+public class MethodHandlesInvokersTest extends MethodHandlesTest {
+
+    @Test  // SLOW
+    public void testInvokers() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testInvokers0);
+    }
+
+    public void testInvokers0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("exactInvoker, genericInvoker, varargsInvoker, dynamicInvoker");
+        // exactInvoker, genericInvoker, varargsInvoker[0..N], dynamicInvoker
+        Set<MethodType> done = new HashSet<>();
+        for (int i = 0; i <= 6; i++) {
+            if (CAN_TEST_LIGHTLY && i > 3)  break;
+            MethodType gtype = MethodType.genericMethodType(i);
+            for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
+                for (int j = -1; j < i; j++) {
+                    MethodType type = gtype;
+                    if (j < 0)
+                        type = type.changeReturnType(argType);
+                    else if (argType == void.class)
+                        continue;
+                    else
+                        type = type.changeParameterType(j, argType);
+                    if (done.add(type))
+                        testInvokersWithCatch(type);
+                    MethodType vtype = type.changeReturnType(void.class);
+                    if (done.add(vtype))
+                        testInvokersWithCatch(vtype);
+                }
+            }
+        }
+    }
+
+    public void testInvokersWithCatch(MethodType type) throws Throwable {
+        try {
+            testInvokers(type);
+        } catch (Throwable ex) {
+            System.out.println("*** testInvokers on "+type+" => ");
+            ex.printStackTrace(System.out);
+        }
+    }
+
+    public void testInvokers(MethodType type) throws Throwable {
+        if (verbosity >= 3)
+            System.out.println("test invokers for "+type);
+        int nargs = type.parameterCount();
+        boolean testRetCode = type.returnType() != void.class;
+        MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "invokee",
+                                                 MethodType.genericMethodType(0, true));
+        assertTrue(target.isVarargsCollector());
+        target = target.asType(type);
+        Object[] args = randomArgs(type.parameterArray());
+        List<Object> targetPlusArgs = new ArrayList<>(Arrays.asList(args));
+        targetPlusArgs.add(0, target);
+        int code = (Integer) invokee(args);
+        Object log = logEntry("invokee", args);
+        assertEquals(log.hashCode(), code);
+        assertCalled("invokee", args);
+        MethodHandle inv;
+        Object result;
+        // exact invoker
+        countTest();
+        calledLog.clear();
+        inv = MethodHandles.exactInvoker(type);
+        result = inv.invokeWithArguments(targetPlusArgs);
+        if (testRetCode)  assertEquals(code, result);
+        assertCalled("invokee", args);
+        // generic invoker
+        countTest();
+        inv = MethodHandles.invoker(type);
+        if (nargs <= 3 && type == type.generic()) {
+            calledLog.clear();
+            switch (nargs) {
+            case 0:
+                result = inv.invokeExact(target);
+                break;
+            case 1:
+                result = inv.invokeExact(target, args[0]);
+                break;
+            case 2:
+                result = inv.invokeExact(target, args[0], args[1]);
+                break;
+            case 3:
+                result = inv.invokeExact(target, args[0], args[1], args[2]);
+                break;
+            }
+            if (testRetCode)  assertEquals(code, result);
+            assertCalled("invokee", args);
+        }
+        calledLog.clear();
+        result = inv.invokeWithArguments(targetPlusArgs);
+        if (testRetCode)  assertEquals(code, result);
+        assertCalled("invokee", args);
+        // varargs invoker #0
+        calledLog.clear();
+        inv = MethodHandles.spreadInvoker(type, 0);
+        if (type.returnType() == Object.class) {
+            result = inv.invokeExact(target, args);
+        } else if (type.returnType() == void.class) {
+            result = null; inv.invokeExact(target, args);
+        } else {
+            result = inv.invokeWithArguments(target, (Object) args);
+        }
+        if (testRetCode)  assertEquals(code, result);
+        assertCalled("invokee", args);
+        if (nargs >= 1 && type == type.generic()) {
+            // varargs invoker #1
+            calledLog.clear();
+            inv = MethodHandles.spreadInvoker(type, 1);
+            result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs));
+            if (testRetCode)  assertEquals(code, result);
+            assertCalled("invokee", args);
+        }
+        if (nargs >= 2 && type == type.generic()) {
+            // varargs invoker #2
+            calledLog.clear();
+            inv = MethodHandles.spreadInvoker(type, 2);
+            result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs));
+            if (testRetCode)  assertEquals(code, result);
+            assertCalled("invokee", args);
+        }
+        if (nargs >= 3 && type == type.generic()) {
+            // varargs invoker #3
+            calledLog.clear();
+            inv = MethodHandles.spreadInvoker(type, 3);
+            result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs));
+            if (testRetCode)  assertEquals(code, result);
+            assertCalled("invokee", args);
+        }
+        for (int k = 0; k <= nargs; k++) {
+            // varargs invoker #0..N
+            if (CAN_TEST_LIGHTLY && (k > 1 || k < nargs - 1))  continue;
+            countTest();
+            calledLog.clear();
+            inv = MethodHandles.spreadInvoker(type, k);
+            MethodType expType = (type.dropParameterTypes(k, nargs)
+                                 .appendParameterTypes(Object[].class)
+                                 .insertParameterTypes(0, MethodHandle.class));
+            assertEquals(expType, inv.type());
+            List<Object> targetPlusVarArgs = new ArrayList<>(targetPlusArgs);
+            List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
+            Object[] tail = tailList.toArray();
+            tailList.clear(); tailList.add(tail);
+            result = inv.invokeWithArguments(targetPlusVarArgs);
+            if (testRetCode)  assertEquals(code, result);
+            assertCalled("invokee", args);
+        }
+
+        // dynamic invoker
+        countTest();
+        CallSite site = new MutableCallSite(type);
+        inv = site.dynamicInvoker();
+
+        // see if we get the result of the original target:
+        try {
+            result = inv.invokeWithArguments(args);
+            assertTrue("should not reach here", false);
+        } catch (IllegalStateException ex) {
+            String msg = ex.getMessage();
+            assertTrue(msg, msg.contains("site"));
+        }
+
+        // set new target after invoker is created, to make sure we track target
+        site.setTarget(target);
+        calledLog.clear();
+        result = inv.invokeWithArguments(args);
+        if (testRetCode)  assertEquals(code, result);
+        assertCalled("invokee", args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/MethodHandlesPermuteArgumentsTest.java	Thu May 03 04:18:22 2018 -0700
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @library /lib/testlibrary /java/lang/invoke/common
+ * @compile MethodHandlesTest.java MethodHandlesPermuteArgumentsTest.java remote/RemoteExample.java
+ * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions
+ *                                 -XX:-VerifyDependencies
+ *                                 -esa
+ *                                 test.java.lang.invoke.MethodHandlesPermuteArgumentsTest
+ */
+
+
+package test.java.lang.invoke;
+
+import org.junit.*;
+import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class MethodHandlesPermuteArgumentsTest extends MethodHandlesTest {
+
+    @Test // SLOW
+    public void testPermuteArguments() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testPermuteArguments0);
+    }
+
+    public void testPermuteArguments0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("permuteArguments");
+        testPermuteArguments(4, Integer.class, 2, long.class, 6);
+        if (CAN_TEST_LIGHTLY)  return;
+        testPermuteArguments(4, Integer.class, 2, String.class, 0);
+        testPermuteArguments(6, Integer.class, 0, null, 30);
+    }
+
+    public void testPermuteArguments(int max, Class<?> type1, int t2c, Class<?> type2, int dilution) throws Throwable {
+        if (verbosity >= 2)
+            System.out.println("permuteArguments "+max+"*"+type1.getName()
+                    +(t2c==0?"":"/"+t2c+"*"+type2.getName())
+                    +(dilution > 0 ? " with dilution "+dilution : ""));
+        int t2pos = t2c == 0 ? 0 : 1;
+        for (int inargs = t2pos+1; inargs <= max; inargs++) {
+            Class<?>[] types = new Class<?>[inargs];
+            Arrays.fill(types, type1);
+            if (t2c != 0) {
+                // Fill in a middle range with type2:
+                Arrays.fill(types, t2pos, Math.min(t2pos+t2c, inargs), type2);
+            }
+            Object[] args = randomArgs(types);
+            int numcases = 1;
+            for (int outargs = 0; outargs <= max; outargs++) {
+                if (outargs - inargs >= MAX_ARG_INCREASE)  continue;
+                int casStep = dilution + 1;
+                // Avoid some common factors:
+                while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) ||
+                       (casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0))
+                    casStep++;
+                testPermuteArguments(args, types, outargs, numcases, casStep);
+                numcases *= inargs;
+                if (CAN_TEST_LIGHTLY && outargs < max-2)  continue;
+                if (dilution > 10 && outargs >= 4) {
+                    if (CAN_TEST_LIGHTLY)  continue;
+                    int[] reorder = new int[outargs];
+                    // Do some special patterns, which we probably missed.
+                    // Replication of a single argument or argument pair.
+                    for (int i = 0; i < inargs; i++) {
+                        Arrays.fill(reorder, i);
+                        testPermuteArguments(args, types, reorder);
+                        for (int d = 1; d <= 2; d++) {
+                            if (i + d >= inargs)  continue;
+                            for (int j = 1; j < outargs; j += 2)
+                                reorder[j] += 1;
+                            testPermuteArguments(args, types, reorder);
+                            testPermuteArguments(args, types, reverse(reorder));
+                        }
+                    }
+                    // Repetition of a sequence of 3 or more arguments.
+                    for (int i = 1; i < inargs; i++) {
+                        for (int len = 3; len <= inargs; len++) {
+                            for (int j = 0; j < outargs; j++)
+                                reorder[j] = (i + (j % len)) % inargs;
+                            testPermuteArguments(args, types, reorder);
+                            testPermuteArguments(args, types, reverse(reorder));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void testPermuteArguments(Object[] args, Class<?>[] types,
+                                     int outargs, int numcases, int casStep) throws Throwable {
+        int inargs = args.length;
+        int[] reorder = new int[outargs];
+        for (int cas = 0; cas < numcases; cas += casStep) {
+            for (int i = 0, c = cas; i < outargs; i++) {
+                reorder[i] = c % inargs;
+                c /= inargs;
+            }
+            if (CAN_TEST_LIGHTLY && outargs >= 3 &&
+               (reorder[0] == reorder[1] || reorder[1] == reorder[2]))
+                   continue;
+            testPermuteArguments(args, types, reorder);
+        }
+    }
+
+    static int[] reverse(int[] reorder) {
+        reorder = reorder.clone();
+        for (int i = 0, imax = reorder.length / 2; i < imax; i++) {
+            int j = reorder.length - 1 - i;
+            int tem = reorder[i];
+            reorder[i] = reorder[j];
+            reorder[j] = tem;
+        }
+        return reorder;
+    }
+
+    void testPermuteArguments(Object[] args, Class<?>[] types, int[] reorder) throws Throwable {
+        countTest();
+        if (args == null && types == null) {
+            int max = 0;
+            for (int j : reorder) {
+                if (max < j)  max = j;
+            }
+            args = randomArgs(max+1, Integer.class);
+        }
+        if (args == null) {
+            args = randomArgs(types);
+        }
+        if (types == null) {
+            types = new Class<?>[args.length];
+            for (int i = 0; i < args.length; i++)
+                types[i] = args[i].getClass();
+        }
+        int inargs = args.length, outargs = reorder.length;
+        assertTrue(inargs == types.length);
+        if (verbosity >= 3)
+            System.out.println("permuteArguments "+Arrays.toString(reorder));
+        Object[] permArgs = new Object[outargs];
+        Class<?>[] permTypes = new Class<?>[outargs];
+        for (int i = 0; i < outargs; i++) {
+            permArgs[i] = args[reorder[i]];
+            permTypes[i] = types[reorder[i]];
+        }
+        if (verbosity >= 4) {
+            System.out.println("in args:   "+Arrays.asList(args));
+            System.out.println("out args:  "+Arrays.asList(permArgs));
+            System.out.println("in types:  "+Arrays.asList(types));
+            System.out.println("out types: "+Arrays.asList(permTypes));
+        }
+        MethodType inType  = MethodType.methodType(Object.class, types);
+        MethodType outType = MethodType.methodType(Object.class, permTypes);
+        MethodHandle target = varargsList(outargs).asType(outType);
+        MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
+        if (verbosity >= 5)  System.out.println("newTarget = "+newTarget);
+        Object result = newTarget.invokeWithArguments(args);
+        Object expected = Arrays.asList(permArgs);
+        if (!expected.equals(result)) {
+            System.out.println("*** failed permuteArguments "+Arrays.toString(reorder)+
+                               " types="+Arrays.asList(types));
+            System.out.println("in args:   "+Arrays.asList(args));
+            System.out.println("out args:  "+expected);
+            System.out.println("bad args:  "+result);
+        }
+        assertEquals(expected, result);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/MethodHandlesSpreadArgumentsTest.java	Thu May 03 04:18:22 2018 -0700
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @library /lib/testlibrary /java/lang/invoke/common
+ * @compile MethodHandlesTest.java MethodHandlesSpreadArgumentsTest.java remote/RemoteExample.java
+ * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions
+ *                                 -XX:-VerifyDependencies
+ *                                 -esa
+ *                                 test.java.lang.invoke.MethodHandlesSpreadArgumentsTest
+ */
+
+package test.java.lang.invoke;
+
+import org.junit.*;
+import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class MethodHandlesSpreadArgumentsTest extends MethodHandlesTest {
+
+    @Test // SLOW
+    public void testSpreadArguments() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments0);
+        CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments1);
+    }
+
+    public void testSpreadArguments0() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("spreadArguments");
+        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, arrayType, pos, nargs);
+                }
+            }
+        }
+    }
+
+    public void testSpreadArguments(Class<?> argType, Class<?> arrayType, int pos, 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+".."+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);
+            switch (nargs) {
+                case 0:
+                    check = (Object[]) (Object) target.invokeExact();
+                    assertArrayEquals(args, check);
+                    break;
+                case 1:
+                    check = (Object[]) (Object) target.invokeExact(args[0]);
+                    assertArrayEquals(args, check);
+                    break;
+                case 2:
+                    check = (Object[]) (Object) target.invokeExact(args[0], args[1]);
+                    assertArrayEquals(args, check);
+                    break;
+            }
+        }
+    }
+
+    private static void checkReturnValue(Class<?> argType, Object[] args, MethodHandle result, Object returnValue) {
+        String argstr = Arrays.toString(args);
+        if (!argType.isPrimitive()) {
+            Object[] rv = (Object[]) returnValue;
+            String rvs = Arrays.toString(rv);
+            if (!Arrays.equals(args, rv)) {
+                System.out.println("method:   "+result);
+                System.out.println("expected: "+argstr);
+                System.out.println("returned: "+rvs);
+                assertArrayEquals(args, rv);
+            }
+        } else if (argType == int.class) {
+            String rvs = Arrays.toString((int[]) returnValue);
+            if (!argstr.equals(rvs)) {
+                System.out.println("method:   "+result);
+                System.out.println("expected: "+argstr);
+                System.out.println("returned: "+rvs);
+                assertEquals(argstr, rvs);
+            }
+        } else if (argType == long.class) {
+            String rvs = Arrays.toString((long[]) returnValue);
+            if (!argstr.equals(rvs)) {
+                System.out.println("method:   "+result);
+                System.out.println("expected: "+argstr);
+                System.out.println("returned: "+rvs);
+                assertEquals(argstr, rvs);
+            }
+        } else {
+            // cannot test...
+        }
+    }
+
+}
--- a/test/jdk/java/lang/invoke/MethodHandlesTest.java	Mon May 07 11:38:21 2018 +0200
+++ b/test/jdk/java/lang/invoke/MethodHandlesTest.java	Thu May 03 04:18:22 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2018, 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
@@ -21,57 +21,36 @@
  * questions.
  */
 
-/* @test
- * @summary unit tests for java.lang.invoke.MethodHandles
- * @library /lib/testlibrary /java/lang/invoke/common
- * @compile MethodHandlesTest.java remote/RemoteExample.java
- * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions
- *                                 -XX:-VerifyDependencies
- *                                 -esa
- *                                 test.java.lang.invoke.MethodHandlesTest
- */
-
 package test.java.lang.invoke;
 
 import org.junit.*;
-import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
 import test.java.lang.invoke.remote.RemoteExample;
 
-import java.lang.invoke.CallSite;
 import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandleProxies;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
-import java.lang.invoke.MutableCallSite;
-import java.lang.invoke.WrongMethodTypeException;
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.lang.reflect.UndeclaredThrowableException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Formatter;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
-import static java.lang.invoke.MethodType.methodType;
 import static org.junit.Assert.*;
 
 /**
  *
  * @author jrose
  */
-public class MethodHandlesTest {
+public abstract class MethodHandlesTest {
+
     static final Class<?> THIS_CLASS = MethodHandlesTest.class;
     // How much output?
     static int verbosity = 0;
+
     static {
         String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbosity");
         if (vstr == null)
@@ -83,6 +62,7 @@
     // a particular new, non-working test.  Tests which are known to
     // work (or have recently worked) test this flag and return on true.
     static final boolean CAN_SKIP_WORKING;
+
     static {
         String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".CAN_SKIP_WORKING");
         if (vstr == null)
@@ -94,21 +74,12 @@
     // This might be useful with -Xcomp stress tests that compile all method handles.
     static boolean CAN_TEST_LIGHTLY = Boolean.getBoolean(THIS_CLASS.getName()+".CAN_TEST_LIGHTLY");
 
-    @Test
-    public void testFirst() throws Throwable {
-        verbosity += 9; try {
-            // left blank for debugging
-        } finally { printCounts(); verbosity -= 9; }
-    }
-
     static final int MAX_ARG_INCREASE = 3;
 
-    public MethodHandlesTest() {
-    }
-
     String testName;
     static int allPosTests, allNegTests;
     int posTests, negTests;
+
     @After
     public void printCounts() {
         if (verbosity >= 2 && (posTests | negTests) != 0) {
@@ -120,11 +91,14 @@
             posTests = negTests = 0;
         }
     }
+
     void countTest(boolean positive) {
         if (positive) ++posTests;
         else          ++negTests;
     }
+
     void countTest() { countTest(true); }
+
     void startTest(String name) {
         if (testName != null)  printCounts();
         if (verbosity >= 1)
@@ -151,14 +125,17 @@
     }
 
     static List<Object> calledLog = new ArrayList<>();
+
     static Object logEntry(String name, Object... args) {
         return Arrays.asList(name, Arrays.asList(args));
     }
+
     public static Object called(String name, Object... args) {
         Object entry = logEntry(name, args);
         calledLog.add(entry);
         return entry;
     }
+
     static void assertCalled(String name, Object... args) {
         Object expected = logEntry(name, args);
         Object actual   = calledLog.get(calledLog.size() - 1);
@@ -170,10 +147,12 @@
         System.out.println("act. types: "+getClasses(actual));
         assertEquals("previous method call", expected, actual);
     }
+
     static void printCalled(MethodHandle target, String name, Object... args) {
         if (verbosity >= 3)
             System.out.println("calling MH="+target+" to "+name+deepToString(args));
     }
+
     static String deepToString(Object x) {
         if (x == null)  return "null";
         if (x instanceof Collection)
@@ -228,6 +207,7 @@
                      TEN_BILLION = (10*1000*1000*1000),  // scale factor to reach upper 32 bits
                      INITIAL_ARG_VAL = ONE_MILLION << 1;  // <<1 makes space for sign bit;
     static long nextArgVal;
+
     static long nextArg(boolean moreBits) {
         long val = nextArgVal++;
         long sign = -(val & 1); // alternate signs
@@ -240,11 +220,13 @@
             val += (val % ONE_MILLION) * TEN_BILLION;
         return val ^ sign;
     }
+
     static int nextArg() {
         // Produce a 32-bit result something like ONE_MILLION+(smallint).
         // Example: 1_000_042.
         return (int) nextArg(false);
     }
+
     static long nextArg(Class<?> kind) {
         if (kind == long.class   || kind == Long.class ||
             kind == double.class || kind == Double.class)
@@ -260,12 +242,12 @@
         if (wrap != null) {
             return wrap;
         }
-//        import sun.invoke.util.Wrapper;
-//        Wrapper wrap = Wrapper.forBasicType(dst);
-//        if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst))
-//            wrap = Wrapper.forWrapperType(dst);
-//        if (wrap != Wrapper.OBJECT)
-//            return wrap.wrap(nextArg++);
+        //import sun.invoke.util.Wrapper;
+        //Wrapper wrap = Wrapper.forBasicType(dst);
+        //if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst))
+        //   wrap = Wrapper.forWrapperType(dst);
+        //   if (wrap != Wrapper.OBJECT)
+        //       return wrap.wrap(nextArg++);
         if (param.isInterface()) {
             for (Class<?> c : param.getClasses()) {
                 if (param.isAssignableFrom(c) && !c.isInterface())
@@ -289,18 +271,21 @@
             }
         return null;  // random class not Object, String, Integer, etc.
     }
+
     static Object[] randomArgs(Class<?>... params) {
         Object[] args = new Object[params.length];
         for (int i = 0; i < args.length; i++)
             args[i] = randomArg(params[i]);
         return args;
     }
+
     static Object[] randomArgs(int nargs, Class<?> param) {
         Object[] args = new Object[nargs];
         for (int i = 0; i < args.length; i++)
             args[i] = randomArg(param);
         return args;
     }
+
     static Object[] randomArgs(List<Class<?>> params) {
         return randomArgs(params.toArray(new Class<?>[params.size()]));
     }
@@ -309,6 +294,7 @@
     static <T, E extends T> T[] array(Class<T[]> atype, E... a) {
         return Arrays.copyOf(a, a.length, atype);
     }
+
     @SafeVarargs @SuppressWarnings("varargs")
     static <T> T[] cat(T[] a, T... b) {
         int alen = a.length, blen = b.length;
@@ -317,6 +303,7 @@
         System.arraycopy(b, 0, c, alen, blen);
         return c;
     }
+
     static Integer[] boxAll(int... vx) {
         Integer[] res = new Integer[vx.length];
         for (int i = 0; i < res.length; i++) {
@@ -324,6 +311,7 @@
         }
         return res;
     }
+
     static Object getClasses(Object x) {
         if (x == null)  return x;
         if (x instanceof String)  return x;  // keep the name
@@ -341,13 +329,16 @@
     static MethodHandle varargsList(int arity) {
         return ValueConversions.varargsList(arity);
     }
+
     /** Return lambda(arg...[arity]) { Arrays.asList(arg...) } */
     static MethodHandle varargsArray(int arity) {
         return ValueConversions.varargsArray(arity);
     }
+
     static MethodHandle varargsArray(Class<?> arrayType, int arity) {
         return ValueConversions.varargsArray(arrayType, arity);
     }
+
     /** Variation of varargsList, but with the given rtype. */
     static MethodHandle varargsList(int arity, Class<?> rtype) {
         MethodHandle list = varargsList(arity);
@@ -374,11 +365,13 @@
         }
         return list.asType(listType);
     }
+
     /** Variation of varargsList, but with the given ptypes and rtype. */
     static MethodHandle varargsList(List<Class<?>> ptypes, Class<?> rtype) {
         MethodHandle list = varargsList(ptypes.size(), rtype);
         return list.asType(MethodType.methodType(rtype, ptypes));
     }
+
     private static MethodHandle LIST_TO_STRING, LIST_TO_INT;
     private static String listToString(List<?> x) { return x.toString(); }
     private static int listToInt(List<?> x) { return x.toString().hashCode(); }
@@ -386,6 +379,7 @@
     static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) {
         return changeArgTypes(target, 0, 999, argType);
     }
+
     static MethodHandle changeArgTypes(MethodHandle target,
             int beg, int end, Class<?> argType) {
         MethodType targetType = target.type();
@@ -395,6 +389,7 @@
         MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes);
         return target.asType(ttype2);
     }
+
     static MethodHandle addTrailingArgs(MethodHandle target, int nargs, Class<?> argClass) {
         int targetLen = target.type().parameterCount();
         int extra = (nargs - targetLen);
@@ -456,6 +451,7 @@
 
         static final Lookup EXAMPLE = MethodHandles.lookup();  // for testing findSpecial
     }
+
     static final Lookup EXAMPLE = Example.EXAMPLE;
     public static class PubExample extends Example {
         public PubExample() { this("PubExample"); }
@@ -463,6 +459,7 @@
         protected void         pro_v0() { called("Pub/pro_v0", this); }
         protected static void  pro_s0() { called("Pub/pro_s0"); }
     }
+
     static class SubExample extends Example {
         @Override public void  v0()     { called("Sub/v0", this); }
         @Override void         pkg_v0() { called("Sub/pkg_v0", this); }
@@ -470,6 +467,7 @@
         private      SubExample(int x)  { called("<init>", this, x); }
         public SubExample() { super("SubExample#"+nextArg()); }
     }
+
     public static interface IntExample {
         public void            v0();
         public default void    vd() { called("vd", this); }
@@ -480,14 +478,15 @@
             @Override public String toString() { return name; }
         }
     }
+
     static interface SubIntExample extends IntExample { }
 
     static final Object[][][] ACCESS_CASES = {
         { { false, PUBLIC }, { false, SUBCLASS }, { false, PACKAGE }, { false, PRIVATE }, { false, EXAMPLE } }, //[0]: all false
-        { { false, PUBLIC }, { false, SUBCLASS }, { false, PACKAGE }, { true,  PRIVATE }, { true,  EXAMPLE } }, //[1]: only PRIVATE
-        { { false, PUBLIC }, { false, SUBCLASS }, { true,  PACKAGE }, { true,  PRIVATE }, { true,  EXAMPLE } }, //[2]: PUBLIC false
-        { { false, PUBLIC }, { true,  SUBCLASS }, { true,  PACKAGE }, { true,  PRIVATE }, { true,  EXAMPLE } }, //[3]: subclass OK
-        { { true,  PUBLIC }, { true,  SUBCLASS }, { true,  PACKAGE }, { true,  PRIVATE }, { true,  EXAMPLE } }, //[4]: all true
+        { { false, PUBLIC }, { false, SUBCLASS }, { false, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[1]: only PRIVATE
+        { { false, PUBLIC }, { false, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[2]: PUBLIC false
+        { { false, PUBLIC }, { true, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[3]: subclass OK
+        { { true, PUBLIC }, { true, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[4]: all true
     };
 
     static Object[][] accessCases(Class<?> defc, String name, boolean isSpecial) {
@@ -510,6 +509,7 @@
             cases = Arrays.copyOfRange(cases, 0, cases.length-1);
         return cases;
     }
+
     static Object[][] accessCases(Class<?> defc, String name) {
         return accessCases(defc, name, false);
     }
@@ -524,77 +524,6 @@
     /** Is findVirtual (etc.) of "&lt;init&lt;" supposed to elicit a NoSuchMethodException? */
     static final boolean INIT_REF_CAUSES_NSME = true;
 
-    @Test
-    public void testFindStatic() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFindStatic0);
-    }
-
-    public void testFindStatic0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("findStatic");
-        testFindStatic(PubExample.class, void.class, "s0");
-        testFindStatic(Example.class, void.class, "s0");
-        testFindStatic(Example.class, void.class, "pkg_s0");
-        testFindStatic(Example.class, void.class, "pri_s0");
-        testFindStatic(Example.class, void.class, "pro_s0");
-        testFindStatic(PubExample.class, void.class, "Pub/pro_s0");
-
-        testFindStatic(Example.class, Object.class, "s1", Object.class);
-        testFindStatic(Example.class, Object.class, "s2", int.class);
-        testFindStatic(Example.class, Object.class, "s3", long.class);
-        testFindStatic(Example.class, Object.class, "s4", int.class, int.class);
-        testFindStatic(Example.class, Object.class, "s5", long.class, int.class);
-        testFindStatic(Example.class, Object.class, "s6", int.class, long.class);
-        testFindStatic(Example.class, Object.class, "s7", float.class, double.class);
-
-        testFindStatic(false, PRIVATE, Example.class, void.class, "bogus");
-        testFindStatic(false, PRIVATE, Example.class, void.class, "<init>", int.class);
-        testFindStatic(false, PRIVATE, Example.class, void.class, "<init>", Void.class);
-        testFindStatic(false, PRIVATE, Example.class, void.class, "v0");
-    }
-
-    void testFindStatic(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        for (Object[] ac : accessCases(defc, name)) {
-            testFindStatic((Boolean)ac[0], (Lookup)ac[1], defc, ret, name, params);
-        }
-    }
-    void testFindStatic(Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        testFindStatic(true, lookup, defc, ret, name, params);
-    }
-    void testFindStatic(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        countTest(positive);
-        String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
-        MethodType type = MethodType.methodType(ret, params);
-        MethodHandle target = null;
-        Exception noAccess = null;
-        try {
-            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
-            target = maybeMoveIn(lookup, defc).findStatic(defc, methodName, type);
-        } catch (ReflectiveOperationException ex) {
-            noAccess = ex;
-            assertExceptionClass(
-                (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
-                ?   NoSuchMethodException.class
-                :   IllegalAccessException.class,
-                noAccess);
-            if (verbosity >= 5)  ex.printStackTrace(System.out);
-        }
-        if (verbosity >= 3)
-            System.out.println("findStatic "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
-                    +(noAccess == null ? "" : " !! "+noAccess));
-        if (positive && noAccess != null)  throw noAccess;
-        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
-        if (!positive)  return; // negative test failed as expected
-        assertEquals(type, target.type());
-        assertNameStringContains(target, methodName);
-        Object[] args = randomArgs(params);
-        printCalled(target, name, args);
-        target.invokeWithArguments(args);
-        assertCalled(name, args);
-        if (verbosity >= 1)
-            System.out.print(':');
-    }
-
     static void assertExceptionClass(Class<? extends Throwable> expected,
                                      Throwable actual) {
         if (expected.isInstance(actual))  return;
@@ -615,454 +544,6 @@
         assertEquals(s, x);
     }
 
-    @Test
-    public void testFindVirtual() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFindVirtual0);
-    }
-
-    public void testFindVirtual0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("findVirtual");
-        testFindVirtual(Example.class, void.class, "v0");
-        testFindVirtual(Example.class, void.class, "pkg_v0");
-        testFindVirtual(Example.class, void.class, "pri_v0");
-        testFindVirtual(Example.class, Object.class, "v1", Object.class);
-        testFindVirtual(Example.class, Object.class, "v2", Object.class, Object.class);
-        testFindVirtual(Example.class, Object.class, "v2", Object.class, int.class);
-        testFindVirtual(Example.class, Object.class, "v2", int.class, Object.class);
-        testFindVirtual(Example.class, Object.class, "v2", int.class, int.class);
-        testFindVirtual(Example.class, void.class, "pro_v0");
-        testFindVirtual(PubExample.class, void.class, "Pub/pro_v0");
-
-        testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "bogus");
-        testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "<init>", int.class);
-        testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "<init>", Void.class);
-        testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "s0");
-
-        // test dispatch
-        testFindVirtual(SubExample.class,      SubExample.class, void.class, "Sub/v0");
-        testFindVirtual(SubExample.class,         Example.class, void.class, "Sub/v0");
-        testFindVirtual(SubExample.class,      IntExample.class, void.class, "Sub/v0");
-        testFindVirtual(SubExample.class,      SubExample.class, void.class, "Sub/pkg_v0");
-        testFindVirtual(SubExample.class,         Example.class, void.class, "Sub/pkg_v0");
-        testFindVirtual(Example.class,         IntExample.class, void.class, "v0");
-        testFindVirtual(IntExample.Impl.class, IntExample.class, void.class, "Int/v0");
-    }
-
-    @Test
-    public void testFindVirtualClone() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFindVirtualClone0);
-    }
-
-    public void testFindVirtualClone0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        // test some ad hoc system methods
-        testFindVirtual(false, PUBLIC, Object.class, Object.class, "clone");
-
-        // ##### FIXME - disable tests for clone until we figure out how they should work with modules
-
-        /*
-        testFindVirtual(true, PUBLIC, Object[].class, Object.class, "clone");
-        testFindVirtual(true, PUBLIC, int[].class, Object.class, "clone");
-        for (Class<?> cls : new Class<?>[]{ boolean[].class, long[].class, float[].class, char[].class })
-            testFindVirtual(true, PUBLIC, cls, Object.class, "clone");
-         */
-    }
-
-    void testFindVirtual(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        Class<?> rcvc = defc;
-        testFindVirtual(rcvc, defc, ret, name, params);
-    }
-    void testFindVirtual(Class<?> rcvc, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        for (Object[] ac : accessCases(defc, name)) {
-            testFindVirtual((Boolean)ac[0], (Lookup)ac[1], rcvc, defc, ret, name, params);
-        }
-    }
-    void testFindVirtual(Lookup lookup, Class<?> rcvc, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        testFindVirtual(true, lookup, rcvc, defc, ret, name, params);
-    }
-    void testFindVirtual(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        testFindVirtual(positive, lookup, defc, defc, ret, name, params);
-    }
-    void testFindVirtual(boolean positive, Lookup lookup, Class<?> rcvc, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        countTest(positive);
-        String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
-        MethodType type = MethodType.methodType(ret, params);
-        MethodHandle target = null;
-        Exception noAccess = null;
-        try {
-            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
-            target = maybeMoveIn(lookup, defc).findVirtual(defc, methodName, type);
-        } catch (ReflectiveOperationException ex) {
-            noAccess = ex;
-            assertExceptionClass(
-                (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
-                ?   NoSuchMethodException.class
-                :   IllegalAccessException.class,
-                noAccess);
-            if (verbosity >= 5)  ex.printStackTrace(System.out);
-        }
-        if (verbosity >= 3)
-            System.out.println("findVirtual "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
-                    +(noAccess == null ? "" : " !! "+noAccess));
-        if (positive && noAccess != null)  throw noAccess;
-        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
-        if (!positive)  return; // negative test failed as expected
-        Class<?> selfc = defc;
-        // predict receiver type narrowing:
-        if (lookup == SUBCLASS &&
-                name.contains("pro_") &&
-                selfc.isAssignableFrom(lookup.lookupClass())) {
-            selfc = lookup.lookupClass();
-            if (name.startsWith("Pub/"))  name = "Rem/"+name.substring(4);
-        }
-        Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)selfc), params);
-        MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
-        assertEquals(typeWithSelf, target.type());
-        assertNameStringContains(target, methodName);
-        Object[] argsWithSelf = randomArgs(paramsWithSelf);
-        if (selfc.isAssignableFrom(rcvc) && rcvc != selfc)  argsWithSelf[0] = randomArg(rcvc);
-        printCalled(target, name, argsWithSelf);
-        Object res = target.invokeWithArguments(argsWithSelf);
-        if (Example.class.isAssignableFrom(defc) || IntExample.class.isAssignableFrom(defc)) {
-            assertCalled(name, argsWithSelf);
-        } else if (name.equals("clone")) {
-            // Ad hoc method call outside Example.  For Object[].clone.
-            printCalled(target, name, argsWithSelf);
-            assertEquals(MethodType.methodType(Object.class, rcvc), target.type());
-            Object orig = argsWithSelf[0];
-            assertEquals(orig.getClass(), res.getClass());
-            if (res instanceof Object[])
-                assertArrayEquals((Object[])res, (Object[])argsWithSelf[0]);
-            assert(Arrays.deepEquals(new Object[]{res}, new Object[]{argsWithSelf[0]}));
-        } else {
-            assert(false) : Arrays.asList(positive, lookup, rcvc, defc, ret, name, deepToString(params));
-        }
-        if (verbosity >= 1)
-            System.out.print(':');
-    }
-
-    @Test
-    public void testFindSpecial() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFindSpecial0);
-    }
-
-    public void testFindSpecial0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("findSpecial");
-        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");
-            testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus");
-            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, 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);
-            testFindSpecial(false, PACKAGE,  specialCaller, defc, ret, name, params);
-            testFindSpecial(true,  SUBCLASS, specialCaller, defc, ret, name, params);
-            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 || 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 {
-        countTest(positive);
-        String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
-        MethodType type = MethodType.methodType(ret, params);
-        Lookup specialLookup = maybeMoveIn(lookup, specialCaller);
-        boolean specialAccessOK = (specialLookup.lookupClass() == specialCaller &&
-                                   (specialLookup.lookupModes() & Lookup.PRIVATE) != 0);
-        MethodHandle target = null;
-        Exception noAccess = null;
-        try {
-            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
-            if (verbosity >= 5)  System.out.println("  lookup => "+specialLookup);
-            target = specialLookup.findSpecial(defc, methodName, type, specialCaller);
-        } catch (ReflectiveOperationException ex) {
-            noAccess = ex;
-            assertExceptionClass(
-                (!specialAccessOK)  // this check should happen first
-                ?   IllegalAccessException.class
-                : (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
-                ?   NoSuchMethodException.class
-                : IllegalAccessException.class,
-                noAccess);
-            if (verbosity >= 5)  ex.printStackTrace(System.out);
-        }
-        if (verbosity >= 3)
-            System.out.println("findSpecial from "+specialCaller.getName()+" to "+defc.getName()+"."+name+"/"+type+" => "+target
-                               +(target == null ? "" : target.type())
-                               +(noAccess == null ? "" : " !! "+noAccess));
-        if (positive && noAccess != null)  throw noAccess;
-        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
-        if (!positive)  return; // negative test failed as expected
-        assertEquals(specialCaller, target.type().parameterType(0));
-        assertEquals(type,          target.type().dropParameterTypes(0,1));
-        Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)specialCaller), params);
-        MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
-        assertNameStringContains(target, methodName);
-        Object[] args = randomArgs(paramsWithSelf);
-        printCalled(target, name, args);
-        target.invokeWithArguments(args);
-        assertCalled(name, args);
-    }
-
-    @Test
-    public void testFindConstructor() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFindConstructor0);
-    }
-
-    public void testFindConstructor0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("findConstructor");
-        testFindConstructor(true, EXAMPLE, Example.class);
-        testFindConstructor(true, EXAMPLE, Example.class, int.class);
-        testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class);
-        testFindConstructor(true, EXAMPLE, Example.class, int.class, long.class);
-        testFindConstructor(true, EXAMPLE, Example.class, int.class, float.class);
-        testFindConstructor(true, EXAMPLE, Example.class, int.class, double.class);
-        testFindConstructor(true, EXAMPLE, Example.class, String.class);
-        testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class, int.class);
-        testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class, int.class, int.class);
-    }
-    void testFindConstructor(boolean positive, Lookup lookup,
-                             Class<?> defc, Class<?>... params) throws Throwable {
-        countTest(positive);
-        MethodType type = MethodType.methodType(void.class, params);
-        MethodHandle target = null;
-        Exception noAccess = null;
-        try {
-            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" <init>"+type);
-            target = lookup.findConstructor(defc, type);
-        } catch (ReflectiveOperationException ex) {
-            noAccess = ex;
-            assertTrue(noAccess.getClass().getName(), noAccess instanceof IllegalAccessException);
-        }
-        if (verbosity >= 3)
-            System.out.println("findConstructor "+defc.getName()+".<init>/"+type+" => "+target
-                               +(target == null ? "" : target.type())
-                               +(noAccess == null ? "" : " !! "+noAccess));
-        if (positive && noAccess != null)  throw noAccess;
-        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
-        if (!positive)  return; // negative test failed as expected
-        assertEquals(type.changeReturnType(defc), target.type());
-        Object[] args = randomArgs(params);
-        printCalled(target, defc.getSimpleName(), args);
-        Object obj = target.invokeWithArguments(args);
-        if (!(defc == Example.class && params.length < 2))
-            assertCalled(defc.getSimpleName()+".<init>", args);
-        assertTrue("instance of "+defc.getName(), defc.isInstance(obj));
-    }
-
-    @Test
-    public void testBind() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testBind0);
-    }
-
-    public void testBind0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("bind");
-        testBind(Example.class, void.class, "v0");
-        testBind(Example.class, void.class, "pkg_v0");
-        testBind(Example.class, void.class, "pri_v0");
-        testBind(Example.class, Object.class, "v1", Object.class);
-        testBind(Example.class, Object.class, "v2", Object.class, Object.class);
-        testBind(Example.class, Object.class, "v2", Object.class, int.class);
-        testBind(Example.class, Object.class, "v2", int.class, Object.class);
-        testBind(Example.class, Object.class, "v2", int.class, int.class);
-        testBind(false, PRIVATE, Example.class, void.class, "bogus");
-        testBind(false, PRIVATE, Example.class, void.class, "<init>", int.class);
-        testBind(false, PRIVATE, Example.class, void.class, "<init>", Void.class);
-        testBind(SubExample.class, void.class, "Sub/v0");
-        testBind(SubExample.class, void.class, "Sub/pkg_v0");
-        testBind(IntExample.Impl.class, void.class, "Int/v0");
-    }
-
-    void testBind(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        for (Object[] ac : accessCases(defc, name)) {
-            testBind((Boolean)ac[0], (Lookup)ac[1], defc, ret, name, params);
-        }
-    }
-
-    void testBind(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        countTest(positive);
-        String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
-        MethodType type = MethodType.methodType(ret, params);
-        Object receiver = randomArg(defc);
-        MethodHandle target = null;
-        Exception noAccess = null;
-        try {
-            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
-            target = maybeMoveIn(lookup, defc).bind(receiver, methodName, type);
-        } catch (ReflectiveOperationException ex) {
-            noAccess = ex;
-            assertExceptionClass(
-                (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
-                ?   NoSuchMethodException.class
-                :   IllegalAccessException.class,
-                noAccess);
-            if (verbosity >= 5)  ex.printStackTrace(System.out);
-        }
-        if (verbosity >= 3)
-            System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target
-                    +(noAccess == null ? "" : " !! "+noAccess));
-        if (positive && noAccess != null)  throw noAccess;
-        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
-        if (!positive)  return; // negative test failed as expected
-        assertEquals(type, target.type());
-        Object[] args = randomArgs(params);
-        printCalled(target, name, args);
-        target.invokeWithArguments(args);
-        Object[] argsWithReceiver = cat(array(Object[].class, receiver), args);
-        assertCalled(name, argsWithReceiver);
-        if (verbosity >= 1)
-            System.out.print(':');
-    }
-
-    @Test
-    public void testUnreflect() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testUnreflect0);
-    }
-
-    public void testUnreflect0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("unreflect");
-        testUnreflect(Example.class, true, void.class, "s0");
-        testUnreflect(Example.class, true, void.class, "pro_s0");
-        testUnreflect(Example.class, true, void.class, "pkg_s0");
-        testUnreflect(Example.class, true, void.class, "pri_s0");
-
-        testUnreflect(Example.class, true, Object.class, "s1", Object.class);
-        testUnreflect(Example.class, true, Object.class, "s2", int.class);
-        testUnreflect(Example.class, true, Object.class, "s3", long.class);
-        testUnreflect(Example.class, true, Object.class, "s4", int.class, int.class);
-        testUnreflect(Example.class, true, Object.class, "s5", long.class, int.class);
-        testUnreflect(Example.class, true, Object.class, "s6", int.class, long.class);
-
-        testUnreflect(Example.class, false, void.class, "v0");
-        testUnreflect(Example.class, false, void.class, "pkg_v0");
-        testUnreflect(Example.class, false, void.class, "pri_v0");
-        testUnreflect(Example.class, false, Object.class, "v1", Object.class);
-        testUnreflect(Example.class, false, Object.class, "v2", Object.class, Object.class);
-        testUnreflect(Example.class, false, Object.class, "v2", Object.class, int.class);
-        testUnreflect(Example.class, false, Object.class, "v2", int.class, Object.class);
-        testUnreflect(Example.class, false, Object.class, "v2", int.class, int.class);
-
-        // Test a public final member in another package:
-        testUnreflect(RemoteExample.class, false, void.class, "Rem/fin_v0");
-    }
-
-    void testUnreflect(Class<?> defc, boolean isStatic, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        for (Object[] ac : accessCases(defc, name)) {
-            testUnreflectMaybeSpecial(null, (Boolean)ac[0], (Lookup)ac[1], defc, (isStatic ? null : defc), ret, name, params);
-        }
-    }
-    void testUnreflect(Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        for (Object[] ac : accessCases(defc, name)) {
-            testUnreflectMaybeSpecial(null, (Boolean)ac[0], (Lookup)ac[1], defc, rcvc, ret, name, params);
-        }
-    }
-    void testUnreflectMaybeSpecial(Class<?> specialCaller,
-                                   boolean positive, Lookup lookup,
-                                   Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        countTest(positive);
-        String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
-        MethodType type = MethodType.methodType(ret, params);
-        Lookup specialLookup = (specialCaller != null ? maybeMoveIn(lookup, specialCaller) : null);
-        boolean specialAccessOK = (specialCaller != null &&
-                                   specialLookup.lookupClass() == specialCaller &&
-                                   (specialLookup.lookupModes() & Lookup.PRIVATE) != 0);
-        Method rmethod = defc.getDeclaredMethod(methodName, params);
-        MethodHandle target = null;
-        Exception noAccess = null;
-        boolean isStatic = (rcvc == null);
-        boolean isSpecial = (specialCaller != null);
-        try {
-            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
-            if (isSpecial)
-                target = specialLookup.unreflectSpecial(rmethod, specialCaller);
-            else
-                target = maybeMoveIn(lookup, defc).unreflect(rmethod);
-        } catch (ReflectiveOperationException ex) {
-            noAccess = ex;
-            assertExceptionClass(
-                IllegalAccessException.class,  // NSME is impossible, since it was already reflected
-                noAccess);
-            if (verbosity >= 5)  ex.printStackTrace(System.out);
-        }
-        if (verbosity >= 3)
-            System.out.println("unreflect"+(isSpecial?"Special":"")+" "+defc.getName()+"."+name+"/"+type
-                               +(!isSpecial ? "" : " specialCaller="+specialCaller)
-                               +( isStatic  ? "" : " receiver="+rcvc)
-                               +" => "+target
-                               +(noAccess == null ? "" : " !! "+noAccess));
-        if (positive && noAccess != null)  throw noAccess;
-        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
-        if (!positive)  return; // negative test failed as expected
-        assertEquals(isStatic, Modifier.isStatic(rmethod.getModifiers()));
-        Class<?>[] paramsMaybeWithSelf = params;
-        if (!isStatic) {
-            paramsMaybeWithSelf = cat(array(Class[].class, (Class)rcvc), params);
-        }
-        MethodType typeMaybeWithSelf = MethodType.methodType(ret, paramsMaybeWithSelf);
-        if (isStatic) {
-            assertEquals(typeMaybeWithSelf, target.type());
-        } else {
-            if (isSpecial)
-                assertEquals(specialCaller, target.type().parameterType(0));
-            else
-                assertEquals(defc, target.type().parameterType(0));
-            assertEquals(typeMaybeWithSelf, target.type().changeParameterType(0, rcvc));
-        }
-        Object[] argsMaybeWithSelf = randomArgs(paramsMaybeWithSelf);
-        printCalled(target, name, argsMaybeWithSelf);
-        target.invokeWithArguments(argsMaybeWithSelf);
-        assertCalled(name, argsMaybeWithSelf);
-        if (verbosity >= 1)
-            System.out.print(':');
-    }
-
-    void testUnreflectSpecial(Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        for (Object[] ac : accessCases(defc, name, true)) {
-            Class<?> specialCaller = rcvc;
-            testUnreflectMaybeSpecial(specialCaller, (Boolean)ac[0], (Lookup)ac[1], defc, rcvc, ret, name, params);
-        }
-    }
-
-    @Test
-    public void testUnreflectSpecial() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testUnreflectSpecial0);
-    }
-
-    public void testUnreflectSpecial0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("unreflectSpecial");
-        testUnreflectSpecial(Example.class,    Example.class, void.class, "v0");
-        testUnreflectSpecial(Example.class, SubExample.class, void.class, "v0");
-        testUnreflectSpecial(Example.class,    Example.class, void.class, "pkg_v0");
-        testUnreflectSpecial(Example.class, SubExample.class, void.class, "pkg_v0");
-        testUnreflectSpecial(Example.class,    Example.class, Object.class, "v2", int.class, int.class);
-        testUnreflectSpecial(Example.class, SubExample.class, Object.class, "v2", int.class, int.class);
-        testUnreflectMaybeSpecial(Example.class, false, PRIVATE, Example.class, Example.class, void.class, "s0");
-    }
-
     public static class HasFields {
         boolean fZ = false;
         byte fB = (byte)'B';
@@ -1133,6 +614,7 @@
     }
 
     static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10, TEST_BOUND = 0x20, TEST_NPE = 0x40;
+
     static boolean testModeMatches(int testMode, boolean isStatic) {
         switch (testMode) {
         case TEST_FIND_STATIC:          return isStatic;
@@ -1142,467 +624,6 @@
         throw new InternalError("testMode="+testMode);
     }
 
-    @Test
-    public void testUnreflectGetter() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testUnreflectGetter0);
-    }
-
-    public void testUnreflectGetter0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("unreflectGetter");
-        testGetter(TEST_UNREFLECT);
-    }
-
-    @Test
-    public void testFindGetter() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFindGetter0);
-    }
-
-    public void testFindGetter0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("findGetter");
-        testGetter(TEST_FIND_FIELD);
-        testGetter(TEST_FIND_FIELD | TEST_BOUND);
-    }
-
-    @Test
-    public void testFindStaticGetter() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFindStaticGetter0);
-    }
-
-    public void testFindStaticGetter0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("findStaticGetter");
-        testGetter(TEST_FIND_STATIC);
-    }
-
-    public void testGetter(int testMode) throws Throwable {
-        Lookup lookup = PRIVATE;  // FIXME: test more lookups than this one
-        for (Object[] c : HasFields.CASES) {
-            boolean positive = (c[1] != Error.class);
-            testGetter(positive, lookup, c[0], c[1], testMode);
-            if (positive)
-                testGetter(positive, lookup, c[0], c[1], testMode | TEST_NPE);
-        }
-        testGetter(true, lookup,
-                   new Object[]{ true,  System.class, "out", java.io.PrintStream.class },
-                   System.out, testMode);
-        for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
-            testGetter(false, lookup,
-                       new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
-                       null, testMode);
-        }
-    }
-    public void testGetter(boolean positive, MethodHandles.Lookup lookup,
-                           Object fieldRef, Object value, int testMode) throws Throwable {
-        testAccessor(positive, lookup, fieldRef, value, testMode);
-    }
-
-    public void testAccessor(boolean positive0, MethodHandles.Lookup lookup,
-                             Object fieldRef, Object value, int testMode0) throws Throwable {
-        if (verbosity >= 4)
-            System.out.println("testAccessor"+Arrays.deepToString(new Object[]{positive0, lookup, fieldRef, value, testMode0}));
-        boolean isGetter = ((testMode0 & TEST_SETTER) == 0);
-        boolean doBound  = ((testMode0 & TEST_BOUND) != 0);
-        boolean testNPE  = ((testMode0 & TEST_NPE) != 0);
-        int testMode = testMode0 & ~(TEST_SETTER | TEST_BOUND | TEST_NPE);
-        boolean positive = positive0 && !testNPE;
-        boolean isStatic;
-        Class<?> fclass;
-        String   fname;
-        Class<?> ftype;
-        Field f = (fieldRef instanceof Field ? (Field)fieldRef : null);
-        if (f != null) {
-            isStatic = Modifier.isStatic(f.getModifiers());
-            fclass   = f.getDeclaringClass();
-            fname    = f.getName();
-            ftype    = f.getType();
-        } else {
-            Object[] scnt = (Object[]) fieldRef;
-            isStatic = (Boolean)  scnt[0];
-            fclass   = (Class<?>) scnt[1];
-            fname    = (String)   scnt[2];
-            ftype    = (Class<?>) scnt[3];
-            try {
-                f = fclass.getDeclaredField(fname);
-            } catch (ReflectiveOperationException ex) {
-                f = null;
-            }
-        }
-        if (!testModeMatches(testMode, isStatic))  return;
-        if (f == null && testMode == TEST_UNREFLECT)  return;
-        if (testNPE && isStatic)  return;
-        countTest(positive);
-        MethodType expType;
-        if (isGetter)
-            expType = MethodType.methodType(ftype, HasFields.class);
-        else
-            expType = MethodType.methodType(void.class, HasFields.class, ftype);
-        if (isStatic)  expType = expType.dropParameterTypes(0, 1);
-        Exception noAccess = null;
-        MethodHandle mh;
-        try {
-            switch (testMode0 & ~(TEST_BOUND | TEST_NPE)) {
-            case TEST_UNREFLECT:   mh = lookup.unreflectGetter(f);                      break;
-            case TEST_FIND_FIELD:  mh = lookup.findGetter(fclass, fname, ftype);        break;
-            case TEST_FIND_STATIC: mh = lookup.findStaticGetter(fclass, fname, ftype);  break;
-            case TEST_SETTER|
-                 TEST_UNREFLECT:   mh = lookup.unreflectSetter(f);                      break;
-            case TEST_SETTER|
-                 TEST_FIND_FIELD:  mh = lookup.findSetter(fclass, fname, ftype);        break;
-            case TEST_SETTER|
-                 TEST_FIND_STATIC: mh = lookup.findStaticSetter(fclass, fname, ftype);  break;
-            default:
-                throw new InternalError("testMode="+testMode);
-            }
-        } catch (ReflectiveOperationException ex) {
-            mh = null;
-            noAccess = ex;
-            assertExceptionClass(
-                (fname.contains("bogus"))
-                ?   NoSuchFieldException.class
-                :   IllegalAccessException.class,
-                noAccess);
-            if (verbosity >= 5)  ex.printStackTrace(System.out);
-        }
-        if (verbosity >= 3)
-            System.out.println("find"+(isStatic?"Static":"")+(isGetter?"Getter":"Setter")+" "+fclass.getName()+"."+fname+"/"+ftype
-                               +" => "+mh
-                               +(noAccess == null ? "" : " !! "+noAccess));
-        if (positive && !testNPE && noAccess != null)  throw new RuntimeException(noAccess);
-        assertEquals(positive0 ? "positive test" : "negative test erroneously passed", positive0, mh != null);
-        if (!positive && !testNPE)  return; // negative access test failed as expected
-        assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount());
-
-
-        assertSame(mh.type(), expType);
-        //assertNameStringContains(mh, fname);  // This does not hold anymore with LFs
-        HasFields fields = new HasFields();
-        HasFields fieldsForMH = fields;
-        if (testNPE)  fieldsForMH = null;  // perturb MH argument to elicit expected error
-        if (doBound)
-            mh = mh.bindTo(fieldsForMH);
-        Object sawValue;
-        Class<?> vtype = ftype;
-        if (ftype != int.class)  vtype = Object.class;
-        if (isGetter) {
-            mh = mh.asType(mh.type().generic()
-                           .changeReturnType(vtype));
-        } else {
-            int last = mh.type().parameterCount() - 1;
-            mh = mh.asType(mh.type().generic()
-                           .changeReturnType(void.class)
-                           .changeParameterType(last, vtype));
-        }
-        if (f != null && f.getDeclaringClass() == HasFields.class) {
-            assertEquals(f.get(fields), value);  // clean to start with
-        }
-        Throwable caughtEx = null;
-        if (isGetter) {
-            Object expValue = value;
-            for (int i = 0; i <= 1; i++) {
-                sawValue = null;  // make DA rules happy under try/catch
-                try {
-                    if (isStatic || doBound) {
-                        if (ftype == int.class)
-                            sawValue = (int) mh.invokeExact();  // do these exactly
-                        else
-                            sawValue = mh.invokeExact();
-                    } else {
-                        if (ftype == int.class)
-                            sawValue = (int) mh.invokeExact((Object) fieldsForMH);
-                        else
-                            sawValue = mh.invokeExact((Object) fieldsForMH);
-                    }
-                } catch (RuntimeException ex) {
-                    if (ex instanceof NullPointerException && testNPE) {
-                        caughtEx = ex;
-                        break;
-                    }
-                }
-                assertEquals(sawValue, expValue);
-                if (f != null && f.getDeclaringClass() == HasFields.class
-                    && !Modifier.isFinal(f.getModifiers())) {
-                    Object random = randomArg(ftype);
-                    f.set(fields, random);
-                    expValue = random;
-                } else {
-                    break;
-                }
-            }
-        } else {
-            for (int i = 0; i <= 1; i++) {
-                Object putValue = randomArg(ftype);
-                try {
-                    if (isStatic || doBound) {
-                        if (ftype == int.class)
-                            mh.invokeExact((int)putValue);  // do these exactly
-                        else
-                            mh.invokeExact(putValue);
-                    } else {
-                        if (ftype == int.class)
-                            mh.invokeExact((Object) fieldsForMH, (int)putValue);
-                        else
-                            mh.invokeExact((Object) fieldsForMH, putValue);
-                    }
-                } catch (RuntimeException ex) {
-                    if (ex instanceof NullPointerException && testNPE) {
-                        caughtEx = ex;
-                        break;
-                    }
-                }
-                if (f != null && f.getDeclaringClass() == HasFields.class) {
-                    assertEquals(f.get(fields), putValue);
-                }
-            }
-        }
-        if (f != null && f.getDeclaringClass() == HasFields.class) {
-            f.set(fields, value);  // put it back
-        }
-        if (testNPE) {
-            if (caughtEx == null || !(caughtEx instanceof NullPointerException))
-                throw new RuntimeException("failed to catch NPE exception"+(caughtEx == null ? " (caughtEx=null)" : ""), caughtEx);
-            caughtEx = null;  // nullify expected exception
-        }
-        if (caughtEx != null) {
-            throw new RuntimeException("unexpected exception", caughtEx);
-        }
-    }
-
-    @Test
-    public void testUnreflectSetter() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testUnreflectSetter0);
-    }
-
-    public void testUnreflectSetter0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("unreflectSetter");
-        testSetter(TEST_UNREFLECT);
-    }
-
-    @Test
-    public void testFindSetter() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFindSetter0);
-    }
-
-    public void testFindSetter0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("findSetter");
-        testSetter(TEST_FIND_FIELD);
-        testSetter(TEST_FIND_FIELD | TEST_BOUND);
-    }
-
-    @Test
-    public void testFindStaticSetter() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFindStaticSetter0);
-    }
-
-    public void testFindStaticSetter0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("findStaticSetter");
-        testSetter(TEST_FIND_STATIC);
-    }
-
-    public void testSetter(int testMode) throws Throwable {
-        Lookup lookup = PRIVATE;  // FIXME: test more lookups than this one
-        startTest("unreflectSetter");
-        for (Object[] c : HasFields.CASES) {
-            boolean positive = (c[1] != Error.class);
-            testSetter(positive, lookup, c[0], c[1], testMode);
-            if (positive)
-                testSetter(positive, lookup, c[0], c[1], testMode | TEST_NPE);
-        }
-        for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
-            testSetter(false, lookup,
-                       new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
-                       null, testMode);
-        }
-    }
-    public void testSetter(boolean positive, MethodHandles.Lookup lookup,
-                           Object fieldRef, Object value, int testMode) throws Throwable {
-        testAccessor(positive, lookup, fieldRef, value, testMode | TEST_SETTER);
-    }
-
-    @Test
-    public void testArrayElementGetter() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testArrayElementGetter0);
-    }
-
-    public void testArrayElementGetter0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("arrayElementGetter");
-        testArrayElementGetterSetter(false);
-    }
-
-    @Test
-    public void testArrayElementSetter() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testArrayElementSetter0);
-    }
-
-    public void testArrayElementSetter0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("arrayElementSetter");
-        testArrayElementGetterSetter(true);
-    }
-
-    private static final int TEST_ARRAY_NONE = 0, TEST_ARRAY_NPE = 1, TEST_ARRAY_OOB = 2, TEST_ARRAY_ASE = 3;
-
-    public void testArrayElementGetterSetter(boolean testSetter) throws Throwable {
-        testArrayElementGetterSetter(testSetter, TEST_ARRAY_NONE);
-    }
-
-    @Test
-    public void testArrayElementErrors() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testArrayElementErrors0);
-    }
-
-    public void testArrayElementErrors0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("arrayElementErrors");
-        testArrayElementGetterSetter(false, TEST_ARRAY_NPE);
-        testArrayElementGetterSetter(true, TEST_ARRAY_NPE);
-        testArrayElementGetterSetter(false, TEST_ARRAY_OOB);
-        testArrayElementGetterSetter(true, TEST_ARRAY_OOB);
-        testArrayElementGetterSetter(new Object[10], true, TEST_ARRAY_ASE);
-        testArrayElementGetterSetter(new Example[10], true, TEST_ARRAY_ASE);
-        testArrayElementGetterSetter(new IntExample[10], true, TEST_ARRAY_ASE);
-    }
-
-    public void testArrayElementGetterSetter(boolean testSetter, int negTest) throws Throwable {
-        testArrayElementGetterSetter(new String[10], testSetter, negTest);
-        testArrayElementGetterSetter(new Iterable<?>[10], testSetter, negTest);
-        testArrayElementGetterSetter(new Example[10], testSetter, negTest);
-        testArrayElementGetterSetter(new IntExample[10], testSetter, negTest);
-        testArrayElementGetterSetter(new Object[10], testSetter, negTest);
-        testArrayElementGetterSetter(new boolean[10], testSetter, negTest);
-        testArrayElementGetterSetter(new byte[10], testSetter, negTest);
-        testArrayElementGetterSetter(new char[10], testSetter, negTest);
-        testArrayElementGetterSetter(new short[10], testSetter, negTest);
-        testArrayElementGetterSetter(new int[10], testSetter, negTest);
-        testArrayElementGetterSetter(new float[10], testSetter, negTest);
-        testArrayElementGetterSetter(new long[10], testSetter, negTest);
-        testArrayElementGetterSetter(new double[10], testSetter, negTest);
-    }
-
-    public void testArrayElementGetterSetter(Object array, boolean testSetter, int negTest) throws Throwable {
-        boolean positive = (negTest == TEST_ARRAY_NONE);
-        int length = java.lang.reflect.Array.getLength(array);
-        Class<?> arrayType = array.getClass();
-        Class<?> elemType = arrayType.getComponentType();
-        Object arrayToMH = array;
-        // this stanza allows negative tests to make argument perturbations:
-        switch (negTest) {
-        case TEST_ARRAY_NPE:
-            arrayToMH = null;
-            break;
-        case TEST_ARRAY_OOB:
-            assert(length > 0);
-            arrayToMH = java.lang.reflect.Array.newInstance(elemType, 0);
-            break;
-        case TEST_ARRAY_ASE:
-            assert(testSetter && !elemType.isPrimitive());
-            if (elemType == Object.class)
-                arrayToMH = new StringBuffer[length];  // very random subclass of Object!
-            else if (elemType == Example.class)
-                arrayToMH = new SubExample[length];
-            else if (elemType == IntExample.class)
-                arrayToMH = new SubIntExample[length];
-            else
-                return;  // can't make an ArrayStoreException test
-            assert(arrayType.isInstance(arrayToMH))
-                : Arrays.asList(arrayType, arrayToMH.getClass(), testSetter, negTest);
-            break;
-        }
-        countTest(positive);
-        if (verbosity > 2)  System.out.println("array type = "+array.getClass().getComponentType().getName()+"["+length+"]"+(positive ? "" : " negative test #"+negTest+" using "+Arrays.deepToString(new Object[]{arrayToMH})));
-        MethodType expType = !testSetter
-                ? MethodType.methodType(elemType,   arrayType, int.class)
-                : MethodType.methodType(void.class, arrayType, int.class, elemType);
-        MethodHandle mh = !testSetter
-                ? MethodHandles.arrayElementGetter(arrayType)
-                : MethodHandles.arrayElementSetter(arrayType);
-        assertSame(mh.type(), expType);
-        if (elemType != int.class && elemType != boolean.class) {
-            MethodType gtype = mh.type().generic().changeParameterType(1, int.class);
-            if (testSetter)  gtype = gtype.changeReturnType(void.class);
-            mh = mh.asType(gtype);
-        }
-        Object sawValue, expValue;
-        List<Object> model = array2list(array);
-        Throwable caughtEx = null;
-        for (int i = 0; i < length; i++) {
-            // update array element
-            Object random = randomArg(elemType);
-            model.set(i, random);
-            if (testSetter) {
-                try {
-                    if (elemType == int.class)
-                        mh.invokeExact((int[]) arrayToMH, i, (int)random);
-                    else if (elemType == boolean.class)
-                        mh.invokeExact((boolean[]) arrayToMH, i, (boolean)random);
-                    else
-                        mh.invokeExact(arrayToMH, i, random);
-                } catch (RuntimeException ex) {
-                    caughtEx = ex;
-                    break;
-                }
-                assertEquals(model, array2list(array));
-            } else {
-                Array.set(array, i, random);
-            }
-            if (verbosity >= 5) {
-                List<Object> array2list = array2list(array);
-                System.out.println("a["+i+"]="+random+" => "+array2list);
-                if (!array2list.equals(model))
-                    System.out.println("***   != "+model);
-            }
-            // observe array element
-            sawValue = Array.get(array, i);
-            if (!testSetter) {
-                expValue = sawValue;
-                try {
-                    if (elemType == int.class)
-                        sawValue = (int) mh.invokeExact((int[]) arrayToMH, i);
-                    else if (elemType == boolean.class)
-                        sawValue = (boolean) mh.invokeExact((boolean[]) arrayToMH, i);
-                    else
-                        sawValue = mh.invokeExact(arrayToMH, i);
-                } catch (RuntimeException ex) {
-                    caughtEx = ex;
-                    break;
-                }
-                assertEquals(sawValue, expValue);
-                assertEquals(model, array2list(array));
-            }
-        }
-        if (!positive) {
-            if (caughtEx == null)
-                throw new RuntimeException("failed to catch exception for negTest="+negTest);
-            // test the kind of exception
-            Class<?> reqType = null;
-            switch (negTest) {
-            case TEST_ARRAY_ASE:  reqType = ArrayStoreException.class; break;
-            case TEST_ARRAY_OOB:  reqType = ArrayIndexOutOfBoundsException.class; break;
-            case TEST_ARRAY_NPE:  reqType = NullPointerException.class; break;
-            default:              assert(false);
-            }
-            if (reqType.isInstance(caughtEx)) {
-                caughtEx = null;  // nullify expected exception
-            }
-        }
-        if (caughtEx != null) {
-            throw new RuntimeException("unexpected exception", caughtEx);
-        }
-    }
-
-    List<Object> array2list(Object array) {
-        int length = Array.getLength(array);
-        ArrayList<Object> model = new ArrayList<>(length);
-        for (int i = 0; i < length; i++)
-            model.add(Array.get(array, i));
-        return model;
-    }
-
     static class Callee {
         static Object id() { return called("id"); }
         static Object id(Object x) { return called("id", x); }
@@ -1634,1252 +655,86 @@
         }
     }
 
-    @Test
-    public void testConvertArguments() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testConvertArguments0);
-    }
-
-    public void testConvertArguments0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("convertArguments");
-        testConvert(Callee.ofType(1), null, "id", int.class);
-        testConvert(Callee.ofType(1), null, "id", String.class);
-        testConvert(Callee.ofType(1), null, "id", Integer.class);
-        testConvert(Callee.ofType(1), null, "id", short.class);
-        testConvert(Callee.ofType(1), null, "id", char.class);
-        testConvert(Callee.ofType(1), null, "id", byte.class);
-    }
-
-    void testConvert(MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
-        testConvert(true, id, rtype, name, params);
-    }
-
-    void testConvert(boolean positive,
-                     MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
-        countTest(positive);
-        MethodType idType = id.type();
-        if (rtype == null)  rtype = idType.returnType();
-        for (int i = 0; i < params.length; i++) {
-            if (params[i] == null)  params[i] = idType.parameterType(i);
-        }
-        // simulate the pairwise conversion
-        MethodType newType = MethodType.methodType(rtype, params);
-        Object[] args = randomArgs(newType.parameterArray());
-        Object[] convArgs = args.clone();
-        for (int i = 0; i < args.length; i++) {
-            Class<?> src = newType.parameterType(i);
-            Class<?> dst = idType.parameterType(i);
-            if (src != dst)
-                convArgs[i] = castToWrapper(convArgs[i], dst);
-        }
-        Object convResult = id.invokeWithArguments(convArgs);
-        {
-            Class<?> dst = newType.returnType();
-            Class<?> src = idType.returnType();
-            if (src != dst)
-                convResult = castToWrapper(convResult, dst);
-        }
-        MethodHandle target = null;
-        RuntimeException error = null;
-        try {
-            target = id.asType(newType);
-        } catch (WrongMethodTypeException ex) {
-            error = ex;
-        }
-        if (verbosity >= 3)
-            System.out.println("convert "+id+ " to "+newType+" => "+target
-                    +(error == null ? "" : " !! "+error));
-        if (positive && error != null)  throw error;
-        assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
-        if (!positive)  return; // negative test failed as expected
-        assertEquals(newType, target.type());
-        printCalled(target, id.toString(), args);
-        Object result = target.invokeWithArguments(args);
-        assertCalled(name, convArgs);
-        assertEquals(convResult, result);
-        if (verbosity >= 1)
-            System.out.print(':');
-    }
-
-    @Test
-    public void testVarargsCollector() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testVarargsCollector0);
-    }
-
-    public void testVarargsCollector0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("varargsCollector");
-        MethodHandle vac0 = PRIVATE.findStatic(MethodHandlesTest.class, "called",
-                               MethodType.methodType(Object.class, String.class, Object[].class));
-        vac0 = vac0.bindTo("vac");
-        MethodHandle vac = vac0.asVarargsCollector(Object[].class);
-        testConvert(true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
-        testConvert(true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
-        for (Class<?> at : new Class<?>[] { Object.class, String.class, Integer.class }) {
-            testConvert(true, vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
-            testConvert(true, vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
-        }
-    }
-
-    @Test // SLOW
-    public void testPermuteArguments() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testPermuteArguments0);
-    }
-
-    public void testPermuteArguments0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("permuteArguments");
-        testPermuteArguments(4, Integer.class,  2, long.class,    6);
-        if (CAN_TEST_LIGHTLY)  return;
-        testPermuteArguments(4, Integer.class,  2, String.class,  0);
-        testPermuteArguments(6, Integer.class,  0, null,         30);
-    }
-    public void testPermuteArguments(int max, Class<?> type1, int t2c, Class<?> type2, int dilution) throws Throwable {
-        if (verbosity >= 2)
-            System.out.println("permuteArguments "+max+"*"+type1.getName()
-                    +(t2c==0?"":"/"+t2c+"*"+type2.getName())
-                    +(dilution > 0 ? " with dilution "+dilution : ""));
-        int t2pos = t2c == 0 ? 0 : 1;
-        for (int inargs = t2pos+1; inargs <= max; inargs++) {
-            Class<?>[] types = new Class<?>[inargs];
-            Arrays.fill(types, type1);
-            if (t2c != 0) {
-                // Fill in a middle range with type2:
-                Arrays.fill(types, t2pos, Math.min(t2pos+t2c, inargs), type2);
-            }
-            Object[] args = randomArgs(types);
-            int numcases = 1;
-            for (int outargs = 0; outargs <= max; outargs++) {
-                if (outargs - inargs >= MAX_ARG_INCREASE)  continue;
-                int casStep = dilution + 1;
-                // Avoid some common factors:
-                while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) ||
-                       (casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0))
-                    casStep++;
-                testPermuteArguments(args, types, outargs, numcases, casStep);
-                numcases *= inargs;
-                if (CAN_TEST_LIGHTLY && outargs < max-2)  continue;
-                if (dilution > 10 && outargs >= 4) {
-                    if (CAN_TEST_LIGHTLY)  continue;
-                    int[] reorder = new int[outargs];
-                    // Do some special patterns, which we probably missed.
-                    // Replication of a single argument or argument pair.
-                    for (int i = 0; i < inargs; i++) {
-                        Arrays.fill(reorder, i);
-                        testPermuteArguments(args, types, reorder);
-                        for (int d = 1; d <= 2; d++) {
-                            if (i + d >= inargs)  continue;
-                            for (int j = 1; j < outargs; j += 2)
-                                reorder[j] += 1;
-                            testPermuteArguments(args, types, reorder);
-                            testPermuteArguments(args, types, reverse(reorder));
-                        }
-                    }
-                    // Repetition of a sequence of 3 or more arguments.
-                    for (int i = 1; i < inargs; i++) {
-                        for (int len = 3; len <= inargs; len++) {
-                            for (int j = 0; j < outargs; j++)
-                                reorder[j] = (i + (j % len)) % inargs;
-                            testPermuteArguments(args, types, reorder);
-                            testPermuteArguments(args, types, reverse(reorder));
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    public void testPermuteArguments(Object[] args, Class<?>[] types,
-                                     int outargs, int numcases, int casStep) throws Throwable {
-        int inargs = args.length;
-        int[] reorder = new int[outargs];
-        for (int cas = 0; cas < numcases; cas += casStep) {
-            for (int i = 0, c = cas; i < outargs; i++) {
-                reorder[i] = c % inargs;
-                c /= inargs;
-            }
-            if (CAN_TEST_LIGHTLY && outargs >= 3 && (reorder[0] == reorder[1] || reorder[1] == reorder[2]))  continue;
-            testPermuteArguments(args, types, reorder);
-        }
-    }
-
-    static int[] reverse(int[] reorder) {
-        reorder = reorder.clone();
-        for (int i = 0, imax = reorder.length / 2; i < imax; i++) {
-            int j = reorder.length - 1 - i;
-            int tem = reorder[i];
-            reorder[i] = reorder[j];
-            reorder[j] = tem;
-        }
-        return reorder;
-    }
-
-    void testPermuteArguments(Object[] args, Class<?>[] types, int[] reorder) throws Throwable {
-        countTest();
-        if (args == null && types == null) {
-            int max = 0;
-            for (int j : reorder) {
-                if (max < j)  max = j;
-            }
-            args = randomArgs(max+1, Integer.class);
-        }
-        if (args == null) {
-            args = randomArgs(types);
-        }
-        if (types == null) {
-            types = new Class<?>[args.length];
-            for (int i = 0; i < args.length; i++)
-                types[i] = args[i].getClass();
-        }
-        int inargs = args.length, outargs = reorder.length;
-        assertTrue(inargs == types.length);
-        if (verbosity >= 3)
-            System.out.println("permuteArguments "+Arrays.toString(reorder));
-        Object[] permArgs = new Object[outargs];
-        Class<?>[] permTypes = new Class<?>[outargs];
-        for (int i = 0; i < outargs; i++) {
-            permArgs[i] = args[reorder[i]];
-            permTypes[i] = types[reorder[i]];
-        }
-        if (verbosity >= 4) {
-            System.out.println("in args:   "+Arrays.asList(args));
-            System.out.println("out args:  "+Arrays.asList(permArgs));
-            System.out.println("in types:  "+Arrays.asList(types));
-            System.out.println("out types: "+Arrays.asList(permTypes));
-        }
-        MethodType inType  = MethodType.methodType(Object.class, types);
-        MethodType outType = MethodType.methodType(Object.class, permTypes);
-        MethodHandle target = varargsList(outargs).asType(outType);
-        MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
-        if (verbosity >= 5)  System.out.println("newTarget = "+newTarget);
-        Object result = newTarget.invokeWithArguments(args);
-        Object expected = Arrays.asList(permArgs);
-        if (!expected.equals(result)) {
-            System.out.println("*** failed permuteArguments "+Arrays.toString(reorder)+" types="+Arrays.asList(types));
-            System.out.println("in args:   "+Arrays.asList(args));
-            System.out.println("out args:  "+expected);
-            System.out.println("bad args:  "+result);
-        }
-        assertEquals(expected, result);
-    }
-
-
-    @Test // SLOW
-    public void testSpreadArguments() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments0);
-        CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments1);
-    }
-
-    public void testSpreadArguments0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("spreadArguments");
-        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, arrayType, pos, nargs);
-                }
-            }
-        }
-    }
-    public void testSpreadArguments(Class<?> argType, Class<?> arrayType, int pos, 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+".."+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);
-            switch (nargs) {
-                case 0:
-                    check = (Object[]) (Object) target.invokeExact();
-                    assertArrayEquals(args, check);
-                    break;
-                case 1:
-                    check = (Object[]) (Object) target.invokeExact(args[0]);
-                    assertArrayEquals(args, check);
-                    break;
-                case 2:
-                    check = (Object[]) (Object) target.invokeExact(args[0], args[1]);
-                    assertArrayEquals(args, check);
-                    break;
-            }
-        }
-    }
-    private static void checkReturnValue(Class<?> argType, Object[] args, MethodHandle result, Object returnValue) {
-        String argstr = Arrays.toString(args);
-        if (!argType.isPrimitive()) {
-            Object[] rv = (Object[]) returnValue;
-            String rvs = Arrays.toString(rv);
-            if (!Arrays.equals(args, rv)) {
-                System.out.println("method:   "+result);
-                System.out.println("expected: "+argstr);
-                System.out.println("returned: "+rvs);
-                assertArrayEquals(args, rv);
-            }
-        } else if (argType == int.class) {
-            String rvs = Arrays.toString((int[]) returnValue);
-            if (!argstr.equals(rvs)) {
-                System.out.println("method:   "+result);
-                System.out.println("expected: "+argstr);
-                System.out.println("returned: "+rvs);
-                assertEquals(argstr, rvs);
-            }
-        } else if (argType == long.class) {
-            String rvs = Arrays.toString((long[]) returnValue);
-            if (!argstr.equals(rvs)) {
-                System.out.println("method:   "+result);
-                System.out.println("expected: "+argstr);
-                System.out.println("returned: "+rvs);
-                assertEquals(argstr, rvs);
-            }
-        } else {
-            // cannot test...
-        }
-    }
-
-    @Test // SLOW
-    public void testAsCollector() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testAsCollector0);
-        CodeCacheOverflowProcessor.runMHTest(this::testAsCollector1);
-    }
-
-    public void testAsCollector0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("asCollector");
-        for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
-            if (verbosity >= 3)
-                System.out.println("asCollector "+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;
-                    testAsCollector(argType, pos, nargs);
-                }
-            }
-        }
-    }
-    public void testAsCollector(Class<?> argType, int pos, 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:
-        Object[] collectedArgs = Arrays.copyOfRange(args, 0, pos+1);
-        collectedArgs[pos] = Arrays.copyOfRange(args, pos, args.length);
-        // here is the MH which will witness the collected argument tail:
-        MethodHandle target = varargsArray(pos+1);
-        target = changeArgTypes(target, 0, pos, argType);
-        target = changeArgTypes(target, pos, pos+1, Object[].class);
-        if (verbosity >= 3)
-            System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]");
-        MethodHandle result = target.asCollector(Object[].class, nargs-pos).asType(newType);
-        Object[] returnValue = (Object[]) result.invokeWithArguments(args);
-//        assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]);
-//        returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]);
-//        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 {
-        CodeCacheOverflowProcessor.runMHTest(this::testInsertArguments0);
-    }
-
-    public void testInsertArguments0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("insertArguments");
-        for (int nargs = 0; nargs < 50; nargs++) {
-            if (CAN_TEST_LIGHTLY && nargs > 11)  break;
-            for (int ins = 0; ins <= nargs; ins++) {
-                if (nargs > 10 && ins > 4 && ins < nargs-4 && ins % 10 != 3)
-                    continue;
-                for (int pos = 0; pos <= nargs; pos++) {
-                    if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
-                        continue;
-                    if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2)  continue;
-                    testInsertArguments(nargs, pos, ins);
-                }
-            }
-        }
-    }
-
-    void testInsertArguments(int nargs, int pos, int ins) throws Throwable {
-        countTest();
-        MethodHandle target = varargsArray(nargs + ins);
-        Object[] args = randomArgs(target.type().parameterArray());
-        List<Object> resList = Arrays.asList(args);
-        List<Object> argsToPass = new ArrayList<>(resList);
-        List<Object> argsToInsert = argsToPass.subList(pos, pos + ins);
-        if (verbosity >= 3)
-            System.out.println("insert: "+argsToInsert+" @"+pos+" into "+target);
-        @SuppressWarnings("cast")  // cast to spread Object... is helpful
-        MethodHandle target2 = MethodHandles.insertArguments(target, pos,
-                (Object[]/*...*/) argsToInsert.toArray());
-        argsToInsert.clear();  // remove from argsToInsert
-        Object res2 = target2.invokeWithArguments(argsToPass);
-        Object res2List = Arrays.asList((Object[])res2);
-        if (verbosity >= 3)
-            System.out.println("result: "+res2List);
-        //if (!resList.equals(res2List))
-        //    System.out.println("*** fail at n/p/i = "+nargs+"/"+pos+"/"+ins+": "+resList+" => "+res2List);
-        assertEquals(resList, res2List);
-    }
-
-    @Test
-    public void testFilterReturnValue() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFilterReturnValue0);
-    }
-
-    public void testFilterReturnValue0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("filterReturnValue");
-        Class<?> classOfVCList = varargsList(1).invokeWithArguments(0).getClass();
-        assertTrue(List.class.isAssignableFrom(classOfVCList));
-        for (int nargs = 0; nargs <= 3; nargs++) {
-            for (Class<?> rtype : new Class<?>[] { Object.class,
-                                                List.class,
-                                                int.class,
-                                                byte.class,
-                                                long.class,
-                                                CharSequence.class,
-                                                String.class }) {
-                testFilterReturnValue(nargs, rtype);
-            }
-        }
-    }
-
-    void testFilterReturnValue(int nargs, Class<?> rtype) throws Throwable {
-        countTest();
-        MethodHandle target = varargsList(nargs, rtype);
-        MethodHandle filter;
-        if (List.class.isAssignableFrom(rtype) || rtype.isAssignableFrom(List.class))
-            filter = varargsList(1);  // add another layer of list-ness
-        else
-            filter = MethodHandles.identity(rtype);
-        filter = filter.asType(MethodType.methodType(target.type().returnType(), rtype));
-        Object[] argsToPass = randomArgs(nargs, Object.class);
-        if (verbosity >= 3)
-            System.out.println("filter "+target+" to "+rtype.getSimpleName()+" with "+filter);
-        MethodHandle target2 = MethodHandles.filterReturnValue(target, filter);
-        if (verbosity >= 4)
-            System.out.println("filtered target: "+target2);
-        // Simulate expected effect of filter on return value:
-        Object unfiltered = target.invokeWithArguments(argsToPass);
-        Object expected = filter.invokeWithArguments(unfiltered);
-        if (verbosity >= 4)
-            System.out.println("unfiltered: "+unfiltered+" : "+unfiltered.getClass().getSimpleName());
-        if (verbosity >= 4)
-            System.out.println("expected: "+expected+" : "+expected.getClass().getSimpleName());
-        Object result = target2.invokeWithArguments(argsToPass);
-        if (verbosity >= 3)
-            System.out.println("result: "+result+" : "+result.getClass().getSimpleName());
-        if (!expected.equals(result))
-            System.out.println("*** fail at n/rt = "+nargs+"/"+rtype.getSimpleName()+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected);
-        assertEquals(expected, result);
-    }
-
-    @Test
-    public void testFilterArguments() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testFilterArguments0);
-    }
-
-    public void testFilterArguments0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("filterArguments");
-        for (int nargs = 1; nargs <= 6; nargs++) {
-            for (int pos = 0; pos < nargs; pos++) {
-                testFilterArguments(nargs, pos);
-            }
-        }
-    }
-
-    void testFilterArguments(int nargs, int pos) throws Throwable {
-        countTest();
-        MethodHandle target = varargsList(nargs);
-        MethodHandle filter = varargsList(1);
-        filter = filter.asType(filter.type().generic());
-        Object[] argsToPass = randomArgs(nargs, Object.class);
-        if (verbosity >= 3)
-            System.out.println("filter "+target+" at "+pos+" with "+filter);
-        MethodHandle target2 = MethodHandles.filterArguments(target, pos, filter);
-        // Simulate expected effect of filter on arglist:
-        Object[] filteredArgs = argsToPass.clone();
-        filteredArgs[pos] = filter.invokeExact(filteredArgs[pos]);
-        List<Object> expected = Arrays.asList(filteredArgs);
-        Object result = target2.invokeWithArguments(argsToPass);
-        if (verbosity >= 3)
-            System.out.println("result: "+result);
-        if (!expected.equals(result))
-            System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected);
-        assertEquals(expected, result);
-    }
-
-    @Test
-    public void testCollectArguments() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testCollectArguments0);
-    }
-
-    public void testCollectArguments0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("collectArguments");
-        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, false);
-    }
-
-    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;
-                if (collectType != String.class)  maxArity = 5;
-                if (lastType != Object.class)  maxArity = 4;
-                for (int nargs = 0; nargs <= maxArity; nargs++) {
-                    ArrayList<Class<?>> argTypes = new ArrayList<>(Collections.nCopies(nargs, Object.class));
-                    int maxMix = 20;
-                    if (collectType != Object.class)  maxMix = 0;
-                    Map<Object,Integer> argTypesSeen = new HashMap<>();
-                    for (int mix = 0; mix <= maxMix; mix++) {
-                        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, withFoldPos);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    boolean mixArgs(List<Class<?>> argTypes, int mix, Map<Object,Integer> argTypesSeen) {
-        assert(mix >= 0);
-        if (mix == 0)  return true;  // no change
-        if ((mix >>> argTypes.size()) != 0)  return false;
-        for (int i = 0; i < argTypes.size(); i++) {
-            if (i >= 31)  break;
-            boolean bit = (mix & (1 << i)) != 0;
-            if (bit) {
-                Class<?> type = argTypes.get(i);
-                if (type == Object.class)
-                    type = String.class;
-                else if (type == String.class)
-                    type = int.class;
-                else
-                    type = Object.class;
-                argTypes.set(i, type);
-            }
-        }
-        Integer prev = argTypesSeen.put(new ArrayList<>(argTypes), mix);
-        if (prev != null) {
-            if (verbosity >= 4)  System.out.println("mix "+prev+" repeated "+mix+": "+argTypes);
-            return false;
-        }
-        if (verbosity >= 3)  System.out.println("mix "+mix+" = "+argTypes);
-        return true;
-    }
-
-    void testFoldOrCollectArguments(List<Class<?>> argTypes,  // argument types minus the inserted combineType
-                                    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,
-                                    boolean withFoldPos) throws Throwable {
-        int nargs = argTypes.size();
-        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 target see arg[pos..pos+cc-1]?
-            targetArgTypes.subList(pos, pos + fold).clear();
-        if (combineType != void.class)
-            targetArgTypes.add(pos, combineType);
-        MethodHandle target = varargsList(targetArgTypes, lastType);
-        MethodHandle combine = varargsList(combineArgTypes, combineType);
-        List<Object> argsToPass = Arrays.asList(randomArgs(argTypes));
-        if (verbosity >= 3)
-            System.out.println((isCollect ? "collect" : "fold")+" "+target+" with "+combine);
-        MethodHandle target2;
-        if (isCollect)
-            target2 = MethodHandles.collectArguments(target, pos, combine);
-        else
-            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);
-        if (verbosity >= 3)
-            System.out.println((isCollect ? "collect" : "fold")+": "+argsToFold+" into "+target2);
-        Object foldedArgs = combine.invokeWithArguments(argsToFold);
-        if (isCollect)
-            argsToFold.clear();
-        if (combineType != void.class)
-            argsToFold.add(0, foldedArgs);
-        Object result = target2.invokeWithArguments(argsToPass);
-        if (verbosity >= 3)
-            System.out.println("result: "+result);
-        Object expected = target.invokeWithArguments(expectedList);
-        if (!expected.equals(result))
-            System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result+" != "+expected);
-        assertEquals(expected, result);
-    }
-
-    @Test
-    public void testDropArguments() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testDropArguments0);
-    }
-
-    public void testDropArguments0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("dropArguments");
-        for (int nargs = 0; nargs <= 4; nargs++) {
-            for (int drop = 1; drop <= 4; drop++) {
-                for (int pos = 0; pos <= nargs; pos++) {
-                    testDropArguments(nargs, pos, drop);
-                }
-            }
-        }
-    }
-
-    void testDropArguments(int nargs, int pos, int drop) throws Throwable {
-        countTest();
-        MethodHandle target = varargsArray(nargs);
-        Object[] args = randomArgs(target.type().parameterArray());
-        MethodHandle target2 = MethodHandles.dropArguments(target, pos,
-                Collections.nCopies(drop, Object.class).toArray(new Class<?>[0]));
-        List<Object> resList = Arrays.asList(args);
-        List<Object> argsToDrop = new ArrayList<>(resList);
-        for (int i = drop; i > 0; i--) {
-            argsToDrop.add(pos, "blort#"+i);
-        }
-        Object res2 = target2.invokeWithArguments(argsToDrop);
-        Object res2List = Arrays.asList((Object[])res2);
-        //if (!resList.equals(res2List))
-        //    System.out.println("*** fail at n/p/d = "+nargs+"/"+pos+"/"+drop+": "+argsToDrop+" => "+res2List);
-        assertEquals(resList, res2List);
-    }
-
-    @Test  // SLOW
-    public void testInvokers() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testInvokers0);
-    }
-
-    public void testInvokers0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("exactInvoker, genericInvoker, varargsInvoker, dynamicInvoker");
-        // exactInvoker, genericInvoker, varargsInvoker[0..N], dynamicInvoker
-        Set<MethodType> done = new HashSet<>();
-        for (int i = 0; i <= 6; i++) {
-            if (CAN_TEST_LIGHTLY && i > 3)  break;
-            MethodType gtype = MethodType.genericMethodType(i);
-            for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
-                for (int j = -1; j < i; j++) {
-                    MethodType type = gtype;
-                    if (j < 0)
-                        type = type.changeReturnType(argType);
-                    else if (argType == void.class)
-                        continue;
-                    else
-                        type = type.changeParameterType(j, argType);
-                    if (done.add(type))
-                        testInvokersWithCatch(type);
-                    MethodType vtype = type.changeReturnType(void.class);
-                    if (done.add(vtype))
-                        testInvokersWithCatch(vtype);
-                }
-            }
-        }
-    }
-
-    public void testInvokersWithCatch(MethodType type) throws Throwable {
-        try {
-            testInvokers(type);
-        } catch (Throwable ex) {
-            System.out.println("*** testInvokers on "+type+" => ");
-            ex.printStackTrace(System.out);
-        }
-    }
-    public void testInvokers(MethodType type) throws Throwable {
-        if (verbosity >= 3)
-            System.out.println("test invokers for "+type);
-        int nargs = type.parameterCount();
-        boolean testRetCode = type.returnType() != void.class;
-        MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "invokee",
-                                MethodType.genericMethodType(0, true));
-        assertTrue(target.isVarargsCollector());
-        target = target.asType(type);
-        Object[] args = randomArgs(type.parameterArray());
-        List<Object> targetPlusArgs = new ArrayList<>(Arrays.asList(args));
-        targetPlusArgs.add(0, target);
-        int code = (Integer) invokee(args);
-        Object log = logEntry("invokee", args);
-        assertEquals(log.hashCode(), code);
-        assertCalled("invokee", args);
-        MethodHandle inv;
-        Object result;
-        // exact invoker
-        countTest();
-        calledLog.clear();
-        inv = MethodHandles.exactInvoker(type);
-        result = inv.invokeWithArguments(targetPlusArgs);
-        if (testRetCode)  assertEquals(code, result);
-        assertCalled("invokee", args);
-        // generic invoker
-        countTest();
-        inv = MethodHandles.invoker(type);
-        if (nargs <= 3 && type == type.generic()) {
-            calledLog.clear();
-            switch (nargs) {
-            case 0:
-                result = inv.invokeExact(target);
-                break;
-            case 1:
-                result = inv.invokeExact(target, args[0]);
-                break;
-            case 2:
-                result = inv.invokeExact(target, args[0], args[1]);
-                break;
-            case 3:
-                result = inv.invokeExact(target, args[0], args[1], args[2]);
-                break;
-            }
-            if (testRetCode)  assertEquals(code, result);
-            assertCalled("invokee", args);
-        }
-        calledLog.clear();
-        result = inv.invokeWithArguments(targetPlusArgs);
-        if (testRetCode)  assertEquals(code, result);
-        assertCalled("invokee", args);
-        // varargs invoker #0
-        calledLog.clear();
-        inv = MethodHandles.spreadInvoker(type, 0);
-        if (type.returnType() == Object.class) {
-            result = inv.invokeExact(target, args);
-        } else if (type.returnType() == void.class) {
-            result = null; inv.invokeExact(target, args);
-        } else {
-            result = inv.invokeWithArguments(target, (Object) args);
-        }
-        if (testRetCode)  assertEquals(code, result);
-        assertCalled("invokee", args);
-        if (nargs >= 1 && type == type.generic()) {
-            // varargs invoker #1
-            calledLog.clear();
-            inv = MethodHandles.spreadInvoker(type, 1);
-            result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs));
-            if (testRetCode)  assertEquals(code, result);
-            assertCalled("invokee", args);
-        }
-        if (nargs >= 2 && type == type.generic()) {
-            // varargs invoker #2
-            calledLog.clear();
-            inv = MethodHandles.spreadInvoker(type, 2);
-            result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs));
-            if (testRetCode)  assertEquals(code, result);
-            assertCalled("invokee", args);
-        }
-        if (nargs >= 3 && type == type.generic()) {
-            // varargs invoker #3
-            calledLog.clear();
-            inv = MethodHandles.spreadInvoker(type, 3);
-            result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs));
-            if (testRetCode)  assertEquals(code, result);
-            assertCalled("invokee", args);
-        }
-        for (int k = 0; k <= nargs; k++) {
-            // varargs invoker #0..N
-            if (CAN_TEST_LIGHTLY && (k > 1 || k < nargs - 1))  continue;
-            countTest();
-            calledLog.clear();
-            inv = MethodHandles.spreadInvoker(type, k);
-            MethodType expType = (type.dropParameterTypes(k, nargs)
-                                  .appendParameterTypes(Object[].class)
-                                  .insertParameterTypes(0, MethodHandle.class));
-            assertEquals(expType, inv.type());
-            List<Object> targetPlusVarArgs = new ArrayList<>(targetPlusArgs);
-            List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
-            Object[] tail = tailList.toArray();
-            tailList.clear(); tailList.add(tail);
-            result = inv.invokeWithArguments(targetPlusVarArgs);
-            if (testRetCode)  assertEquals(code, result);
-            assertCalled("invokee", args);
-        }
-
-        // dynamic invoker
-        countTest();
-        CallSite site = new MutableCallSite(type);
-        inv = site.dynamicInvoker();
-
-        // see if we get the result of the original target:
-        try {
-            result = inv.invokeWithArguments(args);
-            assertTrue("should not reach here", false);
-        } catch (IllegalStateException ex) {
-            String msg = ex.getMessage();
-            assertTrue(msg, msg.contains("site"));
-        }
-
-        // set new target after invoker is created, to make sure we track target
-        site.setTarget(target);
-        calledLog.clear();
-        result = inv.invokeWithArguments(args);
-        if (testRetCode)  assertEquals(code, result);
-        assertCalled("invokee", args);
-    }
-
     static Object invokee(Object... args) {
         return called("invokee", args).hashCode();
     }
 
-    private static final String MISSING_ARG = "missingArg";
-    private static final String MISSING_ARG_2 = "missingArg#2";
+    protected static final String MISSING_ARG = "missingArg";
+    protected static final String MISSING_ARG_2 = "missingArg#2";
+
     static Object targetIfEquals() {
         return called("targetIfEquals");
     }
+
     static Object fallbackIfNotEquals() {
         return called("fallbackIfNotEquals");
     }
+
     static Object targetIfEquals(Object x) {
         assertEquals(x, MISSING_ARG);
         return called("targetIfEquals", x);
     }
+
     static Object fallbackIfNotEquals(Object x) {
         assertFalse(x.toString(), x.equals(MISSING_ARG));
         return called("fallbackIfNotEquals", x);
     }
+
     static Object targetIfEquals(Object x, Object y) {
         assertEquals(x, y);
         return called("targetIfEquals", x, y);
     }
+
     static Object fallbackIfNotEquals(Object x, Object y) {
         assertFalse(x.toString(), x.equals(y));
         return called("fallbackIfNotEquals", x, y);
     }
+
     static Object targetIfEquals(Object x, Object y, Object z) {
         assertEquals(x, y);
         return called("targetIfEquals", x, y, z);
     }
+
     static Object fallbackIfNotEquals(Object x, Object y, Object z) {
         assertFalse(x.toString(), x.equals(y));
         return called("fallbackIfNotEquals", x, y, z);
     }
 
-    @Test
-    public void testGuardWithTest() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testGuardWithTest0);
-    }
-
-    public void testGuardWithTest0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("guardWithTest");
-        for (int nargs = 0; nargs <= 50; nargs++) {
-            if (CAN_TEST_LIGHTLY && nargs > 7)  break;
-            testGuardWithTest(nargs, Object.class);
-            testGuardWithTest(nargs, String.class);
-        }
-    }
-    void testGuardWithTest(int nargs, Class<?> argClass) throws Throwable {
-        testGuardWithTest(nargs, 0, argClass);
-        if (nargs <= 5 || nargs % 10 == 3) {
-            for (int testDrops = 1; testDrops <= nargs; testDrops++)
-                testGuardWithTest(nargs, testDrops, argClass);
-        }
-    }
-    void testGuardWithTest(int nargs, int testDrops, Class<?> argClass) throws Throwable {
-        countTest();
-        int nargs1 = Math.min(3, nargs);
-        MethodHandle test = PRIVATE.findVirtual(Object.class, "equals", MethodType.methodType(boolean.class, Object.class));
-        MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "targetIfEquals", MethodType.genericMethodType(nargs1));
-        MethodHandle fallback = PRIVATE.findStatic(MethodHandlesTest.class, "fallbackIfNotEquals", MethodType.genericMethodType(nargs1));
-        while (test.type().parameterCount() > nargs)
-            // 0: test = constant(MISSING_ARG.equals(MISSING_ARG))
-            // 1: test = lambda (_) MISSING_ARG.equals(_)
-            test = MethodHandles.insertArguments(test, 0, MISSING_ARG);
-        if (argClass != Object.class) {
-            test = changeArgTypes(test, argClass);
-            target = changeArgTypes(target, argClass);
-            fallback = changeArgTypes(fallback, argClass);
-        }
-        int testArgs = nargs - testDrops;
-        assert(testArgs >= 0);
-        test = addTrailingArgs(test, Math.min(testArgs, nargs), argClass);
-        target = addTrailingArgs(target, nargs, argClass);
-        fallback = addTrailingArgs(fallback, nargs, argClass);
-        Object[][] argLists = {
-            { },
-            { "foo" }, { MISSING_ARG },
-            { "foo", "foo" }, { "foo", "bar" },
-            { "foo", "foo", "baz" }, { "foo", "bar", "baz" }
-        };
-        for (Object[] argList : argLists) {
-            Object[] argList1 = argList;
-            if (argList.length != nargs) {
-                if (argList.length != nargs1)  continue;
-                argList1 = Arrays.copyOf(argList, nargs);
-                Arrays.fill(argList1, nargs1, nargs, MISSING_ARG_2);
-            }
-            MethodHandle test1 = test;
-            if (test1.type().parameterCount() > testArgs) {
-                int pc = test1.type().parameterCount();
-                test1 = MethodHandles.insertArguments(test, testArgs, Arrays.copyOfRange(argList1, testArgs, pc));
-            }
-            MethodHandle mh = MethodHandles.guardWithTest(test1, target, fallback);
-            assertEquals(target.type(), mh.type());
-            boolean equals;
-            switch (nargs) {
-            case 0:   equals = true; break;
-            case 1:   equals = MISSING_ARG.equals(argList[0]); break;
-            default:  equals = argList[0].equals(argList[1]); break;
-            }
-            String willCall = (equals ? "targetIfEquals" : "fallbackIfNotEquals");
-            if (verbosity >= 3)
-                System.out.println(logEntry(willCall, argList));
-            Object result = mh.invokeWithArguments(argList1);
-            assertCalled(willCall, argList);
-        }
-    }
-
-    @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);
-            if (verbosity >= 6) {
-                System.out.println("pre-handle: " + pre);
-            }
-            Object[] preResults = (Object[]) pre.invokeWithArguments(args);
-            if (verbosity >= 4) {
-                System.out.println("pre-checked: expected " + Arrays.asList(preCheckedResults[i]) + ", actual " +
-                        Arrays.asList(preResults));
-            }
-            if (verbosity >= 6) {
-                System.out.println("post-handle: " + post);
-            }
-            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));
@@ -2887,603 +742,33 @@
         return c + a;
     }
 
-    @Test
-    public void testThrowException() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testThrowException0);
-    }
-
-    public void testThrowException0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("throwException");
-        testThrowException(int.class, new ClassCastException("testing"));
-        testThrowException(void.class, new java.io.IOException("testing"));
-        testThrowException(String.class, new LinkageError("testing"));
-    }
-
-    void testThrowException(Class<?> returnType, Throwable thrown) throws Throwable {
-        countTest();
-        Class<? extends Throwable> exType = thrown.getClass();
-        MethodHandle target = MethodHandles.throwException(returnType, exType);
-        //System.out.println("throwing with "+target+" : "+thrown);
-        MethodType expectedType = MethodType.methodType(returnType, exType);
-        assertEquals(expectedType, target.type());
-        target = target.asType(target.type().generic());
-        Throwable caught = null;
-        try {
-            Object res = target.invokeExact((Object) thrown);
-            fail("got "+res+" instead of throwing "+thrown);
-        } catch (Throwable ex) {
-            if (ex != thrown) {
-                if (ex instanceof Error)  throw (Error)ex;
-                if (ex instanceof RuntimeException)  throw (RuntimeException)ex;
-            }
-            caught = ex;
-        }
-        assertSame(thrown, caught);
-    }
-
-    @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;
-        startTest("interfaceCast");
-        assert( (((Object)"foo") instanceof CharSequence));
-        assert(!(((Object)"foo") instanceof Iterable));
-        for (MethodHandle mh : new MethodHandle[]{
-            MethodHandles.identity(String.class),
-            MethodHandles.identity(CharSequence.class),
-            MethodHandles.identity(Iterable.class)
-        }) {
-            if (verbosity > 0)  System.out.println("-- mh = "+mh);
-            for (Class<?> ctype : new Class<?>[]{
-                Object.class, String.class, CharSequence.class,
-                Number.class, Iterable.class
-            }) {
-                if (verbosity > 0)  System.out.println("---- ctype = "+ctype.getName());
-                //                           doret  docast
-                testInterfaceCast(mh, ctype, false, false);
-                testInterfaceCast(mh, ctype, true,  false);
-                testInterfaceCast(mh, ctype, false, true);
-                testInterfaceCast(mh, ctype, true,  true);
-            }
-        }
-    }
-    private static Class<?> i2o(Class<?> c) {
-        return (c.isInterface() ? Object.class : c);
-    }
-    public void testInterfaceCast(MethodHandle mh, Class<?> ctype,
-                                                   boolean doret, boolean docast) throws Throwable {
-        MethodHandle mh0 = mh;
-        if (verbosity > 1)
-            System.out.println("mh="+mh+", ctype="+ctype.getName()+", doret="+doret+", docast="+docast);
-        String normalRetVal = "normal return value";
-        MethodType mt = mh.type();
-        MethodType mt0 = mt;
-        if (doret)  mt = mt.changeReturnType(ctype);
-        else        mt = mt.changeParameterType(0, ctype);
-        if (docast) mh = MethodHandles.explicitCastArguments(mh, mt);
-        else        mh = mh.asType(mt);
-        assertEquals(mt, mh.type());
-        MethodType mt1 = mt;
-        // this bit is needed to make the interface types disappear for invokeWithArguments:
-        mh = MethodHandles.explicitCastArguments(mh, mt.generic());
-        Class<?>[] step = {
-            mt1.parameterType(0),  // param as passed to mh at first
-            mt0.parameterType(0),  // param after incoming cast
-            mt0.returnType(),      // return value before cast
-            mt1.returnType(),      // return value after outgoing cast
-        };
-        // where might a checkCast occur?
-        boolean[] checkCast = new boolean[step.length];
-        // the string value must pass each step without causing an exception
-        if (!docast) {
-            if (!doret) {
-                if (step[0] != step[1])
-                    checkCast[1] = true;  // incoming value is cast
-            } else {
-                if (step[2] != step[3])
-                    checkCast[3] = true;  // outgoing value is cast
-            }
-        }
-        boolean expectFail = false;
-        for (int i = 0; i < step.length; i++) {
-            Class<?> c = step[i];
-            if (!checkCast[i])  c = i2o(c);
-            if (!c.isInstance(normalRetVal)) {
-                if (verbosity > 3)
-                    System.out.println("expect failure at step "+i+" in "+Arrays.toString(step)+Arrays.toString(checkCast));
-                expectFail = true;
-                break;
-            }
-        }
-        countTest(!expectFail);
-        if (verbosity > 2)
-            System.out.println("expectFail="+expectFail+", mt="+mt);
-        Object res;
-        try {
-            res = mh.invokeWithArguments(normalRetVal);
-        } catch (Exception ex) {
-            res = ex;
-        }
-        boolean sawFail = !(res instanceof String);
-        if (sawFail != expectFail) {
-            System.out.println("*** testInterfaceCast: mh0 = "+mh0);
-            System.out.println("  retype using "+(docast ? "explicitCastArguments" : "asType")+" to "+mt+" => "+mh);
-            System.out.println("  call returned "+res);
-            System.out.println("  expected "+(expectFail ? "an exception" : normalRetVal));
-        }
-        if (!expectFail) {
-            assertFalse(res.toString(), sawFail);
-            assertEquals(normalRetVal, res);
-        } else {
-            assertTrue(res.toString(), sawFail);
-        }
-    }
-
-    @Test  // SLOW
-    public void testCastFailure() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testCastFailure0);
-    }
-
-    public void testCastFailure0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("testCastFailure");
-        testCastFailure("cast/argument", 11000);
-        if (CAN_TEST_LIGHTLY)  return;
-        testCastFailure("unbox/argument", 11000);
-        testCastFailure("cast/return", 11000);
-        testCastFailure("unbox/return", 11000);
-    }
-
-    static class Surprise {
-        public MethodHandle asMethodHandle() {
-            return VALUE.bindTo(this);
-        }
-        Object value(Object x) {
-            trace("value", x);
-            if (boo != null)  return boo;
-            return x;
-        }
-        Object boo;
-        void boo(Object x) { boo = x; }
-
-        static void trace(String x, Object y) {
-            if (verbosity > 8) System.out.println(x+"="+y);
-        }
-        static Object  refIdentity(Object x)  { trace("ref.x", x); return x; }
-        static Integer boxIdentity(Integer x) { trace("box.x", x); return x; }
-        static int     intIdentity(int x)     { trace("int.x", x); return x; }
-        static MethodHandle VALUE, REF_IDENTITY, BOX_IDENTITY, INT_IDENTITY;
-        static {
-            try {
-                VALUE = PRIVATE.findVirtual(
-                    Surprise.class, "value",
-                        MethodType.methodType(Object.class, Object.class));
-                REF_IDENTITY = PRIVATE.findStatic(
-                    Surprise.class, "refIdentity",
-                        MethodType.methodType(Object.class, Object.class));
-                BOX_IDENTITY = PRIVATE.findStatic(
-                    Surprise.class, "boxIdentity",
-                        MethodType.methodType(Integer.class, Integer.class));
-                INT_IDENTITY = PRIVATE.findStatic(
-                    Surprise.class, "intIdentity",
-                        MethodType.methodType(int.class, int.class));
-            } catch (NoSuchMethodException | IllegalAccessException ex) {
-                throw new RuntimeException(ex);
-            }
-        }
-    }
-
-    @SuppressWarnings("ConvertToStringSwitch")
-    void testCastFailure(String mode, int okCount) throws Throwable {
-        countTest(false);
-        if (verbosity > 2)  System.out.println("mode="+mode);
-        Surprise boo = new Surprise();
-        MethodHandle identity = Surprise.REF_IDENTITY, surprise0 = boo.asMethodHandle(), surprise = surprise0;
-        if (mode.endsWith("/return")) {
-            if (mode.equals("unbox/return")) {
-                // fail on return to ((Integer)surprise).intValue
-                surprise = surprise.asType(MethodType.methodType(int.class, Object.class));
-                identity = identity.asType(MethodType.methodType(int.class, Object.class));
-            } else if (mode.equals("cast/return")) {
-                // fail on return to (Integer)surprise
-                surprise = surprise.asType(MethodType.methodType(Integer.class, Object.class));
-                identity = identity.asType(MethodType.methodType(Integer.class, Object.class));
-            }
-        } else if (mode.endsWith("/argument")) {
-            MethodHandle callee = null;
-            if (mode.equals("unbox/argument")) {
-                // fail on handing surprise to int argument
-                callee = Surprise.INT_IDENTITY;
-            } else if (mode.equals("cast/argument")) {
-                // fail on handing surprise to Integer argument
-                callee = Surprise.BOX_IDENTITY;
-            }
-            if (callee != null) {
-                callee = callee.asType(MethodType.genericMethodType(1));
-                surprise = MethodHandles.filterArguments(callee, 0, surprise);
-                identity = MethodHandles.filterArguments(callee, 0, identity);
-            }
-        }
-        assertNotSame(mode, surprise, surprise0);
-        identity = identity.asType(MethodType.genericMethodType(1));
-        surprise = surprise.asType(MethodType.genericMethodType(1));
-        Object x = 42;
-        for (int i = 0; i < okCount; i++) {
-            Object y = identity.invokeExact(x);
-            assertEquals(x, y);
-            Object z = surprise.invokeExact(x);
-            assertEquals(x, z);
-        }
-        boo.boo("Boo!");
-        Object y = identity.invokeExact(x);
-        assertEquals(x, y);
-        try {
-            Object z = surprise.invokeExact(x);
-            System.out.println("Failed to throw; got z="+z);
-            assertTrue(false);
-        } catch (ClassCastException ex) {
-            if (verbosity > 2)
-                System.out.println("caught "+ex);
-            if (verbosity > 3)
-                ex.printStackTrace(System.out);
-            assertTrue(true);  // all is well
-        }
-    }
-
-    static Example userMethod(Object o, String s, int i) {
-        called("userMethod", o, s, i);
-        return null;
-    }
-
-    @Test
-    public void testUserClassInSignature() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testUserClassInSignature0);
-    }
-
-    public void testUserClassInSignature0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("testUserClassInSignature");
-        Lookup lookup = MethodHandles.lookup();
-        String name; MethodType mt; MethodHandle mh;
-        Object[] args;
-
-        // Try a static method.
-        name = "userMethod";
-        mt = MethodType.methodType(Example.class, Object.class, String.class, int.class);
-        mh = lookup.findStatic(lookup.lookupClass(), name, mt);
-        assertEquals(mt, mh.type());
-        assertEquals(Example.class, mh.type().returnType());
-        args = randomArgs(mh.type().parameterArray());
-        mh.invokeWithArguments(args);
-        assertCalled(name, args);
-
-        // Try a virtual method.
-        name = "v2";
-        mt = MethodType.methodType(Object.class, Object.class, int.class);
-        mh = lookup.findVirtual(Example.class, name, mt);
-        assertEquals(mt, mh.type().dropParameterTypes(0,1));
-        assertTrue(mh.type().parameterList().contains(Example.class));
-        args = randomArgs(mh.type().parameterArray());
-        mh.invokeWithArguments(args);
-        assertCalled(name, args);
-    }
-
-    static void runForRunnable() {
-        called("runForRunnable");
-    }
-    public interface Fooable {
-        // overloads:
-        Object  foo(Object x, String y);
-        List<?> foo(String x, int y);
-        Object  foo(String x);
-    }
-    static Object fooForFooable(String x, Object... y) {
-        return called("fooForFooable/"+x, y);
-    }
-    @SuppressWarnings("serial")  // not really a public API, just a test case
-    public static class MyCheckedException extends Exception {
-    }
-    public interface WillThrow {
-        void willThrow() throws MyCheckedException;
-    }
-    /*non-public*/ interface PrivateRunnable {
-        public void run();
-    }
-
-    @Test
-    public void testAsInterfaceInstance() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testAsInterfaceInstance0);
-    }
-
-    public void testAsInterfaceInstance0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("asInterfaceInstance");
-        Lookup lookup = MethodHandles.lookup();
-        // test typical case:  Runnable.run
-        {
-            countTest();
-            if (verbosity >= 2)  System.out.println("Runnable");
-            MethodType mt = MethodType.methodType(void.class);
-            MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "runForRunnable", mt);
-            Runnable proxy = MethodHandleProxies.asInterfaceInstance(Runnable.class, mh);
-            proxy.run();
-            assertCalled("runForRunnable");
-        }
-        // well known single-name overloaded interface:  Appendable.append
-        {
-            countTest();
-            if (verbosity >= 2)  System.out.println("Appendable");
-            ArrayList<List<?>> appendResults = new ArrayList<>();
-            MethodHandle append = lookup.bind(appendResults, "add", MethodType.methodType(boolean.class, Object.class));
-            append = append.asType(MethodType.methodType(void.class, List.class)); // specialize the type
-            MethodHandle asList = lookup.findStatic(Arrays.class, "asList", MethodType.methodType(List.class, Object[].class));
-            MethodHandle mh = MethodHandles.filterReturnValue(asList, append).asVarargsCollector(Object[].class);
-            Appendable proxy = MethodHandleProxies.asInterfaceInstance(Appendable.class, mh);
-            proxy.append("one");
-            proxy.append("two", 3, 4);
-            proxy.append('5');
-            assertEquals(Arrays.asList(Arrays.asList("one"),
-                                       Arrays.asList("two", 3, 4),
-                                       Arrays.asList('5')),
-                         appendResults);
-            if (verbosity >= 3)  System.out.println("appendResults="+appendResults);
-            appendResults.clear();
-            Formatter formatter = new Formatter(proxy);
-            String fmt = "foo str=%s char='%c' num=%d";
-            Object[] fmtArgs = { "str!", 'C', 42 };
-            String expect = String.format(fmt, fmtArgs);
-            formatter.format(fmt, fmtArgs);
-            String actual = "";
-            if (verbosity >= 3)  System.out.println("appendResults="+appendResults);
-            for (List<?> l : appendResults) {
-                Object x = l.get(0);
-                switch (l.size()) {
-                case 1:  actual += x; continue;
-                case 3:  actual += ((String)x).substring((int)(Object)l.get(1), (int)(Object)l.get(2)); continue;
-                }
-                actual += l;
-            }
-            if (verbosity >= 3)  System.out.println("expect="+expect);
-            if (verbosity >= 3)  System.out.println("actual="+actual);
-            assertEquals(expect, actual);
-        }
-        // test case of an single name which is overloaded:  Fooable.foo(...)
-        {
-            if (verbosity >= 2)  System.out.println("Fooable");
-            MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "fooForFooable",
-                                                MethodType.methodType(Object.class, String.class, Object[].class));
-            Fooable proxy = MethodHandleProxies.asInterfaceInstance(Fooable.class, mh);
-            for (Method m : Fooable.class.getDeclaredMethods()) {
-                countTest();
-                assertSame("foo", m.getName());
-                if (verbosity > 3)
-                    System.out.println("calling "+m);
-                MethodHandle invoker = lookup.unreflect(m);
-                MethodType mt = invoker.type();
-                Class<?>[] types = mt.parameterArray();
-                types[0] = int.class;  // placeholder
-                Object[] args = randomArgs(types);
-                args[0] = proxy;
-                if (verbosity > 3)
-                    System.out.println("calling "+m+" on "+Arrays.asList(args));
-                Object result = invoker.invokeWithArguments(args);
-                if (verbosity > 4)
-                    System.out.println("result = "+result);
-                String name = "fooForFooable/"+args[1];
-                Object[] argTail = Arrays.copyOfRange(args, 2, args.length);
-                assertCalled(name, argTail);
-                assertEquals(result, logEntry(name, argTail));
-            }
-        }
-        // test processing of thrown exceptions:
-        for (Throwable ex : new Throwable[] { new NullPointerException("ok"),
-                                              new InternalError("ok"),
-                                              new Throwable("fail"),
-                                              new Exception("fail"),
-                                              new MyCheckedException()
-                                            }) {
-            MethodHandle mh = MethodHandles.throwException(void.class, Throwable.class);
-            mh = MethodHandles.insertArguments(mh, 0, ex);
-            WillThrow proxy = MethodHandleProxies.asInterfaceInstance(WillThrow.class, mh);
-            try {
-                countTest();
-                proxy.willThrow();
-                System.out.println("Failed to throw: "+ex);
-                assertTrue(false);
-            } catch (Throwable ex1) {
-                if (verbosity > 3) {
-                    System.out.println("throw "+ex);
-                    System.out.println("catch "+(ex == ex1 ? "UNWRAPPED" : ex1));
-                }
-                if (ex instanceof RuntimeException ||
-                    ex instanceof Error) {
-                    assertSame("must pass unchecked exception out without wrapping", ex, ex1);
-                } else if (ex instanceof MyCheckedException) {
-                    assertSame("must pass declared exception out without wrapping", ex, ex1);
-                } else {
-                    assertNotSame("must pass undeclared checked exception with wrapping", ex, ex1);
-                    if (!(ex1 instanceof UndeclaredThrowableException) || ex1.getCause() != ex) {
-                        ex1.printStackTrace(System.out);
-                    }
-                    assertSame(ex, ex1.getCause());
-                    UndeclaredThrowableException utex = (UndeclaredThrowableException) ex1;
-                }
-            }
-        }
-        // Test error checking on bad interfaces:
-        for (Class<?> nonSMI : new Class<?>[] { Object.class,
-                                             String.class,
-                                             CharSequence.class,
-                                             java.io.Serializable.class,
-                                             PrivateRunnable.class,
-                                             Example.class }) {
-            if (verbosity > 2)  System.out.println(nonSMI.getName());
-            try {
-                countTest(false);
-                MethodHandleProxies.asInterfaceInstance(nonSMI, varargsArray(0));
-                assertTrue("Failed to throw on "+nonSMI.getName(), false);
-            } catch (IllegalArgumentException ex) {
-                if (verbosity > 2)  System.out.println(nonSMI.getSimpleName()+": "+ex);
-                // Object: java.lang.IllegalArgumentException:
-                //     not a public interface: java.lang.Object
-                // String: java.lang.IllegalArgumentException:
-                //     not a public interface: java.lang.String
-                // CharSequence: java.lang.IllegalArgumentException:
-                //     not a single-method interface: java.lang.CharSequence
-                // Serializable: java.lang.IllegalArgumentException:
-                //     not a single-method interface: java.io.Serializable
-                // PrivateRunnable: java.lang.IllegalArgumentException:
-                //     not a public interface: test.java.lang.invoke.MethodHandlesTest$PrivateRunnable
-                // Example: java.lang.IllegalArgumentException:
-                //     not a public interface: test.java.lang.invoke.MethodHandlesTest$Example
-            }
-        }
-        // Test error checking on interfaces with the wrong method type:
-        for (Class<?> intfc : new Class<?>[] { Runnable.class /*arity 0*/,
-                                            Fooable.class /*arity 1 & 2*/ }) {
-            int badArity = 1;  // known to be incompatible
-            if (verbosity > 2)  System.out.println(intfc.getName());
-            try {
-                countTest(false);
-                MethodHandleProxies.asInterfaceInstance(intfc, varargsArray(badArity));
-                assertTrue("Failed to throw on "+intfc.getName(), false);
-            } catch (WrongMethodTypeException ex) {
-                if (verbosity > 2)  System.out.println(intfc.getSimpleName()+": "+ex);
-                // Runnable: java.lang.invoke.WrongMethodTypeException:
-                //     cannot convert MethodHandle(Object)Object[] to ()void
-                // Fooable: java.lang.invoke.WrongMethodTypeException:
-                //     cannot convert MethodHandle(Object)Object[] to (Object,String)Object
-            }
-        }
-    }
-
-    @Test
-    public void testRunnableProxy() throws Throwable {
-        CodeCacheOverflowProcessor.runMHTest(this::testRunnableProxy0);
-    }
-
-    public void testRunnableProxy0() throws Throwable {
-        if (CAN_SKIP_WORKING)  return;
-        startTest("testRunnableProxy");
-        MethodHandles.Lookup lookup = MethodHandles.lookup();
-        MethodHandle run = lookup.findStatic(lookup.lookupClass(), "runForRunnable", MethodType.methodType(void.class));
-        Runnable r = MethodHandleProxies.asInterfaceInstance(Runnable.class, run);
-        testRunnableProxy(r);
-        assertCalled("runForRunnable");
-    }
-    private static void testRunnableProxy(Runnable r) {
-        //7058630: JSR 292 method handle proxy violates contract for Object methods
-        r.run();
-        Object o = r;
-        r = null;
-        boolean eq = (o == o);
-        int     hc = System.identityHashCode(o);
-        String  st = o.getClass().getName() + "@" + Integer.toHexString(hc);
-        Object expect = Arrays.asList(st, eq, hc);
-        if (verbosity >= 2)  System.out.println("expect st/eq/hc = "+expect);
-        Object actual = Arrays.asList(o.toString(), o.equals(o), o.hashCode());
-        if (verbosity >= 2)  System.out.println("actual st/eq/hc = "+actual);
-        assertEquals(expect, actual);
-    }
 }
 // Local abbreviated copy of sun.invoke.util.ValueConversions
 // This guy tests access from outside the same package member, but inside
@@ -3521,6 +806,7 @@
                                   Object a4, Object a5, Object a6, Object a7,
                                   Object a8, Object a9)
                 { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+
     static MethodHandle[] makeArrays() {
         ArrayList<MethodHandle> arrays = new ArrayList<>();
         MethodHandles.Lookup lookup = IMPL_LOOKUP;
@@ -3540,6 +826,7 @@
         assertTrue(arrays.size() == 11);  // current number of methods
         return arrays.toArray(new MethodHandle[0]);
     }
+
     static final MethodHandle[] ARRAYS = makeArrays();
 
     /** Return a method handle that takes the indicated number of Object
@@ -3550,6 +837,7 @@
             return ARRAYS[nargs];
         return MethodHandles.identity(Object[].class).asCollector(Object[].class, nargs);
     }
+
     public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
         Class<?> elemType = arrayType.getComponentType();
         MethodType vaType = MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType));
@@ -3558,6 +846,7 @@
             mh = MethodHandles.filterReturnValue(mh, CHANGE_ARRAY_TYPE.bindTo(arrayType));
         return mh.asType(vaType);
     }
+
     static Object changeArrayType(Class<?> arrayType, Object[] a) {
         Class<?> elemType = arrayType.getComponentType();
         if (!elemType.isPrimitive())
@@ -3567,6 +856,7 @@
             java.lang.reflect.Array.set(b, i, a[i]);
         return b;
     }
+
     private static final MethodHandle CHANGE_ARRAY_TYPE;
     static {
         try {
@@ -3610,6 +900,7 @@
                                      Object a4, Object a5, Object a6, Object a7,
                                      Object a8, Object a9)
                 { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
+
     static MethodHandle[] makeLists() {
         ArrayList<MethodHandle> lists = new ArrayList<>();
         MethodHandles.Lookup lookup = IMPL_LOOKUP;
@@ -3629,8 +920,10 @@
         assertTrue(lists.size() == 11);  // current number of methods
         return lists.toArray(new MethodHandle[0]);
     }
+
     static final MethodHandle[] LISTS = makeLists();
     static final MethodHandle AS_LIST;
+
     static {
         try {
             AS_LIST = IMPL_LOOKUP.findStatic(Arrays.class, "asList", MethodType.methodType(List.class, Object[].class));