7032323: code changes for JSR 292 EG adjustments to API, through Public Review
Summary: API code changes and javadoc changes following JSR 292 Public Review comments, through PFD
Reviewed-by: never
diff --git a/test/java/lang/invoke/6998541/Test6998541.java b/test/java/lang/invoke/6998541/Test6998541.java
index 0f64eee..e9f467b 100644
--- a/test/java/lang/invoke/6998541/Test6998541.java
+++ b/test/java/lang/invoke/6998541/Test6998541.java
@@ -164,6 +164,7 @@
     private static boolean canDoAsType(Class<?> src, Class<?> dst) {
         if (src == dst)  return true;
         if (dst == void.class)  return true;
+        if (src == void.class)  return true;  // allow void->zero
         if (!src.isPrimitive() || !dst.isPrimitive())  return true;
         // primitive conversion works for asType only when it's widening
         if (src == boolean.class || dst == boolean.class)  return false;
@@ -451,7 +452,6 @@
     private final static MethodHandle mh_dv = mh(double.class );
 
     private static void void2prim(int i) throws Throwable {
-        if (!DO_CASTS)  return;
         assertEquals(        false, (boolean) mh_zv.invokeExact());  // void -> boolean
         assertEquals((byte)  0,     (byte)    mh_bv.invokeExact());  // void -> byte
         assertEquals((char)  0,     (char)    mh_cv.invokeExact());  // void -> char
@@ -463,15 +463,7 @@
     }
 
     private static void void2prim_invalid(double x) throws Throwable {
-        if (DO_CASTS)  return;
-        try { assertEquals(        false, (boolean) mh_zv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> boolean
-        try { assertEquals((byte)  0,     (byte)    mh_bv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> byte
-        try { assertEquals((char)  0,     (char)    mh_cv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> char
-        try { assertEquals((short) 0,     (short)   mh_sv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> short
-        try { assertEquals(        0,     (int)     mh_iv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> int
-        try { assertEquals(        0L,    (long)    mh_jv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> long
-        try { assertEquals(        0.0f,  (float)   mh_fv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> float
-        try { assertEquals(        0.0d,  (double)  mh_dv.invokeExact()); fail(); } catch (NullPointerException _) {}  // void -> double
+        // no cases
     }
 
     private static MethodHandle mh_v(Class arg) { return mh(void.class, arg); }
diff --git a/test/java/lang/invoke/InvokeDynamicPrintArgs.java b/test/java/lang/invoke/InvokeDynamicPrintArgs.java
index d8a395d..9c3db42 100644
--- a/test/java/lang/invoke/InvokeDynamicPrintArgs.java
+++ b/test/java/lang/invoke/InvokeDynamicPrintArgs.java
@@ -106,8 +106,10 @@
         "Done printing argument lists."
     };
 
+    private static boolean doPrint = true;
     private static void printArgs(Object bsmInfo, Object... args) {
-        System.out.println(bsmInfo+Arrays.deepToString(args));
+        String message = bsmInfo+Arrays.deepToString(args);
+        if (doPrint)  System.out.println(message);
     }
     private static MethodHandle MH_printArgs() throws ReflectiveOperationException {
         shouldNotCallThis();
@@ -129,11 +131,48 @@
         return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm());
     }
 
-    private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws ReflectiveOperationException {
+    /* Example of a constant call site with user-data.
+     * In this case, the user data is exactly the BSM data.
+     * Note that a CCS with user data must use the "hooked" constructor
+     * to bind the CCS itself into the resulting target.
+     * A normal constructor would not allow a circular relation
+     * between the CCS and its target.
+     */
+    public static class PrintingCallSite extends ConstantCallSite {
+        final Lookup caller;
+        final String name;
+        final Object[] staticArgs;
+
+        PrintingCallSite(Lookup caller, String name, MethodType type, Object... staticArgs) throws Throwable {
+            super(type, MH_createTarget());
+            this.caller = caller;
+            this.name = name;
+            this.staticArgs = staticArgs;
+        }
+
+        public MethodHandle createTarget() {
+            try {
+                return lookup().bind(this, "runTarget", genericMethodType(0, true)).asType(type());
+            } catch (ReflectiveOperationException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        public Object runTarget(Object... dynamicArgs) {
+            List<Object> bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type()));
+            bsmInfo.addAll(Arrays.asList(staticArgs));
+            printArgs(bsmInfo, dynamicArgs);
+            return null;
+        }
+
+        private static MethodHandle MH_createTarget() throws ReflectiveOperationException {
+            shouldNotCallThis();
+            return lookup().findVirtual(lookup().lookupClass(), "createTarget", methodType(MethodHandle.class));
+        }
+    }
+    private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws Throwable {
         // ignore caller and name, but match the type:
-        List<Object> bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type));
-        bsmInfo.addAll(Arrays.asList((Object[])arg));
-        return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
+        return new PrintingCallSite(caller, name, type, arg);
     }
     private static MethodType MT_bsm2() {
         shouldNotCallThis();
@@ -146,33 +185,33 @@
 
     private static MethodHandle INDY_nothing() throws Throwable {
         shouldNotCallThis();
-        return ((CallSite) MH_bsm().invokeGeneric(lookup(),
+        return ((CallSite) MH_bsm().invoke(lookup(),
                                                   "nothing", methodType(void.class)
                                                   )).dynamicInvoker();
     }
     private static MethodHandle INDY_foo() throws Throwable {
         shouldNotCallThis();
-        return ((CallSite) MH_bsm().invokeGeneric(lookup(),
+        return ((CallSite) MH_bsm().invoke(lookup(),
                                                   "foo", methodType(void.class, String.class)
                                                   )).dynamicInvoker();
     }
     private static MethodHandle INDY_bar() throws Throwable {
         shouldNotCallThis();
-        return ((CallSite) MH_bsm2().invokeGeneric(lookup(),
+        return ((CallSite) MH_bsm2().invoke(lookup(),
                                                   "bar", methodType(void.class, String.class, int.class)
                                                   , Void.class, "void type!", 1, 234.5F, 67.5, (long)89
                                                   )).dynamicInvoker();
     }
     private static MethodHandle INDY_bar2() throws Throwable {
         shouldNotCallThis();
-        return ((CallSite) MH_bsm2().invokeGeneric(lookup(),
+        return ((CallSite) MH_bsm2().invoke(lookup(),
                                                   "bar2", methodType(void.class, String.class, int.class)
                                                   , Void.class, "void type!", 1, 234.5F, 67.5, (long)89
                                                   )).dynamicInvoker();
     }
     private static MethodHandle INDY_baz() throws Throwable {
         shouldNotCallThis();
-        return ((CallSite) MH_bsm2().invokeGeneric(lookup(),
+        return ((CallSite) MH_bsm2().invoke(lookup(),
                                                   "baz", methodType(void.class, String.class, int.class, double.class)
                                                   , 1234.5
                                                   )).dynamicInvoker();
diff --git a/test/java/lang/invoke/InvokeGenericTest.java b/test/java/lang/invoke/InvokeGenericTest.java
index 3d49333..fecd2f0 100644
--- a/test/java/lang/invoke/InvokeGenericTest.java
+++ b/test/java/lang/invoke/InvokeGenericTest.java
@@ -314,7 +314,7 @@
         ArrayList<Class<?>> argTypes = new ArrayList<Class<?>>(targetType.parameterList());
         Collections.fill(argTypes.subList(beg, end), argType);
         MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes);
-        return MethodHandles.convertArguments(target, ttype2);
+        return target.asType(ttype2);
     }
 
     // This lookup is good for all members in and under InvokeGenericTest.
@@ -378,7 +378,7 @@
         String[] args = { "one", "two" };
         MethodHandle mh = callable(Object.class, String.class);
         Object res; List resl;
-        res = resl = (List) mh.invokeGeneric((String)args[0], (Object)args[1]);
+        res = resl = (List) mh.invoke((String)args[0], (Object)args[1]);
         //System.out.println(res);
         assertEquals(Arrays.asList(args), res);
     }
diff --git a/test/java/lang/invoke/JavaDocExamplesTest.java b/test/java/lang/invoke/JavaDocExamplesTest.java
index 9f68a20..0e373c1 100644
--- a/test/java/lang/invoke/JavaDocExamplesTest.java
+++ b/test/java/lang/invoke/JavaDocExamplesTest.java
@@ -34,7 +34,7 @@
 $ $JAVA7X_HOME/bin/javac -cp $JUNIT4_JAR -d /tmp/Classes \
    $DAVINCI/sources/jdk/test/java/lang/invoke/JavaDocExamplesTest.java
 $ $JAVA7X_HOME/bin/java   -cp $JUNIT4_JAR:/tmp/Classes \
-   -Dtest.java.lang.invoke.JavaDocExamplesTest.verbosity=1 \
+   -DJavaDocExamplesTest.verbosity=1 \
      test.java.lang.invoke.JavaDocExamplesTest
 ----
 */
@@ -45,12 +45,10 @@
 import static java.lang.invoke.MethodHandles.*;
 import static java.lang.invoke.MethodType.*;
 
-import java.lang.reflect.*;
 import java.util.*;
 
 import org.junit.*;
 import static org.junit.Assert.*;
-import static org.junit.Assume.*;
 
 
 /**
@@ -60,11 +58,29 @@
     /** Wrapper for running the JUnit tests in this module.
      *  Put JUnit on the classpath!
      */
-    public static void main(String... ignore) {
-        org.junit.runner.JUnitCore.runClasses(JavaDocExamplesTest.class);
+    public static void main(String... ignore) throws Throwable {
+        System.out.println("can run this as:");
+        System.out.println("$ java org.junit.runner.JUnitCore "+JavaDocExamplesTest.class.getName());
+        new JavaDocExamplesTest().run();
+    }
+    public void run() throws Throwable {
+        testFindVirtual();
+        testPermuteArguments();
+        testDropArguments();
+        testFilterArguments();
+        testFoldArguments();
+        testMethodHandlesSummary();
+        testAsSpreader();
+        testAsCollector();
+        testAsVarargsCollector();
+        testAsFixedArity();
+        testAsTypeCornerCases();
+        testMutableCallSite();
     }
     // How much output?
-    static int verbosity = Integer.getInteger("test.java.lang.invoke.JavaDocExamplesTest.verbosity", 0);
+    static final Class<?> THIS_CLASS = JavaDocExamplesTest.class;
+    static int verbosity = Integer.getInteger(THIS_CLASS.getSimpleName()+".verbosity", 0);
+
 
 {}
 static final private Lookup LOOKUP = lookup();
@@ -74,17 +90,23 @@
 //     "hashCode", methodType(int.class));
 
 // form required if ReflectiveOperationException is intercepted:
-static final private MethodHandle CONCAT_2, HASHCODE_2;
+    static final private MethodHandle CONCAT_2, HASHCODE_2, ADD_2, SUB_2;
 static {
   try {
+    Class<?> THIS_CLASS = LOOKUP.lookupClass();
     CONCAT_2 = LOOKUP.findVirtual(String.class,
       "concat", methodType(String.class, String.class));
     HASHCODE_2 = LOOKUP.findVirtual(Object.class,
       "hashCode", methodType(int.class));
+    ADD_2 = LOOKUP.findStatic(THIS_CLASS, "add", methodType(int.class, int.class, int.class));
+    SUB_2 = LOOKUP.findStatic(THIS_CLASS, "sub", methodType(int.class, int.class, int.class));
    } catch (ReflectiveOperationException ex) {
      throw new RuntimeException(ex);
    }
 }
+    static int add(int x, int y) { return x + y; }
+    static int sub(int x, int y) { return x - y; }
+
 {}
 
     @Test public void testFindVirtual() throws Throwable {
@@ -101,6 +123,39 @@
 assertEquals("xy".hashCode(), (int) HASHCODE_3.invokeExact((Object)"xy"));
 {}
     }
+
+    @Test public void testPermuteArguments() throws Throwable {
+        {{
+{} /// JAVADOC
+MethodType intfn1 = methodType(int.class, int.class);
+MethodType intfn2 = methodType(int.class, int.class, int.class);
+MethodHandle sub = SUB_2;// ... {int x, int y => x-y} ...;
+assert(sub.type().equals(intfn2));
+MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
+MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
+assert((int)rsub.invokeExact(1, 100) == 99);
+MethodHandle add = ADD_2;// ... {int x, int y => x+y} ...;
+assert(add.type().equals(intfn2));
+MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
+assert(twice.type().equals(intfn1));
+assert((int)twice.invokeExact(21) == 42);
+            }}
+        {{
+{} /// JAVADOC
+MethodHandle cat = lookup().findVirtual(String.class,
+  "concat", methodType(String.class, String.class));
+assertEquals("xy", (String) cat.invokeExact("x", "y"));
+MethodHandle d0 = dropArguments(cat, 0, String.class);
+assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
+MethodHandle d1 = dropArguments(cat, 1, String.class);
+assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
+MethodHandle d2 = dropArguments(cat, 2, String.class);
+assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
+MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
+assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
+            }}
+    }
+
     @Test public void testDropArguments() throws Throwable {
         {{
 {} /// JAVADOC
@@ -145,6 +200,21 @@
             }}
     }
 
+    @Test public void testFoldArguments() throws Throwable {
+        {{
+{} /// JAVADOC
+MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
+  "println", methodType(void.class, String.class))
+    .bindTo(System.out);
+MethodHandle cat = lookup().findVirtual(String.class,
+  "concat", methodType(String.class, String.class));
+assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
+MethodHandle catTrace = foldArguments(cat, trace);
+// also prints "boo":
+assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+            }}
+    }
+
     static void assertEquals(Object exp, Object act) {
         if (verbosity > 0)
             System.out.println("result: "+act);
@@ -162,24 +232,24 @@
 mh = lookup.findVirtual(String.class, "replace", mt);
 s = (String) mh.invokeExact("daddy",'d','n');
 // invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
-assert(s.equals("nanny"));
+assertEquals(s, "nanny");
 // weakly typed invocation (using MHs.invoke)
 s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
-assert(s.equals("savvy"));
+assertEquals(s, "savvy");
 // mt is (Object[])List
 mt = MethodType.methodType(java.util.List.class, Object[].class);
 mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
 assert(mh.isVarargsCollector());
 x = mh.invoke("one", "two");
 // invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
-assert(x.equals(java.util.Arrays.asList("one","two")));
+assertEquals(x, java.util.Arrays.asList("one","two"));
 // mt is (Object,Object,Object)Object
 mt = MethodType.genericMethodType(3);
 mh = mh.asType(mt);
 x = mh.invokeExact((Object)1, (Object)2, (Object)3);
 // invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-assert(x.equals(java.util.Arrays.asList(1,2,3)));
-// mt is { =&gt; int}
+assertEquals(x, java.util.Arrays.asList(1,2,3));
+// mt is ()int
 mt = MethodType.methodType(int.class);
 mh = lookup.findVirtual(java.util.List.class, "size", mt);
 i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
@@ -193,37 +263,239 @@
             }}
     }
 
+    @Test public void testAsSpreader() throws Throwable {
+        {{
+{} /// JAVADOC
+MethodHandle equals = publicLookup()
+  .findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
+assert( (boolean) equals.invokeExact("me", (Object)"me"));
+assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
+// spread both arguments from a 2-array:
+MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
+assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
+assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
+// spread both arguments from a String array:
+MethodHandle eq2s = equals.asSpreader(String[].class, 2);
+assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" }));
+assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" }));
+// spread second arguments from a 1-array:
+MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
+assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
+assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
+// spread no arguments from a 0-array or null:
+MethodHandle eq0 = equals.asSpreader(Object[].class, 0);
+assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0]));
+assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null));
+// asSpreader and asCollector are approximate inverses:
+for (int n = 0; n <= 2; n++) {
+    for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) {
+        MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n);
+        assert( (boolean) equals2.invokeWithArguments("me", "me"));
+        assert(!(boolean) equals2.invokeWithArguments("me", "thee"));
+    }
+}
+MethodHandle caToString = publicLookup()
+  .findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
+assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
+MethodHandle caString3 = caToString.asCollector(char[].class, 3);
+assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
+MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
+assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
+            }}
+    }
+
+    @Test public void testAsCollector() throws Throwable {
+        {{
+{} /// JAVADOC
+MethodHandle deepToString = publicLookup()
+  .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
+assertEquals("[won]",   (String) deepToString.invokeExact(new Object[]{"won"}));
+MethodHandle ts1 = deepToString.asCollector(Object[].class, 1);
+assertEquals(methodType(String.class, Object.class), ts1.type());
+//assertEquals("[won]", (String) ts1.invokeExact(         new Object[]{"won"})); //FAIL
+assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"}));
+// arrayType can be a subtype of Object[]
+MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
+assertEquals(methodType(String.class, String.class, String.class), ts2.type());
+assertEquals("[two, too]", (String) ts2.invokeExact("two", "too"));
+MethodHandle ts0 = deepToString.asCollector(Object[].class, 0);
+assertEquals("[]", (String) ts0.invokeExact());
+// collectors can be nested, Lisp-style
+MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2);
+assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D")));
+// arrayType can be any primitive array type
+MethodHandle bytesToString = publicLookup()
+  .findStatic(Arrays.class, "toString", methodType(String.class, byte[].class))
+  .asCollector(byte[].class, 3);
+assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3));
+MethodHandle longsToString = publicLookup()
+  .findStatic(Arrays.class, "toString", methodType(String.class, long[].class))
+  .asCollector(long[].class, 1);
+assertEquals("[123]", (String) longsToString.invokeExact((long)123));
+            }}
+    }
+
     @Test public void testAsVarargsCollector() throws Throwable {
         {{
 {} /// JAVADOC
+MethodHandle deepToString = publicLookup()
+  .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
+MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class);
+assertEquals("[won]",   (String) ts1.invokeExact(    new Object[]{"won"}));
+assertEquals("[won]",   (String) ts1.invoke(         new Object[]{"won"}));
+assertEquals("[won]",   (String) ts1.invoke(                      "won" ));
+assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
+// findStatic of Arrays.asList(...) produces a variable arity method handle:
 MethodHandle asList = publicLookup()
-  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
-  .asVarargsCollector(Object[].class);
+  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
+assertEquals(methodType(List.class, Object[].class), asList.type());
+assert(asList.isVarargsCollector());
 assertEquals("[]", asList.invoke().toString());
 assertEquals("[1]", asList.invoke(1).toString());
 assertEquals("[two, too]", asList.invoke("two", "too").toString());
-Object[] argv = { "three", "thee", "tee" };
+String[] argv = { "three", "thee", "tee" };
 assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
+assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString());
 List ls = (List) asList.invoke((Object)argv);
 assertEquals(1, ls.size());
 assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
             }}
     }
 
-    @Test public void testVarargsCollectorSuppression() throws Throwable {
+    @Test public void testAsFixedArity() throws Throwable {
         {{
 {} /// JAVADOC
-MethodHandle vamh = publicLookup()
+MethodHandle asListVar = publicLookup()
   .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
   .asVarargsCollector(Object[].class);
-MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
-assert(vamh.type().equals(mh.type()));
-assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString());
-boolean failed = false;
-try { mh.invoke(1,2,3); }
-catch (WrongMethodTypeException ex) { failed = true; }
-assert(failed);
+MethodHandle asListFix = asListVar.asFixedArity();
+assertEquals("[1]", asListVar.invoke(1).toString());
+Exception caught = null;
+try { asListFix.invoke((Object)1); }
+catch (Exception ex) { caught = ex; }
+assert(caught instanceof ClassCastException);
+assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
+try { asListFix.invoke("two", "too"); }
+catch (Exception ex) { caught = ex; }
+assert(caught instanceof WrongMethodTypeException);
+Object[] argv = { "three", "thee", "tee" };
+assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
+assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
+assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
+assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
+            }}
+    }
+
+    @Test public void testAsTypeCornerCases() throws Throwable {
+        {{
+{} /// JAVADOC
+MethodHandle i2s = publicLookup()
+  .findVirtual(Integer.class, "toString", methodType(String.class));
+i2s = i2s.asType(i2s.type().unwrap());
+MethodHandle l2s = publicLookup()
+  .findVirtual(Long.class, "toString", methodType(String.class));
+l2s = l2s.asType(l2s.type().unwrap());
+
+Exception caught = null;
+try { i2s.asType(methodType(String.class, String.class)); }
+catch (Exception ex) { caught = ex; }
+assert(caught instanceof WrongMethodTypeException);
+
+i2s.asType(methodType(String.class, byte.class));
+i2s.asType(methodType(String.class, Byte.class));
+i2s.asType(methodType(String.class, Character.class));
+i2s.asType(methodType(String.class, Integer.class));
+l2s.asType(methodType(String.class, byte.class));
+l2s.asType(methodType(String.class, Byte.class));
+l2s.asType(methodType(String.class, Character.class));
+l2s.asType(methodType(String.class, Integer.class));
+l2s.asType(methodType(String.class, Long.class));
+
+caught = null;
+try { i2s.asType(methodType(String.class, Long.class)); }
+catch (Exception ex) { caught = ex; }
+assert(caught instanceof WrongMethodTypeException);
+
+MethodHandle i2sGen = i2s.asType(methodType(String.class, Object.class));
+MethodHandle l2sGen = l2s.asType(methodType(String.class, Object.class));
+
+i2sGen.invoke(42);  // int -> Integer -> Object -> Integer -> int
+i2sGen.invoke((byte)4);  // byte -> Byte -> Object -> Byte -> byte -> int
+l2sGen.invoke(42);  // int -> Integer -> Object -> Integer -> int
+l2sGen.invoke((byte)4);  // byte -> Byte -> Object -> Byte -> byte -> int
+l2sGen.invoke(0x420000000L);
+
+caught = null;
+try { i2sGen.invoke(0x420000000L); } // long -> Long -> Object -> Integer CCE
+catch (Exception ex) { caught = ex; }
+assert(caught instanceof ClassCastException);
+
+caught = null;
+try { i2sGen.invoke("asdf"); } // String -> Object -> Integer CCE
+catch (Exception ex) { caught = ex; }
+assert(caught instanceof ClassCastException);
 {}
             }}
     }
+
+    @Test public void testMutableCallSite() throws Throwable {
+        {{
+{} /// JAVADOC
+MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
+MethodHandle MH_name = name.dynamicInvoker();
+MethodType MT_str1 = MethodType.methodType(String.class);
+MethodHandle MH_upcase = MethodHandles.lookup()
+    .findVirtual(String.class, "toUpperCase", MT_str1);
+MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
+name.setTarget(MethodHandles.constant(String.class, "Rocky"));
+assertEquals("ROCKY", (String) worker1.invokeExact());
+name.setTarget(MethodHandles.constant(String.class, "Fred"));
+assertEquals("FRED", (String) worker1.invokeExact());
+// (mutation can be continued indefinitely)
+/*
+ * </pre></blockquote>
+ * <p>
+ * The same call site may be used in several places at once.
+ * <blockquote><pre>
+ */
+MethodType MT_str2 = MethodType.methodType(String.class, String.class);
+MethodHandle MH_cat = lookup().findVirtual(String.class,
+  "concat", methodType(String.class, String.class));
+MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
+MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
+assertEquals("Fred, dear?", (String) worker2.invokeExact());
+name.setTarget(MethodHandles.constant(String.class, "Wilma"));
+assertEquals("WILMA", (String) worker1.invokeExact());
+assertEquals("Wilma, dear?", (String) worker2.invokeExact());
+{}
+            }}
+    }
+
+    @Test public void testSwitchPoint() throws Throwable {
+        {{
+{} /// JAVADOC
+MethodHandle MH_strcat = MethodHandles.lookup()
+    .findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
+SwitchPoint spt = new SwitchPoint();
+assert(spt.isValid());
+// the following steps may be repeated to re-use the same switch point:
+MethodHandle worker1 = MH_strcat;
+MethodHandle worker2 = MethodHandles.permuteArguments(MH_strcat, MH_strcat.type(), 1, 0);
+MethodHandle worker = spt.guardWithTest(worker1, worker2);
+assertEquals("method", (String) worker.invokeExact("met", "hod"));
+SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
+assert(!spt.isValid());
+assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
+{}
+            }}
+    }
+
+    /* ---- TEMPLATE ----
+    @Test public void testFoo() throws Throwable {
+        {{
+{} /// JAVADOC
+{}
+            }}
+    }
+    */
 }
diff --git a/test/java/lang/invoke/MethodHandlesTest.java b/test/java/lang/invoke/MethodHandlesTest.java
index ded00ae..1fd6a5c 100644
--- a/test/java/lang/invoke/MethodHandlesTest.java
+++ b/test/java/lang/invoke/MethodHandlesTest.java
@@ -100,6 +100,31 @@
         // ValueConversions.varargsArray: UnsupportedOperationException: NYI: cannot form a varargs array of length 13
         testInsertArguments(0, 0, MAX_ARG_INCREASE+10);
     }
+    @Test @Ignore("permuteArguments has trouble with double slots")
+    public void testFail_7() throws Throwable {
+        testPermuteArguments(new Object[]{10, 200L},
+                             new Class<?>[]{Integer.class, long.class},
+                             new int[]{1,0});
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class<?>[]{Integer.class, long.class, long.class},
+                             new int[]{2,0,1}); //rot
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class<?>[]{Integer.class, long.class, long.class},
+                             new int[]{1,2,0}); //rot
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class<?>[]{Integer.class, long.class, long.class},
+                             new int[]{2,1,0}); //swap
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class<?>[]{Integer.class, long.class, long.class},
+                             new int[]{0,1,2,2}); //dup
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class<?>[]{Integer.class, long.class, long.class},
+                             new int[]{2,0,1,2});
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class<?>[]{Integer.class, long.class, long.class},
+                             new int[]{2,2,0,1});
+        testPermuteArguments(4, Integer.class,  2, long.class,    6);
+    }
     static final int MAX_ARG_INCREASE = 3;
 
     public MethodHandlesTest() {
@@ -356,7 +381,7 @@
         ArrayList<Class<?>> argTypes = new ArrayList<Class<?>>(targetType.parameterList());
         Collections.fill(argTypes.subList(beg, end), argType);
         MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes);
-        return MethodHandles.convertArguments(target, ttype2);
+        return target.asType(ttype2);
     }
 
     // This lookup is good for all members in and under MethodHandlesTest.
@@ -1005,13 +1030,13 @@
         Class<?> vtype = ftype;
         if (ftype != int.class)  vtype = Object.class;
         if (isGetter) {
-            mh = MethodHandles.convertArguments(mh, mh.type().generic()
-                                                .changeReturnType(vtype));
+            mh = mh.asType(mh.type().generic()
+                           .changeReturnType(vtype));
         } else {
             int last = mh.type().parameterCount() - 1;
-            mh = MethodHandles.convertArguments(mh, mh.type().generic()
-                                                .changeReturnType(void.class)
-                                                .changeParameterType(last, vtype));
+            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
@@ -1139,7 +1164,7 @@
             // FIXME: change Integer.class and (Integer) below to int.class and (int) below.
             MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class);
             if (testSetter)  gtype = gtype.changeReturnType(void.class);
-            mh = MethodHandles.convertArguments(mh, gtype);
+            mh = mh.asType(gtype);
         }
         Object sawValue, expValue;
         List<Object> model = array2list(array);
@@ -1233,11 +1258,10 @@
     }
 
     void testConvert(MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
-        testConvert(true, false, id, rtype, name, params);
-        testConvert(true, true,  id, rtype, name, params);
+        testConvert(true, id, rtype, name, params);
     }
 
-    void testConvert(boolean positive, boolean useAsType,
+    void testConvert(boolean positive,
                      MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
         countTest(positive);
         MethodType idType = id.type();
@@ -1265,10 +1289,7 @@
         MethodHandle target = null;
         RuntimeException error = null;
         try {
-            if (useAsType)
-                target = id.asType(newType);
-            else
-                target = MethodHandles.convertArguments(id, newType);
+            target = id.asType(newType);
         } catch (RuntimeException ex) {
             error = ex;
         }
@@ -1293,11 +1314,11 @@
                                MethodType.methodType(Object.class, String.class, Object[].class));
         vac0 = vac0.bindTo("vac");
         MethodHandle vac = vac0.asVarargsCollector(Object[].class);
-        testConvert(true,  true,  vac.asType(MethodType.genericMethodType(0)), null, "vac");
-        testConvert(true,  true,  vac.asType(MethodType.genericMethodType(0)), null, "vac");
+        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,  true,  vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
-            testConvert(true,  true,  vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
+            testConvert(true, vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
+            testConvert(true, vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
         }
     }
 
@@ -1306,8 +1327,8 @@
         if (CAN_SKIP_WORKING)  return;
         startTest("permuteArguments");
         testPermuteArguments(4, Integer.class,  2, String.class,  0);
-        //testPermuteArguments(6, Integer.class,  0, null,         30);
-        //testPermuteArguments(4, Integer.class,  1, int.class,     6);
+        testPermuteArguments(6, Integer.class,  0, null,         30);
+        //testPermuteArguments(4, Integer.class,  2, long.class,    6);  // FIXME Fail_7
     }
     public void testPermuteArguments(int max, Class<?> type1, int t2c, Class<?> type2, int dilution) throws Throwable {
         if (verbosity >= 2)
@@ -1421,8 +1442,9 @@
         }
         MethodType inType  = MethodType.methodType(Object.class, types);
         MethodType outType = MethodType.methodType(Object.class, permTypes);
-        MethodHandle target = MethodHandles.convertArguments(varargsList(outargs), outType);
+        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)) {
@@ -1666,7 +1688,7 @@
         countTest();
         MethodHandle target = varargsList(nargs);
         MethodHandle filter = varargsList(1);
-        filter = MethodHandles.convertArguments(filter, filter.type().generic());
+        filter = filter.asType(filter.type().generic());
         Object[] argsToPass = randomArgs(nargs, Object.class);
         if (verbosity >= 3)
             System.out.println("filter "+target+" at "+pos+" with "+filter);
@@ -1807,7 +1829,7 @@
         // generic invoker
         countTest();
         inv = MethodHandles.invoker(type);
-        if (nargs <= 3) {
+        if (nargs <= 3 && type == type.generic()) {
             calledLog.clear();
             switch (nargs) {
             case 0:
@@ -1833,10 +1855,16 @@
         // varargs invoker #0
         calledLog.clear();
         inv = MethodHandles.spreadInvoker(type, 0);
-        result = inv.invokeExact(target, args);
+        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) {
+        if (nargs >= 1 && type == type.generic()) {
             // varargs invoker #1
             calledLog.clear();
             inv = MethodHandles.spreadInvoker(type, 1);
@@ -1844,7 +1872,7 @@
             if (testRetCode)  assertEquals(code, result);
             assertCalled("invokee", args);
         }
-        if (nargs >= 2) {
+        if (nargs >= 2 && type == type.generic()) {
             // varargs invoker #2
             calledLog.clear();
             inv = MethodHandles.spreadInvoker(type, 2);
@@ -1852,7 +1880,7 @@
             if (testRetCode)  assertEquals(code, result);
             assertCalled("invokee", args);
         }
-        if (nargs >= 3) {
+        if (nargs >= 3 && type == type.generic()) {
             // varargs invoker #3
             calledLog.clear();
             inv = MethodHandles.spreadInvoker(type, 3);
@@ -1865,6 +1893,10 @@
             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<Object>(targetPlusArgs);
             List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
             Object[] tail = tailList.toArray();
@@ -2045,7 +2077,7 @@
         //System.out.println("throwing with "+target+" : "+thrown);
         MethodType expectedType = MethodType.methodType(returnType, exType);
         assertEquals(expectedType, target.type());
-        target = MethodHandles.convertArguments(target, target.type().generic());
+        target = target.asType(target.type().generic());
         Throwable caught = null;
         try {
             Object res = target.invokeExact((Object) thrown);
@@ -2117,12 +2149,12 @@
         if (mode.endsWith("/return")) {
             if (mode.equals("unbox/return")) {
                 // fail on return to ((Integer)surprise).intValue
-                surprise = MethodHandles.convertArguments(surprise, MethodType.methodType(int.class, Object.class));
-                identity = MethodHandles.convertArguments(identity, MethodType.methodType(int.class, Object.class));
+                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 = MethodHandles.convertArguments(surprise, MethodType.methodType(Integer.class, Object.class));
-                identity = MethodHandles.convertArguments(identity, MethodType.methodType(Integer.class, Object.class));
+                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;
@@ -2134,14 +2166,14 @@
                 callee = Surprise.BOX_IDENTITY;
             }
             if (callee != null) {
-                callee = MethodHandles.convertArguments(callee, MethodType.genericMethodType(1));
+                callee = callee.asType(MethodType.genericMethodType(1));
                 surprise = MethodHandles.filterArguments(callee, 0, surprise);
                 identity = MethodHandles.filterArguments(callee, 0, identity);
             }
         }
         assertNotSame(mode, surprise, surprise0);
-        identity = MethodHandles.convertArguments(identity, MethodType.genericMethodType(1));
-        surprise = MethodHandles.convertArguments(surprise, MethodType.genericMethodType(1));
+        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);
@@ -2230,14 +2262,14 @@
         {
             MethodType mt = MethodType.methodType(void.class);
             MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "runForRunnable", mt);
-            Runnable proxy = MethodHandles.asInstance(mh, Runnable.class);
+            Runnable proxy = MethodHandleProxies.asInterfaceInstance(Runnable.class, mh);
             proxy.run();
             assertCalled("runForRunnable");
         }
         {
             MethodType mt = MethodType.methodType(Object.class, Fooable.class, Object.class);
             MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "fooForFooable", mt);
-            Fooable proxy = MethodHandles.asInstance(mh, Fooable.class);
+            Fooable proxy = MethodHandleProxies.asInterfaceInstance(Fooable.class, mh);
             Object[] args = randomArgs(mt.parameterArray());
             Object result = proxy.foo((Fooable) args[0], args[1]);
             assertCalled("fooForFooable", args);
@@ -2251,7 +2283,7 @@
                                             }) {
             MethodHandle mh = MethodHandles.throwException(void.class, Throwable.class);
             mh = MethodHandles.insertArguments(mh, 0, ex);
-            WillThrow proxy = MethodHandles.asInstance(mh, WillThrow.class);
+            WillThrow proxy = MethodHandleProxies.asInterfaceInstance(WillThrow.class, mh);
             try {
                 proxy.willThrow();
                 System.out.println("Failed to throw: "+ex);
@@ -2279,7 +2311,7 @@
                                              CharSequence.class,
                                              Example.class }) {
             try {
-                MethodHandles.asInstance(varargsArray(0), nonSAM);
+                MethodHandleProxies.asInterfaceInstance(nonSAM, varargsArray(0));
                 System.out.println("Failed to throw");
                 assertTrue(false);
             } catch (IllegalArgumentException ex) {
diff --git a/test/java/lang/invoke/indify/Indify.java b/test/java/lang/invoke/indify/Indify.java
index 6c0661d..caa0820 100644
--- a/test/java/lang/invoke/indify/Indify.java
+++ b/test/java/lang/invoke/indify/Indify.java
@@ -524,6 +524,8 @@
             if (verifySpecifierCount >= 0) {
                 List<Object[]> specs = bootstrapMethodSpecifiers(false);
                 int specsLen = (specs == null ? 0 : specs.size());
+                // Pass by specsLen == 0, to help with associated (inner) classes.
+                if (specsLen == 0)  specsLen = verifySpecifierCount;
                 if (specsLen != verifySpecifierCount) {
                     throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount);
                 }