jdk/src/share/classes/java/lang/invoke/MethodHandles.java
changeset 9752 88ab34b6da6d
parent 9731 d0f7a3e441c4
child 9780 6fc3b49cfee4
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java	Tue May 17 19:48:19 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java	Thu May 26 17:37:36 2011 -0700
@@ -180,6 +180,10 @@
      * The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand
      * for reflective objects corresponding to the given members.
      * <p>
+     * In cases where the given member is of variable arity (i.e., a method or constructor)
+     * the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}.
+     * In all other cases, the returned method handle will be of fixed arity.
+     * <p>
      * The equivalence between looked-up method handles and underlying
      * class members can break down in a few ways:
      * <ul>
@@ -201,7 +205,7 @@
      * Access checks are applied in the factory methods of {@code Lookup},
      * when a method handle is created.
      * This is a key difference from the Core Reflection API, since
-     * {@link java.lang.reflect.Method#invoke Method.invoke}
+     * {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}
      * performs access checking against every caller, on every call.
      * <p>
      * All access checks start from a {@code Lookup} object, which
@@ -267,7 +271,7 @@
      * Access checks only apply to named and reflected methods,
      * constructors, and fields.
      * Other method handle creation methods, such as
-     * {@link #convertArguments MethodHandles.convertArguments},
+     * {@link MethodHandle#asType MethodHandle.asType},
      * do not require any access checks, and are done
      * with static methods of {@link MethodHandles},
      * independently of any {@code Lookup} object.
@@ -296,6 +300,12 @@
      *     {@link SecurityManager#checkMemberAccess
      *     smgr.checkMemberAccess(defc, Member.DECLARED)} is called.
      *     (Note that {@code defc} might be the same as {@code refc}.)
+     *     The default implementation of this security manager method
+     *     inspects the stack to determine the original caller of
+     *     the reflective request (such as {@code findStatic}),
+     *     and performs additional permission checks if the
+     *     class loader of {@code defc} differs from the class
+     *     loader of the class from which the reflective request came.
      * <li>If the retrieved member is not public,
      *     and if {@code defc} and {@code refc} are in different class loaders,
      *     and if the class loader of the lookup class is not
@@ -304,8 +314,6 @@
      *     smgr.checkPackageAccess(defcPkg)} is called,
      *     where {@code defcPkg} is the package of {@code defc}.
      * </ul>
-     * In all cases, the requesting class presented to the security
-     * manager will be the lookup class from the current {@code Lookup} object.
      */
     public static final
     class Lookup {
@@ -559,7 +567,10 @@
          * @param type the type of the method
          * @return the desired method handle
          * @throws NoSuchMethodException if the method does not exist
-         * @throws IllegalAccessException if access checking fails, or if the method is not {@code static}
+         * @throws IllegalAccessException if access checking fails,
+         *                                or if the method is not {@code static},
+         *                                or if the method's variable arity modifier bit
+         *                                is set and {@code asVarargsCollector} fails
          * @exception SecurityException if a security manager is present and it
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
@@ -567,6 +578,7 @@
         public
         MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             MemberName method = resolveOrFail(refc, name, type, true);
+            checkSecurityManager(refc, method);  // stack walk magic: do not refactor
             checkMethod(refc, method, true);
             return MethodHandleImpl.findMethod(method, false, lookupClassOrNull());
         }
@@ -601,13 +613,17 @@
          * @param type the type of the method, with the receiver argument omitted
          * @return the desired method handle
          * @throws NoSuchMethodException if the method does not exist
-         * @throws IllegalAccessException if access checking fails, or if the method is {@code static}
+         * @throws IllegalAccessException if access checking fails,
+         *                                or if the method is {@code static}
+         *                                or if the method's variable arity modifier bit
+         *                                is set and {@code asVarargsCollector} fails
          * @exception SecurityException if a security manager is present and it
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
         public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             MemberName method = resolveOrFail(refc, name, type, false);
+            checkSecurityManager(refc, method);  // stack walk magic: do not refactor
             checkMethod(refc, method, false);
             MethodHandle mh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
             return restrictProtectedReceiver(method, mh);
@@ -633,6 +649,8 @@
          * @return the desired method handle
          * @throws NoSuchMethodException if the constructor does not exist
          * @throws IllegalAccessException if access checking fails
+         *                                or if the method's variable arity modifier bit
+         *                                is set and {@code asVarargsCollector} fails
          * @exception SecurityException if a security manager is present and it
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
@@ -641,6 +659,7 @@
             String name = "<init>";
             MemberName ctor = resolveOrFail(refc, name, type, false, false, lookupClassOrNull());
             assert(ctor.isConstructor());
+            checkSecurityManager(refc, ctor);  // stack walk magic: do not refactor
             checkAccess(refc, ctor);
             MethodHandle rawMH = MethodHandleImpl.findMethod(ctor, false, lookupClassOrNull());
             MethodHandle allocMH = MethodHandleImpl.makeAllocator(rawMH);
@@ -658,7 +677,7 @@
                 int arity = type.parameterCount();
                 return mh.asVarargsCollector(type.parameterType(arity-1));
             } else {
-                throw new InternalError("already varargs, but template is not: "+mh);
+                return mh.asFixedArity();
             }
         }
 
@@ -690,6 +709,8 @@
          * @return the desired method handle
          * @throws NoSuchMethodException if the method does not exist
          * @throws IllegalAccessException if access checking fails
+         *                                or if the method's variable arity modifier bit
+         *                                is set and {@code asVarargsCollector} fails
          * @exception SecurityException if a security manager is present and it
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
@@ -698,6 +719,7 @@
                                         Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
             checkSpecialCaller(specialCaller);
             MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller);
+            checkSecurityManager(refc, method);  // stack walk magic: do not refactor
             checkMethod(refc, method, false);
             MethodHandle mh = MethodHandleImpl.findMethod(method, false, specialCaller);
             return restrictReceiver(method, mh, specialCaller);
@@ -721,7 +743,9 @@
          * @throws NullPointerException if any argument is null
          */
         public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
-            return makeAccessor(refc, name, type, false, false);
+            MemberName field = resolveOrFail(refc, name, type, false);
+            checkSecurityManager(refc, field);  // stack walk magic: do not refactor
+            return makeAccessor(refc, field, false, false, 0);
         }
 
         /**
@@ -742,7 +766,9 @@
          * @throws NullPointerException if any argument is null
          */
         public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
-            return makeAccessor(refc, name, type, false, true);
+            MemberName field = resolveOrFail(refc, name, type, false);
+            checkSecurityManager(refc, field);  // stack walk magic: do not refactor
+            return makeAccessor(refc, field, false, true, 0);
         }
 
         /**
@@ -762,7 +788,9 @@
          * @throws NullPointerException if any argument is null
          */
         public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
-            return makeAccessor(refc, name, type, true, false);
+            MemberName field = resolveOrFail(refc, name, type, true);
+            checkSecurityManager(refc, field);  // stack walk magic: do not refactor
+            return makeAccessor(refc, field, false, false, 1);
         }
 
         /**
@@ -782,7 +810,9 @@
          * @throws NullPointerException if any argument is null
          */
         public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
-            return makeAccessor(refc, name, type, true, true);
+            MemberName field = resolveOrFail(refc, name, type, true);
+            checkSecurityManager(refc, field);  // stack walk magic: do not refactor
+            return makeAccessor(refc, field, false, true, 1);
         }
 
         /**
@@ -805,10 +835,13 @@
          * <p>
          * This is equivalent to the following code:
          * <blockquote><pre>
-MethodHandle mh0 = {@link #findVirtual findVirtual}(defc, name, type);
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle mh0 = lookup().{@link #findVirtual findVirtual}(defc, name, type);
 MethodHandle mh1 = mh0.{@link MethodHandle#bindTo bindTo}(receiver);
 MethodType mt1 = mh1.type();
-if (mh0.isVarargsCollector() && mt1.parameterCount() > 0) {
+if (mh0.isVarargsCollector())
   mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1));
 return mh1;
          * </pre></blockquote>
@@ -822,6 +855,8 @@
          * @return the desired method handle
          * @throws NoSuchMethodException if the method does not exist
          * @throws IllegalAccessException if access checking fails
+         *                                or if the method's variable arity modifier bit
+         *                                is set and {@code asVarargsCollector} fails
          * @exception SecurityException if a security manager is present and it
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
@@ -829,13 +864,12 @@
         public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             Class<? extends Object> refc = receiver.getClass(); // may get NPE
             MemberName method = resolveOrFail(refc, name, type, false);
+            checkSecurityManager(refc, method);  // stack walk magic: do not refactor
             checkMethod(refc, method, false);
             MethodHandle dmh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
             MethodHandle bmh = MethodHandleImpl.bindReceiver(dmh, receiver);
             if (bmh == null)
                 throw method.makeAccessException("no access", this);
-            if (dmh.type().parameterCount() == 0)
-                return dmh;  // bound the trailing parameter; no varargs possible
             return fixVarargs(bmh, dmh);
         }
 
@@ -856,6 +890,8 @@
          * @param m the reflected method
          * @return a method handle which can invoke the reflected method
          * @throws IllegalAccessException if access checking fails
+         *                                or if the method's variable arity modifier bit
+         *                                is set and {@code asVarargsCollector} fails
          * @throws NullPointerException if the argument is null
          */
         public MethodHandle unreflect(Method m) throws IllegalAccessException {
@@ -884,6 +920,8 @@
          * @param specialCaller the class nominally calling the method
          * @return a method handle which can invoke the reflected method
          * @throws IllegalAccessException if access checking fails
+         *                                or if the method's variable arity modifier bit
+         *                                is set and {@code asVarargsCollector} fails
          * @throws NullPointerException if any argument is null
          */
         public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
@@ -913,6 +951,8 @@
          * @param c the reflected constructor
          * @return a method handle which can invoke the reflected constructor
          * @throws IllegalAccessException if access checking fails
+         *                                or if the method's variable arity modifier bit
+         *                                is set and {@code asVarargsCollector} fails
          * @throws NullPointerException if the argument is null
          */
         public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException {
@@ -939,7 +979,7 @@
          * @throws NullPointerException if the argument is null
          */
         public MethodHandle unreflectGetter(Field f) throws IllegalAccessException {
-            return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), false);
+            return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), false, -1);
         }
 
         /**
@@ -957,7 +997,7 @@
          * @throws NullPointerException if the argument is null
          */
         public MethodHandle unreflectSetter(Field f) throws IllegalAccessException {
-            return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), true);
+            return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), true, -1);
         }
 
         /// Helper methods, all package-private.
@@ -993,6 +1033,46 @@
                 throw new MemberName(refc).makeAccessException("symbolic reference class is not public", this);
         }
 
+        /**
+         * Perform necessary <a href="MethodHandles.Lookup.html#secmgr">access checks</a>.
+         * This function performs stack walk magic: do not refactor it.
+         */
+        void checkSecurityManager(Class<?> refc, MemberName m) {
+            SecurityManager smgr = System.getSecurityManager();
+            if (smgr == null)  return;
+            if (allowedModes == TRUSTED)  return;
+            // Step 1:
+            smgr.checkMemberAccess(refc, Member.PUBLIC);
+            // Step 2:
+            if (!VerifyAccess.classLoaderIsAncestor(lookupClass, refc))
+                smgr.checkPackageAccess(VerifyAccess.getPackageName(refc));
+            // Step 3:
+            if (m.isPublic()) return;
+            Class<?> defc = m.getDeclaringClass();
+            smgr.checkMemberAccess(defc, Member.DECLARED);  // STACK WALK HERE
+            // Step 4:
+            if (defc != refc)
+                smgr.checkPackageAccess(VerifyAccess.getPackageName(defc));
+
+            // Comment from SM.checkMemberAccess, where which=DECLARED:
+            /*
+             * stack depth of 4 should be the caller of one of the
+             * methods in java.lang.Class that invoke checkMember
+             * access. The stack should look like:
+             *
+             * someCaller                        [3]
+             * java.lang.Class.someReflectionAPI [2]
+             * java.lang.Class.checkMemberAccess [1]
+             * SecurityManager.checkMemberAccess [0]
+             *
+             */
+            // For us it is this stack:
+            // someCaller                        [3]
+            // Lookup.findSomeMember             [2]
+            // Lookup.checkSecurityManager       [1]
+            // SecurityManager.checkMemberAccess [0]
+        }
+
         void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws IllegalAccessException {
             String message;
             if (m.isConstructor())
@@ -1085,19 +1165,14 @@
             return fixVarargs(narrowMH, mh);
         }
 
-        MethodHandle makeAccessor(Class<?> refc, String name, Class<?> type,
-                                  boolean isStatic, boolean isSetter) throws NoSuchFieldException, IllegalAccessException {
-            MemberName field = resolveOrFail(refc, name, type, isStatic);
-            if (isStatic != field.isStatic())
-                throw field.makeAccessException(isStatic
+        MethodHandle makeAccessor(Class<?> refc, MemberName field,
+                                  boolean trusted, boolean isSetter,
+                                  int checkStatic) throws IllegalAccessException {
+            assert(field.isField());
+            if (checkStatic >= 0 && (checkStatic != 0) != field.isStatic())
+                throw field.makeAccessException((checkStatic != 0)
                                                 ? "expected a static field"
                                                 : "expected a non-static field", this);
-            return makeAccessor(refc, field, false, isSetter);
-        }
-
-        MethodHandle makeAccessor(Class<?> refc, MemberName field,
-                                  boolean trusted, boolean isSetter) throws IllegalAccessException {
-            assert(field.isField());
             if (trusted)
                 return MethodHandleImpl.accessField(field, isSetter, lookupClassOrNull());
             checkAccess(refc, field);
@@ -1139,50 +1214,51 @@
 
     /**
      * Produces a method handle which will invoke any method handle of the
-     * given {@code type} on a standard set of {@code Object} type arguments
-     * and a single trailing {@code Object[]} array.
+     * given {@code type}, with a given number of trailing arguments replaced by
+     * a single trailing {@code Object[]} array.
      * The resulting invoker will be a method handle with the following
      * arguments:
      * <ul>
      * <li>a single {@code MethodHandle} target
-     * <li>zero or more {@code Object} values (counted by {@code objectArgCount})
-     * <li>an {@code Object[]} array containing more arguments
+     * <li>zero or more leading values (counted by {@code leadingArgCount})
+     * <li>an {@code Object[]} array containing trailing arguments
      * </ul>
      * <p>
-     * The invoker will behave like a call to {@link MethodHandle#invoke invoke} with
+     * The invoker will invoke its target like a call to {@link MethodHandle#invoke invoke} with
      * the indicated {@code type}.
      * That is, if the target is exactly of the given {@code type}, it will behave
      * like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType}
      * is used to convert the target to the required {@code type}.
      * <p>
      * The type of the returned invoker will not be the given {@code type}, but rather
-     * will have all parameter and return types replaced by {@code Object}, except for
-     * the last parameter type, which will be the array type {@code Object[]}.
+     * will have all parameters except the first {@code leadingArgCount}
+     * replaced by a single array of type {@code Object[]}, which will be
+     * the final parameter.
      * <p>
-     * Before invoking its target, the invoker will spread the varargs array, apply
+     * Before invoking its target, the invoker will spread the final array, apply
      * reference casts as necessary, and unbox and widen primitive arguments.
-     * The return value of the invoker will be an {@code Object} reference,
-     * boxing a primitive value if the original type returns a primitive,
-     * and always null if the original type returns void.
      * <p>
      * This method is equivalent to the following code (though it may be more efficient):
      * <p><blockquote><pre>
 MethodHandle invoker = MethodHandles.invoker(type);
-int spreadArgCount = type.parameterCount - objectArgCount;
+int spreadArgCount = type.parameterCount() - leadingArgCount;
 invoker = invoker.asSpreader(Object[].class, spreadArgCount);
 return invoker;
      * </pre></blockquote>
      * <p>
      * This method throws no reflective or security exceptions.
      * @param type the desired target type
-     * @param objectArgCount number of fixed (non-varargs) {@code Object} arguments
+     * @param leadingArgCount number of fixed arguments, to be passed unchanged to the target
      * @return a method handle suitable for invoking any method handle of the given type
+     * @throws NullPointerException if {@code type} is null
+     * @throws IllegalArgumentException if {@code leadingArgCount} is not in
+     *                  the range from 0 to {@code type.parameterCount()} inclusive
      */
     static public
-    MethodHandle spreadInvoker(MethodType type, int objectArgCount) {
-        if (objectArgCount < 0 || objectArgCount > type.parameterCount())
-            throw new IllegalArgumentException("bad argument count "+objectArgCount);
-        return type.invokers().spreadInvoker(objectArgCount);
+    MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
+        if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
+            throw new IllegalArgumentException("bad argument count "+leadingArgCount);
+        return type.invokers().spreadInvoker(leadingArgCount);
     }
 
     /**
@@ -1212,7 +1288,7 @@
      * method handle values, as long as they are compatible with the type of {@code X}.
      * <p>
      * <em>(Note:  The invoker method is not available via the Core Reflection API.
-     * An attempt to call {@linkplain java.lang.reflect.Method#invoke Method.invoke}
+     * An attempt to call {@linkplain java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}
      * on the declared {@code invokeExact} or {@code invoke} method will raise an
      * {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em>
      * <p>
@@ -1232,12 +1308,18 @@
      * exactly equal to the desired type, except that it will accept
      * an additional leading argument of type {@code MethodHandle}.
      * <p>
-     * Before invoking its target, the invoker will apply reference casts as
+     * Before invoking its target, if the target differs from the expected type,
+     * the invoker will apply reference casts as
      * necessary and box, unbox, or widen primitive values, as if by {@link MethodHandle#asType asType}.
      * Similarly, the return value will be converted as necessary.
      * If the target is a {@linkplain MethodHandle#asVarargsCollector variable arity method handle},
      * the required arity conversion will be made, again as if by {@link MethodHandle#asType asType}.
      * <p>
+     * A {@linkplain MethodType#genericMethodType general method type},
+     * mentions only {@code Object} arguments and return values.
+     * An invoker for such a type is capable of calling any method handle
+     * of the same arity as the general type.
+     * <p>
      * This method is equivalent to the following code (though it may be more efficient):
      * <p><blockquote><pre>
 publicLookup().findVirtual(MethodHandle.class, "invoke", type)
@@ -1253,18 +1335,9 @@
     }
 
     /**
-     * <em>Temporary alias</em> for {@link #invoker}, for backward compatibility with some versions of JSR 292.
-     * @deprecated Will be removed for JSR 292 Proposed Final Draft.
-     */
-    public static
-    MethodHandle genericInvoker(MethodType type) {
-        return invoker(type);
-    }
-
-    /**
      * Perform value checking, exactly as if for an adapted method handle.
      * It is assumed that the given value is either null, of type T0,
-     * or (if T0 is primitive) of the wrapper type corresponding to T0.
+     * or (if T0 is primitive) of the wrapper class corresponding to T0.
      * The following checks and conversions are made:
      * <ul>
      * <li>If T0 and T1 are references, then a cast to T1 is applied.
@@ -1272,11 +1345,11 @@
      * <li>If T0 and T1 are primitives, then a widening or narrowing
      *     conversion is applied, if one exists.
      * <li>If T0 is a primitive and T1 a reference, and
-     *     T0 has a wrapper type TW, a boxing conversion to TW is applied,
+     *     T0 has a wrapper class TW, a boxing conversion to TW is applied,
      *     possibly followed by a reference conversion.
      *     T1 must be TW or a supertype.
      * <li>If T0 is a reference and T1 a primitive, and
-     *     T1 has a wrapper type TW, an unboxing conversion is applied,
+     *     T1 has a wrapper class TW, an unboxing conversion is applied,
      *     possibly preceded by a reference conversion.
      *     T0 must be TW or a supertype.
      * <li>If T1 is void, the return value is discarded
@@ -1289,6 +1362,7 @@
      * @return the value, converted if necessary
      * @throws java.lang.ClassCastException if a cast fails
      */
+    // FIXME: This is used in just one place.  Refactor away.
     static
     <T0, T1> T1 checkValue(Class<T0> t0, Class<T1> t1, Object value)
        throws ClassCastException
@@ -1317,6 +1391,8 @@
         return w1.convert(value, t1);
     }
 
+    // FIXME: Delete this.  It is used only for insertArguments & bindTo.
+    // Replace by a more standard check.
     static
     Object checkValue(Class<?> T1, Object value)
        throws ClassCastException
@@ -1333,137 +1409,53 @@
 
     /**
      * Produces a method handle which adapts the type of the
-     * given method handle to a new type by pairwise argument conversion.
-     * The original type and new type must have the same number of arguments.
-     * The resulting method handle is guaranteed to report a type
-     * which is equal to the desired new type.
-     * <p>
-     * If the original type and new type are equal, returns target.
-     * <p>
-     * The following conversions are applied as needed both to
-     * arguments and return types.  Let T0 and T1 be the differing
-     * new and old parameter types (or old and new return types)
-     * for corresponding values passed by the new and old method types.
-     * Given those types T0, T1, one of the following conversions is applied
-     * if possible:
-     * <ul>
-     * <li>If T0 and T1 are references, then a cast to T1 is applied.
-     *     (The types do not need to be related in any particular way.)
-     * <li>If T0 and T1 are primitives, then a Java method invocation
-     *     conversion (JLS 5.3) is applied, if one exists.
-     * <li>If T0 is a primitive and T1 a reference, a boxing
-     *     conversion is applied if one exists, possibly followed by
-     *     a reference conversion to a superclass.
-     *     T1 must be a wrapper class or a supertype of one.
-     * <li>If T0 is a reference and T1 a primitive, an unboxing
-     *     conversion will be applied at runtime, possibly followed
-     *     by a Java method invocation conversion (JLS 5.3)
-     *     on the primitive value.  (These are the widening conversions.)
-     *     T0 must be a wrapper class or a supertype of one.
-     *     (In the case where T0 is Object, these are the conversions
-     *     allowed by java.lang.reflect.Method.invoke.)
-     * <li>If the return type T1 is void, any returned value is discarded
-     * <li>If the return type T0 is void and T1 a reference, a null value is introduced.
-     * <li>If the return type T0 is void and T1 a primitive, a zero value is introduced.
-     * </ul>
-     * @param target the method handle to invoke after arguments are retyped
-     * @param newType the expected type of the new method handle
-     * @return a method handle which delegates to {@code target} after performing
-     *           any necessary argument conversions, and arranges for any
-     *           necessary return value conversions
-     * @throws NullPointerException if either argument is null
-     * @throws WrongMethodTypeException if the conversion cannot be made
-     * @see MethodHandle#asType
-     * @see MethodHandles#explicitCastArguments
-     */
-    public static
-    MethodHandle convertArguments(MethodHandle target, MethodType newType) {
-        if (!target.type().isConvertibleTo(newType)) {
-            throw new WrongMethodTypeException("cannot convert "+target+" to "+newType);
-        }
-        return MethodHandleImpl.convertArguments(target, newType, 1);
-    }
-
-    /**
-     * Produces a method handle which adapts the type of the
-     * given method handle to a new type by pairwise argument conversion.
+     * given method handle to a new type by pairwise argument and return type conversion.
      * The original type and new type must have the same number of arguments.
      * The resulting method handle is guaranteed to report a type
      * which is equal to the desired new type.
      * <p>
      * If the original type and new type are equal, returns target.
      * <p>
-     * The same conversions are allowed as for {@link #convertArguments convertArguments},
+     * The same conversions are allowed as for {@link MethodHandle#asType MethodHandle.asType},
      * and some additional conversions are also applied if those conversions fail.
-     * Given types T0, T1, one of the following conversions is applied
-     * in addition, if the conversions specified for {@code convertArguments}
-     * would be insufficient:
+     * Given types <em>T0</em>, <em>T1</em>, one of the following conversions is applied
+     * if possible, before or instead of any conversions done by {@code asType}:
      * <ul>
-     * <li>If T0 and T1 are references, and T1 is an interface type,
-     *     then the value of type T0 is passed as a T1 without a cast.
+     * <li>If <em>T0</em> and <em>T1</em> are references, and <em>T1</em> is an interface type,
+     *     then the value of type <em>T0</em> is passed as a <em>T1</em> without a cast.
      *     (This treatment of interfaces follows the usage of the bytecode verifier.)
-     * <li>If T0 and T1 are primitives and one is boolean,
-     *     the boolean is treated as a one-bit unsigned integer.
+     * <li>If <em>T0</em> is boolean and <em>T1</em> is another primitive,
+     *     the boolean is converted to a byte value, 1 for true, 0 for false.
      *     (This treatment follows the usage of the bytecode verifier.)
-     *     A conversion from another primitive type behaves as if
-     *     it first converts to byte, and then masks all but the low bit.
-     * <li>If a primitive value would be converted by {@code convertArguments}
-     *     using Java method invocation conversion (JLS 5.3),
-     *     Java casting conversion (JLS 5.5) may be used also.
-     *     This allows primitives to be narrowed as well as widened.
+     * <li>If <em>T1</em> is boolean and <em>T0</em> is another primitive,
+     *     <em>T0</em> is converted to byte via Java casting conversion (JLS 5.5),
+     *     and the low order bit of the result is tested, as if by {@code (x & 1) != 0}.
+     * <li>If <em>T0</em> and <em>T1</em> are primitives other than boolean,
+     *     then a Java casting conversion (JLS 5.5) is applied.
+     *     (Specifically, <em>T0</em> will convert to <em>T1</em> by
+     *     widening and/or narrowing.)
+     * <li>If <em>T0</em> is a reference and <em>T1</em> a primitive, an unboxing
+     *     conversion will be applied at runtime, possibly followed
+     *     by a Java casting conversion (JLS 5.5) on the primitive value,
+     *     possibly followed by a conversion from byte to boolean by testing
+     *     the low-order bit.
+     * <li>If <em>T0</em> is a reference and <em>T1</em> a primitive,
+     *     and if the reference is null at runtime, a zero value is introduced.
      * </ul>
      * @param target the method handle to invoke after arguments are retyped
      * @param newType the expected type of the new method handle
-     * @return a method handle which delegates to {@code target} after performing
+     * @return a method handle which delegates to the target after performing
      *           any necessary argument conversions, and arranges for any
      *           necessary return value conversions
      * @throws NullPointerException if either argument is null
      * @throws WrongMethodTypeException if the conversion cannot be made
      * @see MethodHandle#asType
-     * @see MethodHandles#convertArguments
      */
     public static
     MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
         return MethodHandleImpl.convertArguments(target, newType, 2);
     }
 
-    /*
-      FIXME: Reconcile javadoc with 10/22/2010 EG notes on conversion:
-
-      Both converters arrange for their method handles to convert arguments
-      and return values.  The conversion rules are the same for arguments
-      and return values, and depend only on source and target types, S and
-      T.  The conversions allowed by castConvertArguments are a strict
-      superset of those performed by convertArguments.
-
-      In all cases, if S and T are references, a simple checkcast is done.
-      If neither S nor T is a primitive, no attempt is made to unbox and
-      box.  A failed conversion throws ClassCastException.
-
-      If T is void, the value is dropped.
-
-      For compatibility with reflection, if S is void and T is a reference,
-      a null value is produced.
-
-      For compatibility with reflection, if S is a reference and T is a
-      primitive, S is first unboxed and then undergoes primitive conversion.
-      In the case of 'convertArguments', only assignment conversion is
-      performed (no narrowing primitive conversion).
-
-      If S is a primitive, S is boxed, and then the above rules are applied.
-      If S and T are both primitives, the boxing will be undetectable; only
-      the primitive conversions will be apparent to the user.  The key point
-      is that if S is a primitive type, the implementation may box it and
-      treat is as Object, without loss of information, or it may use a "fast
-      path" which does not use boxing.
-
-      Notwithstanding the rules above, for compatibility with the verifier,
-      if T is an interface, it is treated as if it were Object.  [KEEP THIS?]
-
-      Also, for compatibility with the verifier, a boolean may be undergo
-      widening or narrowing conversion to any other primitive type.  [KEEP THIS?]
-    */
-
     /**
      * Produces a method handle which adapts the calling sequence of the
      * given method handle to a new type, by reordering the arguments.
@@ -1482,8 +1474,8 @@
      * <p>
      * No argument or return value conversions are applied.
      * The type of each incoming argument, as determined by {@code newType},
-     * must be identical to the type of the corresponding outgoing argument
-     * or arguments in the target method handle.
+     * must be identical to the type of the corresponding outgoing parameter
+     * or parameters in the target method handle.
      * The return type of {@code newType} must be identical to the return
      * type of the original target.
      * <p>
@@ -1495,25 +1487,33 @@
      * incoming arguments which are not mentioned in the reordering array
      * are may be any type, as determined only by {@code newType}.
      * <blockquote><pre>
-MethodType intfn1 = MethodType.methodType(int.class, int.class);
-MethodType intfn2 = MethodType.methodType(int.class, int.class, int.class);
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodType intfn1 = methodType(int.class, int.class);
+MethodType intfn2 = methodType(int.class, int.class, int.class);
 MethodHandle sub = ... {int x, int y => x-y} ...;
 assert(sub.type().equals(intfn2));
-MethodHandle sub1 = MethodHandles.permuteArguments(sub, intfn2, 0, 1);
-MethodHandle rsub = MethodHandles.permuteArguments(sub, intfn2, 1, 0);
+MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
+MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
 assert((int)rsub.invokeExact(1, 100) == 99);
 MethodHandle add = ... {int x, int y => x+y} ...;
 assert(add.type().equals(intfn2));
-MethodHandle twice = MethodHandles.permuteArguments(add, intfn1, 0, 0);
+MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
 assert(twice.type().equals(intfn1));
 assert((int)twice.invokeExact(21) == 42);
      * </pre></blockquote>
      * @param target the method handle to invoke after arguments are reordered
      * @param newType the expected type of the new method handle
-     * @param reorder a string which controls the reordering
-     * @return a method handle which delegates to {@code target} after it
+     * @param reorder an index array which controls the reordering
+     * @return a method handle which delegates to the target after it
      *           drops unused arguments and moves and/or duplicates the other arguments
      * @throws NullPointerException if any argument is null
+     * @throws IllegalArgumentException if the index array length is not equal to
+     *                  the arity of the target, or if any index array element
+     *                  not a valid index for a parameter of {@code newType},
+     *                  or if two corresponding parameter types in
+     *                  {@code target.type()} and {@code newType} are not identical,
      */
     public static
     MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
@@ -1548,78 +1548,13 @@
     }
 
     /**
-     * Equivalent to the following code:
-     * <p><blockquote><pre>
-     * int spreadPos = newType.parameterCount() - 1;
-     * Class&lt;?&gt; spreadType = newType.parameterType(spreadPos);
-     * int spreadCount = target.type().parameterCount() - spreadPos;
-     * MethodHandle adapter = target.asSpreader(spreadType, spreadCount);
-     * adapter = adapter.asType(newType);
-     * return adapter;
-     * </pre></blockquote>
-     * @param target the method handle to invoke after argument spreading
-     * @param newType the expected type of the new method handle
-     * @return a method handle which spreads its final argument,
-     *         before calling the original method handle
-     */
-    /*non-public*/ static
-    MethodHandle spreadArguments(MethodHandle target, MethodType newType) {
-        MethodType oldType = target.type();
-        int inargs  = newType.parameterCount();
-        int outargs = oldType.parameterCount();
-        int spreadPos = inargs - 1;
-        int numSpread = (outargs - spreadPos);
-        MethodHandle res = null;
-        if (spreadPos >= 0 && numSpread >= 0) {
-            res = MethodHandleImpl.spreadArgumentsFromPos(target, newType, spreadPos);
-        }
-        if (res == null) {
-            throw newIllegalArgumentException("cannot spread "+newType+" to " +oldType);
-        }
-        return res;
-    }
-
-    /**
-     * Equivalent to the following code:
-     * <p><blockquote><pre>
-     * int collectPos = target.type().parameterCount() - 1;
-     * Class&lt;?&gt; collectType = target.type().parameterType(collectPos);
-     * if (!collectType.isArray())  collectType = Object[].class;
-     * int collectCount = newType.parameterCount() - collectPos;
-     * MethodHandle adapter = target.asCollector(collectType, collectCount);
-     * adapter = adapter.asType(newType);
-     * return adapter;
-     * </pre></blockquote>
-     * @param target the method handle to invoke after argument collection
-     * @param newType the expected type of the new method handle
-     * @return a method handle which collects some trailing argument
-     *         into an array, before calling the original method handle
-     */
-    /*non-public*/ static
-    MethodHandle collectArguments(MethodHandle target, MethodType newType) {
-        MethodType oldType = target.type();
-        int inargs  = newType.parameterCount();
-        int outargs = oldType.parameterCount();
-        int collectPos = outargs - 1;
-        int numCollect = (inargs - collectPos);
-        if (collectPos < 0 || numCollect < 0)
-            throw newIllegalArgumentException("wrong number of arguments");
-        MethodHandle res = MethodHandleImpl.collectArguments(target, newType, collectPos, null);
-        if (res == null) {
-            throw newIllegalArgumentException("cannot collect from "+newType+" to " +oldType);
-        }
-        return res;
-    }
-
-    /**
      * Produces a method handle of the requested return type which returns the given
      * constant value every time it is invoked.
      * <p>
      * Before the method handle is returned, the passed-in value is converted to the requested type.
      * If the requested type is primitive, widening primitive conversions are attempted,
      * else reference conversions are attempted.
-     * <p>The returned method handle is equivalent to {@code identity(type).bindTo(value)},
-     * unless the type is {@code void}, in which case it is {@code identity(type)}.
+     * <p>The returned method handle is equivalent to {@code identity(type).bindTo(value)}.
      * @param type the return type of the desired method handle
      * @param value the value to return
      * @return a method handle of the given return type and no arguments, which always returns the given value
@@ -1641,7 +1576,6 @@
 
     /**
      * Produces a method handle which returns its sole argument when invoked.
-     * <p>The identity function for {@code void} takes no arguments and returns no values.
      * @param type the type of the sole parameter and return value of the desired method handle
      * @return a unary method handle which accepts and returns the given type
      * @throws NullPointerException if the argument is null
@@ -1661,11 +1595,15 @@
     }
 
     /**
-     * Produces a method handle which calls the original method handle {@code target},
-     * after inserting the given argument(s) at the given position.
-     * The formal parameters to {@code target} which will be supplied by those
-     * arguments are called <em>bound parameters</em>, because the new method
-     * will contain bindings for those parameters take from {@code values}.
+     * Provides a target method handle with one or more <em>bound arguments</em>
+     * in advance of the method handle's invocation.
+     * The formal parameters to the target corresponding to the bound
+     * arguments are called <em>bound parameters</em>.
+     * Returns a new method handle which saves away the bound arguments.
+     * When it is invoked, it receives arguments for any non-bound parameters,
+     * binds the saved arguments to their corresponding parameters,
+     * and calls the original target.
+     * <p>
      * The type of the new method handle will drop the types for the bound
      * parameters from the original target type, since the new method handle
      * will no longer require those arguments to be supplied by its callers.
@@ -1674,15 +1612,16 @@
      * If a bound parameter type is a primitive, the argument object
      * must be a wrapper, and will be unboxed to produce the primitive value.
      * <p>
-     * The  <i>pos</i> may range between zero and <i>N</i> (inclusively),
-     * where <i>N</i> is the number of argument types in resulting method handle
-     * (after bound parameter types are dropped).
+     * The {@code pos} argument selects which parameters are to be bound.
+     * It may range between zero and <i>N-L</i> (inclusively),
+     * where <i>N</i> is the arity of the target method handle
+     * and <i>L</i> is the length of the values array.
      * @param target the method handle to invoke after the argument is inserted
      * @param pos where to insert the argument (zero for the first)
      * @param values the series of arguments to insert
      * @return a method handle which inserts an additional argument,
      *         before calling the original method handle
-     * @throws NullPointerException if the {@code target} argument or the {@code values} array is null
+     * @throws NullPointerException if the target or the {@code values} array is null
      * @see MethodHandle#bindTo
      */
     public static
@@ -1715,15 +1654,17 @@
     }
 
     /**
-     * Produces a method handle which calls the original method handle,
-     * after dropping the given argument(s) at the given position.
-     * The type of the new method handle will insert the given argument
-     * type(s), at that position, into the original handle's type.
+     * Produces a method handle which will discard some dummy arguments
+     * before calling some other specified <i>target</i> method handle.
+     * The type of the new method handle will be the same as the target's type,
+     * except it will also include the dummy argument types,
+     * at some given position.
      * <p>
-     * The <i>pos</i> may range between zero and <i>N</i>,
-     * where <i>N</i> is the number of argument types in <i>target</i>,
-     * meaning to drop the first or last argument (respectively),
-     * or an argument somewhere in between.
+     * The {@code pos} argument may range between zero and <i>N</i>,
+     * where <i>N</i> is the arity of the target.
+     * If {@code pos} is zero, the dummy arguments will precede
+     * the target's real arguments; if {@code pos} is <i>N</i>
+     * they will come after.
      * <p>
      * <b>Example:</b>
      * <p><blockquote><pre>
@@ -1748,14 +1689,16 @@
      * @param pos position of first argument to drop (zero for the leftmost)
      * @return a method handle which drops arguments of the given types,
      *         before calling the original method handle
-     * @throws NullPointerException if the {@code target} argument is null,
+     * @throws NullPointerException if the target is null,
      *                              or if the {@code valueTypes} list or any of its elements is null
-     * @throws IllegalArgumentException if any of the {@code valueTypes} is {@code void.class}
+     * @throws IllegalArgumentException if any element of {@code valueTypes} is {@code void.class},
+     *                  or if {@code pos} is negative or greater than the arity of the target,
+     *                  or if the new method handle's type would have too many parameters
      */
     public static
     MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
+        MethodType oldType = target.type();  // get NPE
         if (valueTypes.size() == 0)  return target;
-        MethodType oldType = target.type();
         int outargs = oldType.parameterCount();
         int inargs  = outargs + valueTypes.size();
         if (pos < 0 || pos >= inargs)
@@ -1768,15 +1711,17 @@
     }
 
     /**
-     * Produces a method handle which calls the original method handle,
-     * after dropping the given argument(s) at the given position.
-     * The type of the new method handle will insert the given argument
-     * type(s), at that position, into the original handle's type.
+     * Produces a method handle which will discard some dummy arguments
+     * before calling some other specified <i>target</i> method handle.
+     * The type of the new method handle will be the same as the target's type,
+     * except it will also include the dummy argument types,
+     * at some given position.
      * <p>
-     * The <i>pos</i> may range between zero and <i>N</i>,
-     * where <i>N</i> is the number of argument types in <i>target</i>,
-     * meaning to drop the first or last argument (respectively),
-     * or an argument somewhere in between.
+     * The {@code pos} argument may range between zero and <i>N</i>,
+     * where <i>N</i> is the arity of the target.
+     * If {@code pos} is zero, the dummy arguments will precede
+     * the target's real arguments; if {@code pos} is <i>N</i>
+     * they will come after.
      * <p>
      * <b>Example:</b>
      * <p><blockquote><pre>
@@ -1805,9 +1750,11 @@
      * @param pos position of first argument to drop (zero for the leftmost)
      * @return a method handle which drops arguments of the given types,
      *         before calling the original method handle
-     * @throws NullPointerException if the {@code target} argument is null,
+     * @throws NullPointerException if the target is null,
      *                              or if the {@code valueTypes} array or any of its elements is null
-     * @throws IllegalArgumentException if any of the {@code valueTypes} is {@code void.class}
+     * @throws IllegalArgumentException if any element of {@code valueTypes} is {@code void.class},
+     *                  or if {@code pos} is negative or greater than the arity of the target,
+     *                  or if the new method handle's type would have too many parameters
      */
     public static
     MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
@@ -1815,19 +1762,23 @@
     }
 
     /**
-     * Adapts a target method handle {@code target} by pre-processing
+     * Adapts a target method handle by pre-processing
      * one or more of its arguments, each with its own unary filter function,
      * and then calling the target with each pre-processed argument
      * replaced by the result of its corresponding filter function.
      * <p>
      * The pre-processing is performed by one or more method handles,
      * specified in the elements of the {@code filters} array.
-     * Null arguments in the array are ignored, and the corresponding arguments left unchanged.
+     * The first element of the filter array corresponds to the {@code pos}
+     * argument of the target, and so on in sequence.
+     * <p>
+     * Null arguments in the array are treated as identity functions,
+     * and the corresponding arguments left unchanged.
      * (If there are no non-null elements in the array, the original target is returned.)
      * Each filter is applied to the corresponding argument of the adapter.
      * <p>
      * If a filter {@code F} applies to the {@code N}th argument of
-     * the method handle, then {@code F} must be a method handle which
+     * the target, then {@code F} must be a method handle which
      * takes exactly one argument.  The type of {@code F}'s sole argument
      * replaces the corresponding argument type of the target
      * in the resulting adapted method handle.
@@ -1835,6 +1786,7 @@
      * parameter type of the target.
      * <p>
      * It is an error if there are elements of {@code filters}
+     * (null or not)
      * which do not correspond to argument positions in the target.
      * <b>Example:</b>
      * <p><blockquote><pre>
@@ -1853,15 +1805,23 @@
 MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
 assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
      * </pre></blockquote>
+     * <p> Here is pseudocode for the resulting adapter:
+     * <blockquote><pre>
+     * V target(P... p, A[i]... a[i], B... b);
+     * A[i] filter[i](V[i]);
+     * T adapter(P... p, V[i]... v[i], B... b) {
+     *   return target(p..., f[i](v[i])..., b...);
+     * }
+     * </pre></blockquote>
      *
      * @param target the method handle to invoke after arguments are filtered
      * @param pos the position of the first argument to filter
      * @param filters method handles to call initially on filtered arguments
      * @return method handle which incorporates the specified argument filtering logic
-     * @throws NullPointerException if the {@code target} argument is null
+     * @throws NullPointerException if the target is null
      *                              or if the {@code filters} array is null
      * @throws IllegalArgumentException if a non-null element of {@code filters}
-     *          does not match a corresponding argument type of {@code target} as described above,
+     *          does not match a corresponding argument type of target as described above,
      *          or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()}
      */
     public static
@@ -1895,15 +1855,18 @@
     }
 
     /**
-     * Adapts a target method handle {@code target} by post-processing
-     * its return value with a unary filter function.
+     * Adapts a target method handle by post-processing
+     * its return value (if any) with a filter (another method handle).
+     * The result of the filter is returned from the adapter.
      * <p>
-     * If a filter {@code F} applies to the return value of
-     * the target method handle, then {@code F} must be a method handle which
-     * takes exactly one argument.  The return type of {@code F}
+     * If the target returns a value, the filter must accept that value as
+     * its only argument.
+     * If the target returns void, the filter must accept no arguments.
+     * <p>
+     * The return type of the filter
      * replaces the return type of the target
      * in the resulting adapted method handle.
-     * The argument type of {@code F} must be identical to the
+     * The argument type of the filter (if any) must be identical to the
      * return type of the target.
      * <b>Example:</b>
      * <p><blockquote><pre>
@@ -1918,12 +1881,35 @@
 MethodHandle f0 = filterReturnValue(cat, length);
 System.out.println((int) f0.invokeExact("x", "y")); // 2
      * </pre></blockquote>
+     * <p> Here is pseudocode for the resulting adapter:
+     * <blockquote><pre>
+     * V target(A...);
+     * T filter(V);
+     * T adapter(A... a) {
+     *   V v = target(a...);
+     *   return filter(v);
+     * }
+     * // and if the target has a void return:
+     * void target2(A...);
+     * T filter2();
+     * T adapter2(A... a) {
+     *   target2(a...);
+     *   return filter2();
+     * }
+     * // and if the filter has a void return:
+     * V target3(A...);
+     * void filter3(V);
+     * void adapter3(A... a) {
+     *   V v = target3(a...);
+     *   filter3(v);
+     * }
+     * </pre></blockquote>
      * @param target the method handle to invoke before filtering the return value
      * @param filter method handle to call on the return value
      * @return method handle which incorporates the specified return value filtering logic
      * @throws NullPointerException if either argument is null
-     * @throws IllegalArgumentException if {@code filter}
-     *          does not match the return type of {@code target} as described above
+     * @throws IllegalArgumentException if the argument list of {@code filter}
+     *          does not match the return type of target as described above
      */
     public static
     MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
@@ -1952,55 +1938,87 @@
     }
 
     /**
-     * Adapts a target method handle {@code target} by pre-processing
+     * Adapts a target method handle by pre-processing
      * some of its arguments, and then calling the target with
-     * the result of the pre-processing, plus all original arguments.
+     * the result of the pre-processing, inserted into the original
+     * sequence of arguments.
+     * <p>
+     * The pre-processing is performed by {@code combiner}, a second method handle.
+     * Of the arguments passed to the adapter, the first {@code N} arguments
+     * are copied to the combiner, which is then called.
+     * (Here, {@code N} is defined as the parameter count of the combiner.)
+     * After this, control passes to the target, with any result
+     * from the combiner inserted before the original {@code N} incoming
+     * arguments.
      * <p>
-     * The pre-processing is performed by a second method handle, the {@code combiner}.
-     * The first {@code N} arguments passed to the adapter,
-     * are copied to the combiner, which then produces a result.
-     * (Here, {@code N} is defined as the parameter count of the adapter.)
-     * After this, control passes to the {@code target}, with both the result
-     * of the combiner, and all the original incoming arguments.
+     * If the combiner returns a value, the first parameter type of the target
+     * must be identical with the return type of the combiner, and the next
+     * {@code N} parameter types of the target must exactly match the parameters
+     * of the combiner.
      * <p>
-     * The first argument type of the target must be identical with the
-     * return type of the combiner.
+     * If the combiner has a void return, no result will be inserted,
+     * and the first {@code N} parameter types of the target
+     * must exactly match the parameters of the combiner.
+     * <p>
      * The resulting adapter is the same type as the target, except that the
-     * initial argument type of the target is dropped.
+     * first parameter type is dropped,
+     * if it corresponds to the result of the combiner.
      * <p>
      * (Note that {@link #dropArguments(MethodHandle,int,List) dropArguments} can be used to remove any arguments
-     * that either the {@code combiner} or {@code target} does not wish to receive.
+     * that either the combiner or the target does not wish to receive.
      * If some of the incoming arguments are destined only for the combiner,
      * consider using {@link MethodHandle#asCollector asCollector} instead, since those
      * arguments will not need to be live on the stack on entry to the
      * target.)
-     * <p>
-     * The first argument of the target must be identical with the
-     * return value of the combiner.
+     * <b>Example:</b>
+     * <p><blockquote><pre>
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
+  "println", methodType(void.class, String.class))
+    .bindTo(System.out);
+MethodHandle cat = lookup().findVirtual(String.class,
+  "concat", methodType(String.class, String.class));
+assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
+MethodHandle catTrace = foldArguments(cat, trace);
+// also prints "boo":
+assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+     * </pre></blockquote>
      * <p> Here is pseudocode for the resulting adapter:
      * <blockquote><pre>
-     * // there are N arguments in the A sequence
+     * // there are N arguments in A...
      * T target(V, A[N]..., B...);
      * V combiner(A...);
      * T adapter(A... a, B... b) {
      *   V v = combiner(a...);
      *   return target(v, a..., b...);
      * }
+     * // and if the combiner has a void return:
+     * T target2(A[N]..., B...);
+     * void combiner2(A...);
+     * T adapter2(A... a, B... b) {
+     *   combiner2(a...);
+     *   return target2(a..., b...);
+     * }
      * </pre></blockquote>
      * @param target the method handle to invoke after arguments are combined
      * @param combiner method handle to call initially on the incoming arguments
      * @return method handle which incorporates the specified argument folding logic
      * @throws NullPointerException if either argument is null
-     * @throws IllegalArgumentException if the first argument type of
-     *          {@code target} is not the same as {@code combiner}'s return type,
-     *          or if the following argument types of {@code target}
+     * @throws IllegalArgumentException if {@code combiner}'s return type
+     *          is non-void and not the same as the first argument type of
+     *          the target, or if the initial {@code N} argument types
+     *          of the target
+     *          (skipping one matching the {@code combiner}'s return type)
      *          are not identical with the argument types of {@code combiner}
      */
     public static
     MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
+        int pos = 0;
         MethodType targetType = target.type();
         MethodType combinerType = combiner.type();
-        int foldPos = 0;  // always at the head, at present
+        int foldPos = pos;
         int foldArgs = combinerType.parameterCount();
         int foldVals = combinerType.returnType() == void.class ? 0 : 1;
         int afterInsertPos = foldPos + foldVals;
@@ -2049,7 +2067,7 @@
      * @throws NullPointerException if any argument is null
      * @throws IllegalArgumentException if {@code test} does not return boolean,
      *          or if all three method types do not match (with the return
-     *          type of {@code test} changed to match that of {@code target}).
+     *          type of {@code test} changed to match that of the target).
      */
     public static
     MethodHandle guardWithTest(MethodHandle test,
@@ -2159,229 +2177,4 @@
     MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
         return MethodHandleImpl.throwException(MethodType.methodType(returnType, exType));
     }
-
-    /**
-     * Produces an instance of the given single-method interface which redirects
-     * its calls to the given method handle.
-     * <p>
-     * A single-method interface is an interface which declares a unique method.
-     * When determining the unique method of a single-method interface,
-     * the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode})
-     * are disregarded.  For example, {@link java.util.Comparator} is a single-method interface,
-     * even though it re-declares the {@code Object.equals} method.
-     * <p>
-     * The type must be public.  No additional access checks are performed.
-     * <p>
-     * The resulting instance of the required type will respond to
-     * invocation of the type's single abstract method by calling
-     * the given {@code target} on the incoming arguments,
-     * and returning or throwing whatever the {@code target}
-     * returns or throws.  The invocation will be as if by
-     * {@code target.invoke}.
-     * The target's type will be checked before the
-     * instance is created, as if by a call to {@code asType},
-     * which may result in a {@code WrongMethodTypeException}.
-     * <p>
-     * The wrapper instance will implement the requested interface
-     * and its super-types, but no other single-method interfaces.
-     * This means that the instance will not unexpectedly
-     * pass an {@code instanceof} test for any unrequested type.
-     * <p style="font-size:smaller;">
-     * <em>Implementation Note:</em>
-     * Therefore, each instance must implement a unique single-method interface.
-     * Implementations may not bundle together
-     * multiple single-method interfaces onto single implementation classes
-     * in the style of {@link java.awt.AWTEventMulticaster}.
-     * <p>
-     * The method handle may throw an <em>undeclared exception</em>,
-     * which means any checked exception (or other checked throwable)
-     * not declared by the requested type's single abstract method.
-     * If this happens, the throwable will be wrapped in an instance of
-     * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException}
-     * and thrown in that wrapped form.
-     * <p>
-     * Like {@link java.lang.Integer#valueOf Integer.valueOf},
-     * {@code asInstance} is a factory method whose results are defined
-     * by their behavior.
-     * It is not guaranteed to return a new instance for every call.
-     * <p>
-     * Because of the possibility of {@linkplain java.lang.reflect.Method#isBridge bridge methods}
-     * and other corner cases, the interface may also have several abstract methods
-     * with the same name but having distinct descriptors (types of returns and parameters).
-     * In this case, all the methods are bound in common to the one given {@code target}.
-     * The type check and effective {@code asType} conversion is applied to each
-     * method type descriptor, and all abstract methods are bound to the {@code target} in common.
-     * Beyond this type check, no further checks are made to determine that the
-     * abstract methods are related in any way.
-     * <p>
-     * Future versions of this API may accept additional types,
-     * such as abstract classes with single abstract methods.
-     * Future versions of this API may also equip wrapper instances
-     * with one or more additional public "marker" interfaces.
-     *
-     * @param target the method handle to invoke from the wrapper
-     * @param smType the desired type of the wrapper, a single-method interface
-     * @return a correctly-typed wrapper for the given {@code target}
-     * @throws NullPointerException if either argument is null
-     * @throws IllegalArgumentException if the {@code smType} is not a
-     *         valid argument to this method
-     * @throws WrongMethodTypeException if the {@code target} cannot
-     *         be converted to the type required by the requested interface
-     */
-    // Other notes to implementors:
-    // <p>
-    // No stable mapping is promised between the single-method interface and
-    // the implementation class C.  Over time, several implementation
-    // classes might be used for the same type.
-    // <p>
-    // If the implementation is able
-    // to prove that a wrapper of the required type
-    // has already been created for a given
-    // method handle, or for another method handle with the
-    // same behavior, the implementation may return that wrapper in place of
-    // a new wrapper.
-    // <p>
-    // This method is designed to apply to common use cases
-    // where a single method handle must interoperate with
-    // an interface that implements a function-like
-    // API.  Additional variations, such as single-abstract-method classes with
-    // private constructors, or interfaces with multiple but related
-    // entry points, must be covered by hand-written or automatically
-    // generated adapter classes.
-    //
-    public static
-    <T> T asInstance(final MethodHandle target, final Class<T> smType) {
-        // POC implementation only; violates the above contract several ways
-        final Method sm = getSingleMethod(smType);
-        if (sm == null)
-            throw new IllegalArgumentException("not a single-method interface: "+smType.getName());
-        MethodType smMT = MethodType.methodType(sm.getReturnType(), sm.getParameterTypes());
-        MethodHandle checkTarget = target.asType(smMT);  // make throw WMT
-        checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class));
-        final MethodHandle vaTarget = checkTarget.asSpreader(Object[].class, smMT.parameterCount());
-        return smType.cast(Proxy.newProxyInstance(
-                smType.getClassLoader(),
-                new Class[]{ smType, WrapperInstance.class },
-                new InvocationHandler() {
-                    private Object getArg(String name) {
-                        if ((Object)name == "getWrapperInstanceTarget")  return target;
-                        if ((Object)name == "getWrapperInstanceType")    return smType;
-                        throw new AssertionError();
-                    }
-                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-                        if (method.getDeclaringClass() == WrapperInstance.class)
-                            return getArg(method.getName());
-                        if (method.equals(sm))
-                            return vaTarget.invokeExact(args);
-                        if (isObjectMethod(method))
-                            return callObjectMethod(this, method, args);
-                        throw new InternalError();
-                    }
-                }));
-    }
-
-    /**
-     * Determines if the given object was produced by a call to {@link #asInstance asInstance}.
-     * @param x any reference
-     * @return true if the reference is not null and points to an object produced by {@code asInstance}
-     */
-    public static
-    boolean isWrapperInstance(Object x) {
-        return x instanceof WrapperInstance;
-    }
-
-    private static WrapperInstance asWrapperInstance(Object x) {
-        try {
-            if (x != null)
-                return (WrapperInstance) x;
-        } catch (ClassCastException ex) {
-        }
-        throw new IllegalArgumentException("not a wrapper instance");
-    }
-
-    /**
-     * Produces or recovers a target method handle which is behaviorally
-     * equivalent to the unique method of this wrapper instance.
-     * The object {@code x} must have been produced by a call to {@link #asInstance asInstance}.
-     * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}.
-     * @param x any reference
-     * @return a method handle implementing the unique method
-     * @throws IllegalArgumentException if the reference x is not to a wrapper instance
-     */
-    public static
-    MethodHandle wrapperInstanceTarget(Object x) {
-        return asWrapperInstance(x).getWrapperInstanceTarget();
-    }
-
-    /**
-     * Recovers the unique single-method interface type for which this wrapper instance was created.
-     * The object {@code x} must have been produced by a call to {@link #asInstance asInstance}.
-     * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}.
-     * @param x any reference
-     * @return the single-method interface type for which the wrapper was created
-     * @throws IllegalArgumentException if the reference x is not to a wrapper instance
-     */
-    public static
-    Class<?> wrapperInstanceType(Object x) {
-        return asWrapperInstance(x).getWrapperInstanceType();
-    }
-
-    private static
-    boolean isObjectMethod(Method m) {
-        switch (m.getName()) {
-        case "toString":
-            return (m.getReturnType() == String.class
-                    && m.getParameterTypes().length == 0);
-        case "hashCode":
-            return (m.getReturnType() == int.class
-                    && m.getParameterTypes().length == 0);
-        case "equals":
-            return (m.getReturnType() == boolean.class
-                    && m.getParameterTypes().length == 1
-                    && m.getParameterTypes()[0] == Object.class);
-        }
-        return false;
-    }
-
-    private static
-    Object callObjectMethod(Object self, Method m, Object[] args) {
-        assert(isObjectMethod(m)) : m;
-        switch (m.getName()) {
-        case "toString":
-            return self.getClass().getName() + "@" + Integer.toHexString(self.hashCode());
-        case "hashCode":
-            return System.identityHashCode(self);
-        case "equals":
-            return (self == args[0]);
-        }
-        return null;
-    }
-
-    private static
-    Method getSingleMethod(Class<?> smType) {
-        Method sm = null;
-        for (Method m : smType.getMethods()) {
-            int mod = m.getModifiers();
-            if (Modifier.isAbstract(mod)) {
-                if (sm != null && !isObjectMethod(sm))
-                    return null;  // too many abstract methods
-                sm = m;
-            }
-        }
-        if (!smType.isInterface() && getSingleConstructor(smType) == null)
-            return null;  // wrong kind of constructor
-        return sm;
-    }
-
-    private static
-    Constructor getSingleConstructor(Class<?> smType) {
-        for (Constructor c : smType.getDeclaredConstructors()) {
-            if (c.getParameterTypes().length == 0) {
-                int mod = c.getModifiers();
-                if (Modifier.isPublic(mod) || Modifier.isProtected(mod))
-                    return c;
-            }
-        }
-        return null;
-    }
 }