Merge
diff --git a/test/java/dyn/MethodHandlesTest.java b/test/java/dyn/MethodHandlesTest.java
index 200d260..10a513c 100644
--- a/test/java/dyn/MethodHandlesTest.java
+++ b/test/java/dyn/MethodHandlesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2010, 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
@@ -26,19 +26,18 @@
 /* @test
  * @summary unit tests for java.dyn.MethodHandles
  * @compile -XDinvokedynamic MethodHandlesTest.java
- * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic jdk.java.dyn.MethodHandlesTest
+ * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic test.java.dyn.MethodHandlesTest
  */
 
-package jdk.java.dyn;
+package test.java.dyn;
 
 import java.dyn.*;
 import java.dyn.MethodHandles.Lookup;
 import java.lang.reflect.*;
 import java.util.*;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 import org.junit.*;
 import static org.junit.Assert.*;
+import static org.junit.Assume.*;
 
 
 /**
@@ -47,7 +46,7 @@
  */
 public class MethodHandlesTest {
     // How much output?
-    static int verbosity = 1;
+    static int verbosity = 0;
 
     // Set this true during development if you want to fast-forward to
     // a particular new, non-working test.  Tests which are known to
@@ -57,55 +56,79 @@
 
     // Set true to test more calls.  If false, some tests are just
     // lookups, without exercising the actual method handle.
-    static boolean DO_MORE_CALLS = false;
+    static boolean DO_MORE_CALLS = true;
 
 
     @Test
     public void testFirst() throws Throwable {
         verbosity += 9; try {
             // left blank for debugging
-        } finally { verbosity -= 9; }
+        } finally { printCounts(); verbosity -= 9; }
     }
 
     // current failures
     @Test @Ignore("failure in call to makeRawRetypeOnly in ToGeneric")
     public void testFail_1() throws Throwable {
+        // AMH.<init>: IllegalArgumentException: bad adapter (conversion=0xfffab300): adapter pushes too many parameters
         testSpreadArguments(int.class, 0, 6);
     }
-    @Test @Ignore("failure in JVM when expanding the stack")
+    @Test @Ignore("failure in JVM when expanding the stack using asm stub for _adapter_spread_args")
     public void testFail_2() throws Throwable {
         // if CONV_OP_IMPLEMENTED_MASK includes OP_SPREAD_ARGS, this crashes:
         testSpreadArguments(Object.class, 0, 2);
     }
     @Test @Ignore("IllArgEx failure in call to ToGeneric.make")
     public void testFail_3() throws Throwable {
+        // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
         testSpreadArguments(int.class, 1, 2);
     }
     @Test @Ignore("IllArgEx failure in call to ToGeneric.make")
     public void testFail_4() throws Throwable {
+        // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
         testCollectArguments(int.class, 1, 2);
     }
     @Test @Ignore("cannot collect leading primitive types")
     public void testFail_5() throws Throwable {
+        // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
         testInvokers(MethodType.genericMethodType(2).changeParameterType(0, int.class));
     }
     @Test @Ignore("should not insert arguments beyond MethodHandlePushLimit")
     public void testFail_6() throws Throwable {
-        testInsertArguments(0, 0, MAX_ARG_INCREASE+1);
+        // ValueConversions.varargsArray: UnsupportedOperationException: NYI: cannot form a varargs array of length 13
+        testInsertArguments(0, 0, MAX_ARG_INCREASE+10);
     }
     static final int MAX_ARG_INCREASE = 3;
 
     public MethodHandlesTest() {
     }
 
+    @Before
+    public void checkImplementedPlatform() {
+        boolean platformOK = false;
+        Properties properties = System.getProperties();
+        String vers = properties.getProperty("java.vm.version");
+        String name = properties.getProperty("java.vm.name");
+        String arch = properties.getProperty("os.arch");
+        if ((arch.equals("i386")  || arch.equals("amd64") ||
+             arch.equals("sparc") || arch.equals("sparcv9")) &&
+            (name.contains("Client") || name.contains("Server"))
+            ) {
+            platformOK = true;
+        } else {
+            System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch));
+        }
+        assumeTrue(platformOK);
+    }
+
     String testName;
     int posTests, negTests;
     @After
     public void printCounts() {
-        if (verbosity >= 1 && (posTests | negTests) != 0) {
+        if (verbosity >= 2 && (posTests | negTests) != 0) {
             System.out.println();
             if (posTests != 0)  System.out.println("=== "+testName+": "+posTests+" positive test cases run");
             if (negTests != 0)  System.out.println("=== "+testName+": "+negTests+" negative test cases run");
+            posTests = negTests = 0;
         }
     }
     void countTest(boolean positive) {
@@ -115,7 +138,7 @@
     void countTest() { countTest(true); }
     void startTest(String name) {
         if (testName != null)  printCounts();
-        if (verbosity >= 0)
+        if (verbosity >= 1)
             System.out.println(name);
         posTests = negTests = 0;
         testName = name;
@@ -125,7 +148,7 @@
     public static void setUpClass() throws Exception {
         calledLog.clear();
         calledLog.add(null);
-        nextArg = 1000000;
+        nextArgVal = INITIAL_ARG_VAL;
     }
 
     @AfterClass
@@ -144,18 +167,17 @@
     static void assertCalled(String name, Object... args) {
         Object expected = logEntry(name, args);
         Object actual   = calledLog.get(calledLog.size() - 1);
-        if (expected.equals(actual))  return;
+        if (expected.equals(actual) && verbosity < 9)  return;
         System.out.println("assertCalled "+name+":");
         System.out.println("expected:   "+expected);
         System.out.println("actual:     "+actual);
         System.out.println("ex. types:  "+getClasses(expected));
         System.out.println("act. types: "+getClasses(actual));
-        assertEquals("previous method call types", expected, actual);
         assertEquals("previous method call", expected, actual);
     }
     static void printCalled(MethodHandle target, String name, Object... args) {
-        if (verbosity >= 2)
-            System.out.println("calling "+logEntry(name, args)+" on "+target);
+        if (verbosity >= 3)
+            System.out.println("calling MH="+target+" to "+name+Arrays.toString(args));
     }
 
     static Object castToWrapper(Object value, Class<?> dst) {
@@ -188,11 +210,40 @@
         return null;
     }
 
-    static int nextArg;
+    static final int ONE_MILLION = (1000*1000),  // first int value
+                     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
+        val >>= 1;
+        if (moreBits)
+            // Guarantee some bits in the high word.
+            // In any case keep the decimal representation simple-looking,
+            // with lots of zeroes, so as not to make the printed decimal
+            // strings unnecessarily noisy.
+            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)
+            // produce a 64-bit result something like
+            // ((TEN_BILLION+1) * (ONE_MILLION+(smallint)))
+            // Example: 10_000_420_001_000_042.
+            return nextArg(true);
+        return (long) nextArg();
+    }
+
     static Object randomArg(Class<?> param) {
-        Object wrap = castToWrapperOrNull(nextArg, param);
+        Object wrap = castToWrapperOrNull(nextArg(param), param);
         if (wrap != null) {
-            nextArg++;
             return wrap;
         }
 //        import sun.dyn.util.Wrapper;
@@ -202,7 +253,7 @@
 //        if (wrap != Wrapper.OBJECT)
 //            return wrap.wrap(nextArg++);
         if (param.isInterface() || param.isAssignableFrom(String.class))
-            return "#"+(nextArg++);
+            return "#"+nextArg();
         else
             try {
                 return param.newInstance();
@@ -277,7 +328,7 @@
     // Subject methods...
     static class Example implements IntExample {
         final String name;
-        public Example() { name = "Example#"+(nextArg++); }
+        public Example() { name = "Example#"+nextArg(); }
         protected Example(String name) { this.name = name; }
         protected Example(int x) { this(); called("protected <init>", this, x); }
         @Override public String toString() { return name; }
@@ -301,43 +352,56 @@
         public static Object   s5(long x, int y) { return called("s5", x, y); }
         public static Object   s6(int x, long y) { return called("s6", x, y); }
         public static Object   s7(float x, double y) { return called("s7", x, y); }
+
+        static final Lookup EXAMPLE = MethodHandles.lookup();  // for testing findSpecial
     }
+    static final Lookup EXAMPLE = Example.EXAMPLE;
     public static class PubExample extends Example {
+        public PubExample() { super("PubExample#"+nextArg()); }
     }
     static class SubExample extends Example {
         @Override public void  v0()     { called("Sub/v0", this); }
         @Override void         pkg_v0() { called("Sub/pkg_v0", this); }
         private      SubExample(int x)  { called("<init>", this, x); }
-        public SubExample() { super("SubExample#"+(nextArg++)); }
+        public SubExample() { super("SubExample#"+nextArg()); }
     }
     public static interface IntExample {
         public void            v0();
         static class Impl implements IntExample {
             public void        v0()     { called("Int/v0", this); }
             final String name;
-            public Impl() { name = "Example#"+(nextArg++); }
+            public Impl() { name = "Impl#"+nextArg(); }
+            @Override public String toString() { return name; }
         }
     }
 
     static final Object[][][] ACCESS_CASES = {
-        { { false, PUBLIC }, { false, PACKAGE }, { false, PRIVATE } },
-        { { false, PUBLIC }, { false, PACKAGE }, { true,  PRIVATE } },
-        { { false, PUBLIC }, { true,  PACKAGE }, { true,  PRIVATE } },
-        { { true,  PUBLIC }, { true,  PACKAGE }, { true,  PRIVATE } },
+        { { false, PUBLIC }, { false, PACKAGE }, { false, PRIVATE }, { false, EXAMPLE } }, //[0]: all false
+        { { false, PUBLIC }, { false, PACKAGE }, { true,  PRIVATE }, { true,  EXAMPLE } }, //[1]: only PRIVATE
+        { { false, PUBLIC }, { true,  PACKAGE }, { true,  PRIVATE }, { true,  EXAMPLE } }, //[2]: PUBLIC false
+        { { true,  PUBLIC }, { true,  PACKAGE }, { true,  PRIVATE }, { true,  EXAMPLE } }, //[3]: all true
     };
 
-    static Object[][] accessCases(Class<?> defc, String name) {
-        if (name.contains("pri_")) {
-            return ACCESS_CASES[1]; // PRIVATE only
-        } else if (name.contains("pkg_")) {
-            return ACCESS_CASES[2]; // not PUBLIC
+    static Object[][] accessCases(Class<?> defc, String name, boolean isSpecial) {
+        Object[][] cases;
+        if (name.contains("pri_") || isSpecial) {
+            cases = ACCESS_CASES[1]; // PRIVATE only
+        } else if (name.contains("pkg_") || !Modifier.isPublic(defc.getModifiers())) {
+            cases = ACCESS_CASES[2]; // not PUBLIC
         } else {
             assertTrue(name.indexOf('_') < 0);
             boolean pubc = Modifier.isPublic(defc.getModifiers());
             if (pubc)
-                return ACCESS_CASES[3]; // all access levels
-            return ACCESS_CASES[2]; // PACKAGE but not PUBLIC
+                cases = ACCESS_CASES[3]; // all access levels
+            else
+                cases = ACCESS_CASES[2]; // PACKAGE but not PUBLIC
         }
+        if (defc != Example.class && cases[cases.length-1][1] == EXAMPLE)
+            cases = Arrays.copyOfRange(cases, 0, cases.length-1);
+        return cases;
+    }
+    static Object[][] accessCases(Class<?> defc, String name) {
+        return accessCases(defc, name, false);
     }
 
     @Test
@@ -374,12 +438,13 @@
         MethodHandle target = null;
         RuntimeException noAccess = null;
         try {
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
             target = lookup.findStatic(defc, name, type);
         } catch (NoAccessException ex) {
             noAccess = ex;
         }
-        if (verbosity >= 2)
-            System.out.println("findStatic "+lookup+": "+defc+"."+name+"/"+type+" => "+target
+        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);
@@ -391,7 +456,8 @@
         printCalled(target, name, args);
         target.invokeVarargs(args);
         assertCalled(name, args);
-        System.out.print(':');
+        if (verbosity >= 1)
+            System.out.print(':');
     }
 
     @Test
@@ -436,21 +502,20 @@
         MethodHandle target = null;
         RuntimeException noAccess = null;
         try {
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
             target = lookup.findVirtual(defc, methodName, type);
         } catch (NoAccessException ex) {
             noAccess = ex;
         }
-        if (verbosity >= 2)
-            System.out.println("findVirtual "+lookup+": "+defc+"."+name+"/"+type+" => "+target
+        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<?>[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params);
         MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
-        MethodType ttype = target.type();
-        ttype = ttype.changeParameterType(0, defc); // FIXME: test this
-        assertEquals(typeWithSelf, ttype);
+        assertEquals(typeWithSelf, target.type());
         assertTrue(target.toString().contains(methodName));  // rough check
         if (!DO_MORE_CALLS && lookup != PRIVATE)  return;
         Object[] argsWithSelf = randomArgs(paramsWithSelf);
@@ -458,52 +523,61 @@
         printCalled(target, name, argsWithSelf);
         target.invokeVarargs(argsWithSelf);
         assertCalled(name, argsWithSelf);
-        System.out.print(':');
+        if (verbosity >= 1)
+            System.out.print(':');
     }
 
     @Test
     public void testFindSpecial() throws Throwable {
         if (CAN_SKIP_WORKING)  return;
         startTest("findSpecial");
-        testFindSpecial(Example.class, void.class, "v0");
-        testFindSpecial(Example.class, void.class, "pkg_v0");
-        testFindSpecial(false, PRIVATE, Example.class, void.class, "<init>", int.class);
-        testFindSpecial(false, PRIVATE, Example.class, void.class, "bogus");
+        testFindSpecial(SubExample.class, Example.class, void.class, "v0");
+        testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
+        // 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, "<init>", int.class);
+            testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0");
+            testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus");
+        }
     }
 
-    void testFindSpecial(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
-        testFindSpecial(true,  PRIVATE, defc, ret, name, params);
-        testFindSpecial(false, PACKAGE, defc, ret, name, params);
-        testFindSpecial(false, PUBLIC,  defc, ret, name, params);
+    void testFindSpecial(Class<?> specialCaller,
+                         Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+        testFindSpecial(true,  EXAMPLE, specialCaller, defc, ret, name, params);
+        testFindSpecial(true,  PRIVATE, specialCaller, defc, ret, name, params);
+        testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params);
+        testFindSpecial(false, PUBLIC,  specialCaller, defc, ret, name, params);
     }
-    void testFindSpecial(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+    void testFindSpecial(boolean positive, Lookup lookup, Class<?> specialCaller,
+                         Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
         countTest(positive);
         MethodType type = MethodType.methodType(ret, params);
         MethodHandle target = null;
         RuntimeException noAccess = null;
         try {
-            target = lookup.findSpecial(defc, name, type, defc);
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
+            target = lookup.findSpecial(defc, name, type, specialCaller);
         } catch (NoAccessException ex) {
             noAccess = ex;
         }
-        if (verbosity >= 2)
-            System.out.println("findSpecial "+defc+"."+name+"/"+type+" => "+target
-                    +(noAccess == null ? "" : " !! "+noAccess));
+        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
-        Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params);
+        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);
-        MethodType ttype = target.type();
-        ttype = ttype.changeParameterType(0, defc); // FIXME: test this
-        assertEquals(typeWithSelf, ttype);
         assertTrue(target.toString().contains(name));  // rough check
-        if (!DO_MORE_CALLS && lookup != PRIVATE)  return;
+        if (!DO_MORE_CALLS && lookup != PRIVATE && lookup != EXAMPLE)  return;
         Object[] args = randomArgs(paramsWithSelf);
         printCalled(target, name, args);
         target.invokeVarargs(args);
         assertCalled(name, args);
-        System.out.print(':');
     }
 
     @Test
@@ -538,11 +612,12 @@
         MethodHandle target = null;
         RuntimeException noAccess = null;
         try {
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
             target = lookup.bind(receiver, methodName, type);
         } catch (NoAccessException ex) {
             noAccess = ex;
         }
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target
                     +(noAccess == null ? "" : " !! "+noAccess));
         if (positive && noAccess != null)  throw noAccess;
@@ -554,7 +629,8 @@
         target.invokeVarargs(args);
         Object[] argsWithReceiver = cat(array(Object[].class, receiver), args);
         assertCalled(name, argsWithReceiver);
-        System.out.print(':');
+        if (verbosity >= 1)
+            System.out.print(':');
     }
 
     @Test
@@ -567,10 +643,10 @@
 
         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, 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");
@@ -584,10 +660,17 @@
 
     void testUnreflect(Class<?> defc, boolean isStatic, Class<?> ret, String name, Class<?>... params) throws Throwable {
         for (Object[] ac : accessCases(defc, name)) {
-            testUnreflect((Boolean)ac[0], (Lookup)ac[1], defc, isStatic, ret, name, params);
+            testUnreflectMaybeSpecial(null, (Boolean)ac[0], (Lookup)ac[1], defc, (isStatic ? null : defc), ret, name, params);
         }
     }
-    void testUnreflect(boolean positive, Lookup lookup, Class<?> defc, boolean isStatic, Class<?> ret, String name, Class<?>... params) throws Throwable {
+    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);
         MethodType type = MethodType.methodType(ret, params);
         Method rmethod = null;
@@ -598,43 +681,67 @@
         } catch (NoSuchMethodException ex) {
             throw new NoAccessException(ex);
         }
-        assertEquals(isStatic, Modifier.isStatic(rmethod.getModifiers()));
+        boolean isStatic = (rcvc == null);
+        boolean isSpecial = (specialCaller != null);
         try {
-            target = lookup.unreflect(rmethod);
+            if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
+            if (isSpecial)
+                target = lookup.unreflectSpecial(rmethod, specialCaller);
+            else
+                target = lookup.unreflect(rmethod);
         } catch (NoAccessException ex) {
             noAccess = ex;
         }
-        if (verbosity >= 2)
-            System.out.println("unreflect "+defc+"."+name+"/"+type+" => "+target
-                    +(noAccess == null ? "" : " !! "+noAccess));
+        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)defc), params);
+            paramsMaybeWithSelf = cat(array(Class[].class, (Class)rcvc), params);
         }
         MethodType typeMaybeWithSelf = MethodType.methodType(ret, paramsMaybeWithSelf);
-        MethodType ttype = target.type();
-        if (!isStatic)
-            ttype = ttype.changeParameterType(0, defc); // FIXME: test this
-        assertEquals(typeMaybeWithSelf, ttype);
+        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.invokeVarargs(argsMaybeWithSelf);
         assertCalled(name, argsMaybeWithSelf);
-        System.out.print(':');
+        if (verbosity >= 1)
+            System.out.print(':');
     }
 
-    @Test @Ignore("unimplemented")
+    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 {
-        Lookup lookup = PRIVATE;  // FIXME: test more lookups than this one
+        if (CAN_SKIP_WORKING)  return;
         startTest("unreflectSpecial");
-        Method m = null;
-        MethodHandle expResult = null;
-        MethodHandle result = lookup.unreflectSpecial(m, Example.class);
-        assertEquals(expResult, result);
-        fail("The test case is a prototype.");
+        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 {
@@ -735,14 +842,14 @@
         for (int i = 0; i <= 1; i++) {
             if (isStatic) {
                 if (type == int.class)
-                    sawValue = mh.<int>invoke();  // do these exactly
+                    sawValue = mh.<int>invokeExact();  // do these exactly
                 else
-                    sawValue = mh.invoke();
+                    sawValue = mh.invokeExact();
             } else {
                 if (type == int.class)
-                    sawValue = mh.<int>invoke((Object) fields);
+                    sawValue = mh.<int>invokeExact((Object) fields);
                 else
-                    sawValue = mh.invoke((Object) fields);
+                    sawValue = mh.invokeExact((Object) fields);
             }
             assertEquals(sawValue, expValue);
             Object random = randomArg(type);
@@ -786,14 +893,14 @@
             Object putValue = randomArg(type);
             if (isStatic) {
                 if (type == int.class)
-                    mh.<void>invoke((int)(Integer)putValue);  // do these exactly
+                    mh.<void>invokeExact((int)(Integer)putValue);  // do these exactly
                 else
-                    mh.<void>invoke(putValue);
+                    mh.<void>invokeExact(putValue);
             } else {
                 if (type == int.class)
-                    mh.<void>invoke((Object) fields, (int)(Integer)putValue);
+                    mh.<void>invokeExact((Object) fields, (int)(Integer)putValue);
                 else
-                    mh.<void>invoke((Object) fields, putValue);
+                    mh.<void>invokeExact((Object) fields, putValue);
             }
             assertEquals(f.get(fields), putValue);
         }
@@ -803,25 +910,31 @@
     @Test
     public void testArrayElementGetter() throws Throwable {
         startTest("arrayElementGetter");
-        testArrayElementGetterSetter(new Object[10], false);
-        testArrayElementGetterSetter(new String[10], false);
-        testArrayElementGetterSetter(new int[10], false);
-        // FIXME: Do the other primitive types.
-        //testArrayElementGetterSetter(new float[10], false);
+        testArrayElementGetterSetter(false);
     }
 
     @Test
     public void testArrayElementSetter() throws Throwable {
         startTest("arrayElementSetter");
-        testArrayElementGetterSetter(new Object[10], true);
-        testArrayElementGetterSetter(new String[10], true);
-        testArrayElementGetterSetter(new int[10], true);
-        // FIXME: Do the other primitive types.
-        //testArrayElementGetterSetter(new float[10], true);
+        testArrayElementGetterSetter(true);
+    }
+
+    public void testArrayElementGetterSetter(boolean testSetter) throws Throwable {
+        testArrayElementGetterSetter(new Object[10], testSetter);
+        testArrayElementGetterSetter(new String[10], testSetter);
+        testArrayElementGetterSetter(new boolean[10], testSetter);
+        testArrayElementGetterSetter(new byte[10], testSetter);
+        testArrayElementGetterSetter(new char[10], testSetter);
+        testArrayElementGetterSetter(new short[10], testSetter);
+        testArrayElementGetterSetter(new int[10], testSetter);
+        testArrayElementGetterSetter(new float[10], testSetter);
+        testArrayElementGetterSetter(new long[10], testSetter);
+        testArrayElementGetterSetter(new double[10], testSetter);
     }
 
     public void testArrayElementGetterSetter(Object array, boolean testSetter) throws Throwable {
         countTest(true);
+        if (verbosity >= 2)  System.out.println("array type = "+array.getClass().getComponentType().getName()+"["+Array.getLength(array)+"]");
         Class<?> arrayType = array.getClass();
         Class<?> elemType = arrayType.getComponentType();
         MethodType expType = !testSetter
@@ -831,7 +944,19 @@
                 ? MethodHandles.arrayElementGetter(arrayType)
                 : MethodHandles.arrayElementSetter(arrayType);
         assertSame(mh.type(), expType);
-        //assertEquals(mh.toString(), f.getName());
+        if (elemType != int.class && elemType != boolean.class) {
+            MethodType gtype;
+            if (true) { // FIXME: remove this path (and remove <void> below in the mh.invokes)
+                gtype = mh.type().changeParameterType(0, Object.class);
+                if (testSetter)
+                    gtype = gtype.changeParameterType(2, Object.class);
+                else
+                    gtype = gtype.changeReturnType(Object.class);
+            } else
+                // FIXME: This simpler path hits a bug in convertArguments => ToGeneric
+                gtype = mh.type().generic().changeParameterType(1, int.class);
+            mh = MethodHandles.convertArguments(mh, gtype);
+        }
         Object sawValue, expValue;
         List<Object> model = array2list(array);
         int length = Array.getLength(array);
@@ -841,22 +966,31 @@
             model.set(i, random);
             if (testSetter) {
                 if (elemType == int.class)
-                    mh.<void>invoke((int[]) array, i, (int)(Integer)random);
+                    mh.<void>invokeExact((int[]) array, i, (int)(Integer)random);
+                else if (elemType == boolean.class)
+                    mh.<void>invokeExact((boolean[]) array, i, (boolean)(Boolean)random);
                 else
-                    mh.invokeGeneric(array, i, random);
+                    mh.<void>invokeExact(array, i, random);
                 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;
                 if (elemType == int.class)
-                    sawValue = mh.<int>invoke((int[]) array, i);
+                    sawValue = mh.<int>invokeExact((int[]) array, i);
+                else if (elemType == boolean.class)
+                    sawValue = mh.<boolean>invokeExact((boolean[]) array, i);
                 else
-                    sawValue = mh.invokeGeneric(array, i);
+                    sawValue = mh.invokeExact(array, i);
                 assertEquals(sawValue, expValue);
                 assertEquals(model, array2list(array));
             }
@@ -906,6 +1040,8 @@
         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 {
@@ -943,7 +1079,7 @@
         } catch (RuntimeException ex) {
             error = ex;
         }
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("convert "+id+ " to "+newType+" => "+target
                     +(error == null ? "" : " !! "+error));
         if (positive && error != null)  throw error;
@@ -954,7 +1090,8 @@
         Object result = target.invokeVarargs(args);
         assertCalled(name, convArgs);
         assertEquals(convResult, result);
-        System.out.print(':');
+        if (verbosity >= 1)
+            System.out.print(':');
     }
 
     @Test
@@ -966,7 +1103,7 @@
         //testPermuteArguments(4, Integer.class,  1, int.class,     6);
     }
     public void testPermuteArguments(int max, Class<?> type1, int t2c, Class<?> type2, int dilution) throws Throwable {
-        if (verbosity >= 1)
+        if (verbosity >= 2)
             System.out.println("permuteArguments "+max+"*"+type1.getName()
                     +(t2c==0?"":"/"+t2c+"*"+type2.getName())
                     +(dilution > 0 ? " with dilution "+dilution : ""));
@@ -1054,7 +1191,7 @@
         }
         int inargs = args.length, outargs = reorder.length;
         assert(inargs == types.length);
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("permuteArguments "+Arrays.toString(reorder));
         Object[] permArgs = new Object[outargs];
         Class<?>[] permTypes = new Class<?>[outargs];
@@ -1062,7 +1199,7 @@
             permArgs[i] = args[reorder[i]];
             permTypes[i] = types[reorder[i]];
         }
-        if (verbosity >= 3) {
+        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));
@@ -1083,13 +1220,14 @@
         if (CAN_SKIP_WORKING)  return;
         startTest("spreadArguments");
         for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) {
-            if (verbosity >= 2)
+            if (verbosity >= 3)
                 System.out.println("spreadArguments "+argType);
+            // FIXME: enable _adapter_spread_args and fix Fail_2
             for (int nargs = 0; nargs < 10; nargs++) {
                 if (argType == int.class && nargs >= 6)  continue; // FIXME Fail_1
                 for (int pos = 0; pos < nargs; pos++) {
                     if (argType == int.class && pos > 0)  continue; // FIXME Fail_3
-                    testSpreadArguments(argType, pos, nargs);
+                     testSpreadArguments(argType, pos, nargs);
                 }
             }
         }
@@ -1098,7 +1236,7 @@
         countTest();
         MethodHandle target = ValueConversions.varargsArray(nargs);
         MethodHandle target2 = changeArgTypes(target, argType);
-        if (verbosity >= 2)
+        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:
@@ -1107,15 +1245,15 @@
             assertArrayEquals(args, check);
             switch (nargs) {
                 case 0:
-                    check = target.<Object[]>invoke();
+                    check = target.<Object[]>invokeExact();
                     assertArrayEquals(args, check);
                     break;
                 case 1:
-                    check = target.<Object[]>invoke(args[0]);
+                    check = target.<Object[]>invokeExact(args[0]);
                     assertArrayEquals(args, check);
                     break;
                 case 2:
-                    check = target.<Object[]>invoke(args[0], args[1]);
+                    check = target.<Object[]>invokeExact(args[0], args[1]);
                     assertArrayEquals(args, check);
                     break;
             }
@@ -1129,7 +1267,7 @@
         MethodHandle result = MethodHandles.spreadArguments(target2, newType);
         Object[] returnValue;
         if (pos == 0) {
-            returnValue = (Object[]) result.invoke(args);
+            returnValue = (Object[]) result.invokeExact(args);
         } else {
             Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
             args1[pos] = Arrays.copyOfRange(args, pos, args.length);
@@ -1143,7 +1281,7 @@
         if (CAN_SKIP_WORKING)  return;
         startTest("collectArguments");
         for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) {
-            if (verbosity >= 2)
+            if (verbosity >= 3)
                 System.out.println("collectArguments "+argType);
             for (int nargs = 0; nargs < 10; nargs++) {
                 for (int pos = 0; pos < nargs; pos++) {
@@ -1167,7 +1305,7 @@
         MethodHandle target = ValueConversions.varargsArray(pos+1);
         target = changeArgTypes(target, 0, pos, argType);
         target = changeArgTypes(target, pos, pos+1, Object[].class);
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]");
         MethodHandle result = MethodHandles.collectArguments(target, newType);
         Object[] returnValue = (Object[]) result.invokeVarargs(args);
@@ -1198,14 +1336,14 @@
         List<Object> resList = Arrays.asList(args);
         List<Object> argsToPass = new ArrayList<Object>(resList);
         List<Object> argsToInsert = argsToPass.subList(pos, pos + ins);
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("insert: "+argsToInsert+" into "+target);
         MethodHandle target2 = MethodHandles.insertArguments(target, pos,
                 (Object[]) argsToInsert.toArray());
         argsToInsert.clear();  // remove from argsToInsert
         Object res2 = target2.invokeVarargs(argsToPass);
         Object res2List = Arrays.asList((Object[])res2);
-        if (verbosity >= 2)
+        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);
@@ -1229,17 +1367,17 @@
         MethodHandle filter = ValueConversions.varargsList(1);
         filter = MethodHandles.convertArguments(filter, filter.type().generic());
         Object[] argsToPass = randomArgs(nargs, Object.class);
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("filter "+target+" at "+pos+" with "+filter);
         MethodHandle[] filters = new MethodHandle[pos*2+1];
         filters[pos] = filter;
         MethodHandle target2 = MethodHandles.filterArguments(target, filters);
         // Simulate expected effect of filter on arglist:
         Object[] filteredArgs = argsToPass.clone();
-        filteredArgs[pos] = filter.invoke(filteredArgs[pos]);
+        filteredArgs[pos] = filter.invokeExact(filteredArgs[pos]);
         List<Object> expected = Arrays.asList(filteredArgs);
         Object result = target2.invokeVarargs(argsToPass);
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("result: "+result);
         if (!expected.equals(result))
             System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+argsToPass+" => "+result);
@@ -1265,18 +1403,18 @@
         MethodHandle target = ValueConversions.varargsList(1 + nargs);
         MethodHandle combine = ValueConversions.varargsList(fold);
         List<Object> argsToPass = Arrays.asList(randomArgs(nargs, Object.class));
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("fold "+target+" with "+combine);
         MethodHandle target2 = MethodHandles.foldArguments(target, combine);
         // Simulate expected effect of combiner on arglist:
         List<Object> expected = new ArrayList<Object>(argsToPass);
         List<Object> argsToFold = expected.subList(pos, pos + fold);
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("fold: "+argsToFold+" into "+target2);
         Object foldedArgs = combine.invokeVarargs(argsToFold);
         argsToFold.add(0, foldedArgs);
         Object result = target2.invokeVarargs(argsToPass);
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("result: "+result);
         if (!expected.equals(result))
             System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result);
@@ -1343,7 +1481,7 @@
     }
 
     public void testInvokers(MethodType type) throws Throwable {
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("test invokers for "+type);
         int nargs = type.parameterCount();
         boolean testRetCode = type.returnType() != void.class;
@@ -1373,16 +1511,16 @@
             calledLog.clear();
             switch (nargs) {
             case 0:
-                result = inv.invoke(target);
+                result = inv.invokeExact(target);
                 break;
             case 1:
-                result = inv.invoke(target, args[0]);
+                result = inv.invokeExact(target, args[0]);
                 break;
             case 2:
-                result = inv.invoke(target, args[0], args[1]);
+                result = inv.invokeExact(target, args[0], args[1]);
                 break;
             case 3:
-                result = inv.invoke(target, args[0], args[1], args[2]);
+                result = inv.invokeExact(target, args[0], args[1], args[2]);
                 break;
             }
             if (testRetCode)  assertEquals(code, result);
@@ -1395,14 +1533,14 @@
         // varargs invoker #0
         calledLog.clear();
         inv = MethodHandles.varargsInvoker(type, 0);
-        result = inv.invoke(target, args);
+        result = inv.invokeExact(target, args);
         if (testRetCode)  assertEquals(code, result);
         assertCalled("invokee", args);
         if (nargs >= 1) {
             // varargs invoker #1
             calledLog.clear();
             inv = MethodHandles.varargsInvoker(type, 1);
-            result = inv.invoke(target, args[0], Arrays.copyOfRange(args, 1, nargs));
+            result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs));
             if (testRetCode)  assertEquals(code, result);
             assertCalled("invokee", args);
         }
@@ -1410,7 +1548,7 @@
             // varargs invoker #2
             calledLog.clear();
             inv = MethodHandles.varargsInvoker(type, 2);
-            result = inv.invoke(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs));
+            result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs));
             if (testRetCode)  assertEquals(code, result);
             assertCalled("invokee", args);
         }
@@ -1418,7 +1556,7 @@
             // varargs invoker #3
             calledLog.clear();
             inv = MethodHandles.varargsInvoker(type, 3);
-            result = inv.invoke(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs));
+            result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs));
             if (testRetCode)  assertEquals(code, result);
             assertCalled("invokee", args);
         }
@@ -1523,7 +1661,7 @@
             default:  equals = argList[0].equals(argList[1]); break;
             }
             String willCall = (equals ? "targetIfEquals" : "fallbackIfNotEquals");
-            if (verbosity >= 2)
+            if (verbosity >= 3)
                 System.out.println(logEntry(willCall, argList));
             Object result = mh.invokeVarargs(argList);
             assertCalled(willCall, argList);
@@ -1552,7 +1690,7 @@
 
     void testCatchException(Class<?> returnType, Throwable thrown, boolean throwIt, int nargs) throws Throwable {
         countTest();
-        if (verbosity >= 2)
+        if (verbosity >= 3)
             System.out.println("catchException rt="+returnType+" throw="+throwIt+" nargs="+nargs);
         Class<? extends Throwable> exType = thrown.getClass();
         MethodHandle throwOrReturn
@@ -1594,9 +1732,10 @@
         //System.out.println("throwing with "+target+" : "+thrown);
         MethodType expectedType = MethodType.methodType(returnType, exType);
         assertEquals(expectedType, target.type());
+        target = MethodHandles.convertArguments(target, target.type().generic());
         Throwable caught = null;
         try {
-            Object res = target.invokeGeneric(thrown);
+            Object res = target.invokeExact((Object) thrown);
             fail("got "+res+" instead of throwing "+thrown);
         } catch (Throwable ex) {
             if (ex != thrown) {
@@ -1647,7 +1786,7 @@
 
     void testCastFailure(String mode, int okCount) throws Throwable {
         countTest(false);
-        if (verbosity > 1)  System.out.println("mode="+mode);
+        if (verbosity > 2)  System.out.println("mode="+mode);
         Surprise boo = new Surprise();
         MethodHandle identity = Surprise.REF_IDENTITY, surprise = boo;
         if (mode.endsWith("/return")) {
@@ -1680,22 +1819,22 @@
         surprise = MethodHandles.convertArguments(surprise, MethodType.genericMethodType(1));
         Object x = 42;
         for (int i = 0; i < okCount; i++) {
-            Object y = identity.invoke(x);
+            Object y = identity.invokeExact(x);
             assertEquals(x, y);
-            Object z = surprise.invoke(x);
+            Object z = surprise.invokeExact(x);
             assertEquals(x, z);
         }
         boo.boo("Boo!");
-        Object y = identity.invoke(x);
+        Object y = identity.invokeExact(x);
         assertEquals(x, y);
         try {
-            Object z = surprise.invoke(x);
+            Object z = surprise.invokeExact(x);
             System.out.println("Failed to throw; got z="+z);
             assertTrue(false);
         } catch (Exception ex) {
-            if (verbosity > 1)
-                System.out.println("caught "+ex);
             if (verbosity > 2)
+                System.out.println("caught "+ex);
+            if (verbosity > 3)
                 ex.printStackTrace();
             assertTrue(ex instanceof ClassCastException
                     // FIXME: accept only one of the two for any given unit test
@@ -1704,6 +1843,39 @@
         }
     }
 
+    static Example userMethod(Object o, String s, int i) {
+        called("userMethod", o, s, i);
+        return null;
+    }
+
+    @Test
+    public void testUserClassInSignature() 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.invokeVarargs(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.invokeVarargs(args);
+        assertCalled(name, args);
+    }
 }
 // Local abbreviated copy of sun.dyn.util.ValueConversions
 class ValueConversions {
@@ -1766,7 +1938,7 @@
         if (nargs < ARRAYS.length)
             return ARRAYS[nargs];
         // else need to spin bytecode or do something else fancy
-        throw new UnsupportedOperationException("NYI");
+        throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs);
     }
 
     private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);