8150829: Enhanced drop-args, identity and default constant, varargs adjustment
authorsrastogi
Wed, 13 Apr 2016 15:48:14 +0200
changeset 37347 f6abe5d4bb1d
parent 37346 931e55c1287d
child 37348 9ccec3170d5e
8150829: Enhanced drop-args, identity and default constant, varargs adjustment Reviewed-by: psandoz, mhaupt, jrose
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
jdk/test/java/lang/invoke/ConstantIdentityMHTest.java
jdk/test/java/lang/invoke/DropArgumentsTest.java
jdk/test/java/lang/invoke/JavaDocExamplesTest.java
jdk/test/java/lang/invoke/VarArgsTest.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Wed Apr 13 14:29:25 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Wed Apr 13 15:48:14 2016 +0200
@@ -219,7 +219,8 @@
  * Method handles produced by lookups or constant loads from methods or
  * constructors with the variable arity modifier bit ({@code 0x0080})
  * have a corresponding variable arity, as if they were defined with
- * the help of {@link #asVarargsCollector asVarargsCollector}.
+ * the help of {@link #asVarargsCollector asVarargsCollector}
+ * or {@link #withVarargs withVarargs}.
  * <p>
  * A method reference may refer either to a static or non-static method.
  * In the non-static case, the method handle type includes an explicit
@@ -972,6 +973,36 @@
                 throw newIllegalArgumentException("array length is not legal for long[] or double[]", arrayLength);
         }
     }
+    /**
+      * Adapts this method handle to be {@linkplain #asVarargsCollector variable arity}
+      * if the boolean flag is true, else {@linkplain #asFixedArity fixed arity}.
+      * If the method handle is already of the proper arity mode, it is returned
+      * unchanged.
+      * @apiNote
+      * <p>This method is sometimes useful when adapting a method handle that
+      * may be variable arity, to ensure that the resulting adapter is also
+      * variable arity if and only if the original handle was.  For example,
+      * this code changes the first argument of a handle {@code mh} to {@code int} without
+      * disturbing its variable arity property:
+      * {@code mh.asType(mh.type().changeParameterType(0,int.class))
+      *     .withVarargs(mh.isVarargsCollector())}
+      * @param makeVarargs true if the return method handle should have variable arity behavior
+      * @return a method handle of the same type, with possibly adjusted variable arity behavior
+      * @throws IllegalArgumentException if {@code makeVarargs} is true and
+      *         this method handle does not have a trailing array parameter
+      * @since 9
+      * @see #asVarargsCollector
+      * @see #asFixedArity
+     */
+     public MethodHandle withVarargs(boolean makeVarargs) {
+        if (!makeVarargs) {
+            return asFixedArity();
+        } else if (!isVarargsCollector()) {
+            return asVarargsCollector(type().lastParameterType());
+        } else {
+            return this;
+        }
+    }
 
     /**
      * Makes an <em>array-collecting</em> method handle, which accepts a given number of trailing
@@ -1000,7 +1031,8 @@
      * to allow the target to use a simple {@code Object} as its last parameter type.)
      * <p>
      * In order to create a collecting adapter which is not restricted to a particular
-     * number of collected arguments, use {@link #asVarargsCollector asVarargsCollector} instead.
+     * number of collected arguments, use {@link #asVarargsCollector asVarargsCollector}
+     * or {@link #withVarargs withVarargs} instead.
      * <p>
      * Here are some examples of array-collecting method handles:
      * <blockquote><pre>{@code
@@ -1216,7 +1248,7 @@
      * <p>
      * No method handle transformations produce new method handles with
      * variable arity, unless they are documented as doing so.
-     * Therefore, besides {@code asVarargsCollector},
+     * Therefore, besides {@code asVarargsCollector} and {@code withVarargs},
      * all methods in {@code MethodHandle} and {@code MethodHandles}
      * will return a method handle with fixed arity,
      * except in the cases where they are specified to return their original
@@ -1273,6 +1305,7 @@
      *         or {@code arrayType} is not assignable to this method handle's trailing parameter type
      * @see #asCollector
      * @see #isVarargsCollector
+     * @see #withVarargs
      * @see #asFixedArity
      */
     public MethodHandle asVarargsCollector(Class<?> arrayType) {
@@ -1344,6 +1377,7 @@
      * @return a new method handle which accepts only a fixed number of arguments
      * @see #asVarargsCollector
      * @see #isVarargsCollector
+     * @see #withVarargs
      */
     public MethodHandle asFixedArity() {
         assert(!isVarargsCollector());
@@ -1428,11 +1462,11 @@
     /*non-public*/
     MethodHandle setVarargs(MemberName member) throws IllegalAccessException {
         if (!member.isVarargs())  return this;
-        Class<?> arrayType = type().lastParameterType();
-        if (arrayType.isArray()) {
-            return MethodHandleImpl.makeVarargsCollector(this, arrayType);
+        try {
+            return this.withVarargs(true);
+        } catch (IllegalArgumentException ex) {
+            throw member.makeAccessException("cannot make variable arity", null);
         }
-        throw member.makeAccessException("cannot make variable arity", null);
     }
 
     /*non-public*/
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Apr 13 14:29:25 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Apr 13 15:48:14 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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
@@ -1055,7 +1055,7 @@
         if (!method.getInvocationType().equals(mh.type()))
             throw new InternalError(method.toString());
         mh = mh.withInternalMemberName(method, false);
-        mh = mh.asVarargsCollector(Object[].class);
+        mh = mh.withVarargs(true);
         assert(method.isVarargs());
         FAKE_METHOD_HANDLE_INVOKE[idx] = mh;
         return mh;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java	Wed Apr 13 14:29:25 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java	Wed Apr 13 15:48:14 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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
@@ -216,13 +216,7 @@
     }
 
     private static MethodHandle bindCaller(MethodHandle target, Class<?> hostClass) {
-        MethodHandle cbmh = MethodHandleImpl.bindCaller(target, hostClass);
-        if (target.isVarargsCollector()) {
-            MethodType type = cbmh.type();
-            int arity = type.parameterCount();
-            return cbmh.asVarargsCollector(type.parameterType(arity-1));
-        }
-        return cbmh;
+        return MethodHandleImpl.bindCaller(target, hostClass).withVarargs(target.isVarargsCollector());
     }
 
     /**
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Apr 13 14:29:25 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Apr 13 15:48:14 2016 +0200
@@ -1352,9 +1352,7 @@
 ...
 MethodHandle mh0 = lookup().findVirtual(defc, name, type);
 MethodHandle mh1 = mh0.bindTo(receiver);
-MethodType mt1 = mh1.type();
-if (mh0.isVarargsCollector())
-  mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1));
+mh1 = mh1.withVarargs(mh0.isVarargsCollector());
 return mh1;
          * }</pre></blockquote>
          * where {@code defc} is either {@code receiver.getClass()} or a super
@@ -2951,9 +2949,55 @@
         if (ident.type().returnType() == type)
             return ident;
         // something like identity(Foo.class); do not bother to intern these
-        assert(btw == Wrapper.OBJECT);
+        assert (btw == Wrapper.OBJECT);
         return makeIdentity(type);
     }
+
+    /**
+     * Produces a constant method handle of the requested return type which
+     * returns the default value for that type every time it is invoked.
+     * The resulting constant method handle will have no side effects.
+     * <p>The returned method handle is equivalent to {@code empty(methodType(type))}.
+     * It is also equivalent to {@code explicitCastArguments(constant(Object.class, null), methodType(type))},
+     * since {@code explicitCastArguments} converts {@code null} to default values.
+     * @param type the expected return type of the desired method handle
+     * @return a constant method handle that takes no arguments
+     *         and returns the default value of the given type (or void, if the type is void)
+     * @throws NullPointerException if the argument is null
+     * @see MethodHandles#constant
+     * @see MethodHandles#empty
+     * @since 9
+     */
+    public static  MethodHandle zero(Class<?> type) {
+        Objects.requireNonNull(type);
+        return type.isPrimitive() ?  zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type);
+    }
+
+    private static MethodHandle identityOrVoid(Class<?> type) {
+        return type == void.class ? zero(type) : identity(type);
+    }
+
+    /**
+     * Produces a method handle of the requested type which ignores any arguments, does nothing,
+     * and returns a suitable default depending on the return type.
+     * That is, it returns a zero primitive value, a {@code null}, or {@code void}.
+     * <p>The returned method handle is equivalent to
+     * {@code dropArguments(zero(type.returnType()), 0, type.parameterList())}.
+     * <p>
+     * @apiNote Given a predicate and target, a useful "if-then" construct can be produced as
+     * {@code guardWithTest(pred, target, empty(target.type())}.
+     * @param type the type of the desired method handle
+     * @return a constant method handle of the given type, which returns a default value of the given return type
+     * @throws NullPointerException if the argument is null
+     * @see MethodHandles#zero
+     * @see MethodHandles#constant
+     * @since 9
+     */
+    public static  MethodHandle empty(MethodType type) {
+        Objects.requireNonNull(type);
+        return dropArguments(zero(type.returnType()), 0, type.parameterList());
+    }
+
     private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.values().length];
     private static MethodHandle makeIdentity(Class<?> ptype) {
         MethodType mtype = MethodType.methodType(ptype, ptype);
@@ -3145,8 +3189,7 @@
      * 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>
+     * @apiNote
      * <blockquote><pre>{@code
 import static java.lang.invoke.MethodHandles.*;
 import static java.lang.invoke.MethodType.*;
@@ -3185,6 +3228,99 @@
         return dropArguments(target, pos, Arrays.asList(valueTypes));
     }
 
+    // private version which allows caller some freedom with error handling
+    private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos,
+                                      boolean nullOnFailure) {
+        List<Class<?>> oldTypes = target.type().parameterList();
+        int match = oldTypes.size();
+        if (skip != 0) {
+            if (skip < 0 || skip > match) {
+                throw newIllegalArgumentException("illegal skip", skip, target);
+            }
+            oldTypes = oldTypes.subList(skip, match);
+            match -= skip;
+        }
+        List<Class<?>> addTypes = newTypes;
+        int add = addTypes.size();
+        if (pos != 0) {
+            if (pos < 0 || pos > add) {
+                throw newIllegalArgumentException("illegal pos", pos, newTypes);
+            }
+            addTypes = addTypes.subList(pos, add);
+            add -= pos; assert(addTypes.size() == add);
+        }
+        // Do not add types which already match the existing arguments.
+        if (match > add || !oldTypes.equals(addTypes.subList(0, match))) {
+            if (nullOnFailure) {
+                return null;
+            }
+            throw newIllegalArgumentException("argument lists do not match", oldTypes, newTypes);
+        }
+        addTypes = addTypes.subList(match, add);
+        add -= match; assert(addTypes.size() == add);
+        // newTypes:     (   P*[pos], M*[match], A*[add] )
+        // target: ( S*[skip],        M*[match]  )
+        MethodHandle adapter = target;
+        if (add > 0) {
+            adapter = dropArguments(adapter, skip+ match, addTypes);
+        }
+        // adapter: (S*[skip],        M*[match], A*[add] )
+        if (pos > 0) {
+            adapter = dropArguments(adapter, skip, newTypes.subList(0, pos));
+       }
+        // adapter: (S*[skip], P*[pos], M*[match], A*[add] )
+        return adapter;
+    }
+
+    /**
+     * Adapts a target method handle to match the given parameter type list, if necessary, by adding dummy arguments.
+     * Some leading parameters are first skipped; they will be left unchanged and are otherwise ignored.
+     * The remaining types in the target's parameter type list must be contained as a sub-list of the given type list,
+     * at the given position.
+     * Any non-matching parameter types (before or after the matching sub-list) are inserted in corresponding
+     * positions of the target method handle's parameters, as if by {@link #dropArguments}.
+     * (More precisely, elements in the new list before {@code pos} are inserted into the target list at {@code skip},
+     * while elements in the new list after the match beginning at {@code pos} are inserted at the end of the
+     * target list.)
+     * The target's return type will be unchanged.
+     * @apiNote
+     * Two method handles whose argument lists are "effectively identical" (i.e., identical
+     * in a common prefix) may be mutually converted to a common type
+     * by two calls to {@code dropArgumentsToMatch}, as follows:
+     * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+...
+MethodHandle h0 = constant(boolean.class, true);
+MethodHandle h1 = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
+MethodType bigType = h1.type().insertParameterTypes(1, String.class, int.class);
+MethodHandle h2 = dropArguments(h1, 0, bigType.parameterList());
+if (h1.type().parameterCount() < h2.type().parameterCount())
+    h1 = dropArgumentsToMatch(h1, 0, h2.type().parameterList(), 0);  // lengthen h1
+else
+    h2 = dropArgumentsToMatch(h2, 0, h1.type().parameterList(), 0);    // lengthen h2
+MethodHandle h3 = guardWithTest(h0, h1, h2);
+assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
+     * }</pre></blockquote>
+     * @param target the method handle to adapt
+     * @param skip number of targets parameters to disregard (they will be unchanged)
+     * @param newTypes the desired argument list of the method handle
+     * @param pos place in {@code newTypes} where the non-skipped target parameters must occur
+     * @return a possibly adapted method handle
+     * @throws NullPointerException if either argument is null
+     * @throws IllegalArgumentException
+     *         if either index is out of range in its corresponding list, or
+     *         if the non-skipped target parameter types match the new types at {@code pos}
+     * @since 9
+     */
+    public static
+    MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos) {
+        Objects.requireNonNull(target);
+        Objects.requireNonNull(newTypes);
+        return dropArgumentsToMatch(target, skip, newTypes, pos, false);
+    }
+
     /**
      * Adapts a target method handle by pre-processing
      * one or more of its arguments, each with its own unary filter function,
@@ -3696,13 +3832,9 @@
         if (gtype.returnType() != boolean.class)
             throw newIllegalArgumentException("guard type is not a predicate "+gtype);
         List<Class<?>> targs = ttype.parameterList();
-        List<Class<?>> gargs = gtype.parameterList();
-        if (!targs.equals(gargs)) {
-            int gpc = gargs.size(), tpc = targs.size();
-            if (gpc >= tpc || !targs.subList(0, gpc).equals(gargs))
-                throw misMatchedTypes("target and test types", ttype, gtype);
-            test = dropArguments(test, gpc, targs.subList(gpc, tpc));
-            gtype = test.type();
+        test = dropArgumentsToMatch(test, 0, targs, 0, true);
+        if (test == null) {
+            throw misMatchedTypes("target and test types", ttype, gtype);
         }
         return MethodHandleImpl.makeGuardWithTest(test, target, fallback);
     }
@@ -3774,15 +3906,9 @@
             throw newIllegalArgumentException("handler does not accept exception type "+exType);
         if (htype.returnType() != ttype.returnType())
             throw misMatchedTypes("target and handler return types", ttype, htype);
-        List<Class<?>> targs = ttype.parameterList();
-        List<Class<?>> hargs = htype.parameterList();
-        hargs = hargs.subList(1, hargs.size());  // omit leading parameter from handler
-        if (!targs.equals(hargs)) {
-            int hpc = hargs.size(), tpc = targs.size();
-            if (hpc >= tpc || !targs.subList(0, hpc).equals(hargs))
-                throw misMatchedTypes("target and handler types", ttype, htype);
-            handler = dropArguments(handler, 1+hpc, targs.subList(hpc, tpc));
-            htype = handler.type();
+        handler = dropArgumentsToMatch(handler, 1, ttype.parameterList(), 0, true);
+        if (handler == null) {
+            throw misMatchedTypes("target and handler types", ttype, htype);
         }
         return MethodHandleImpl.makeGuardWithCatch(target, exType, handler);
     }
@@ -4040,16 +4166,16 @@
         for (int i = 0; i < nclauses; ++i) {
             Class<?> t = iterationVariableTypes.get(i);
             if (init.get(i) == null) {
-                init.set(i, zeroHandle(t));
+                init.set(i, empty(MethodType.methodType(t, commonSuffix)));
             }
             if (step.get(i) == null) {
-                step.set(i, dropArguments(t == void.class ? zeroHandle(t) : identity(t), 0, commonPrefix.subList(0, i)));
+                step.set(i, dropArgumentsToMatch(identityOrVoid(t), 0, commonParameterSequence, i));
             }
             if (pred.get(i) == null) {
-                pred.set(i, constant(boolean.class, true));
+                pred.set(i, dropArguments(constant(boolean.class, true), 0, commonParameterSequence));
             }
             if (fini.get(i) == null) {
-                fini.set(i, zeroHandle(t));
+                fini.set(i, empty(MethodType.methodType(t, commonParameterSequence)));
             }
         }
 
@@ -4143,7 +4269,7 @@
      * @since 9
      */
     public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
-        MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType());
+        MethodHandle fin = init == null ? zero(void.class) : identity(init.type().returnType());
         MethodHandle[] checkExit = {null, null, pred, fin};
         MethodHandle[] varBody = {init, body};
         return loop(checkExit, varBody);
@@ -4209,7 +4335,7 @@
      * @since 9
      */
     public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
-        MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType());
+        MethodHandle fin = init == null ? zero(void.class) : identity(init.type().returnType());
         MethodHandle[] clause = {init, body, pred, fin};
         return loop(clause);
     }
@@ -4346,7 +4472,7 @@
      * @since 9
      */
     public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
-        MethodHandle returnVar = dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()),
+        MethodHandle returnVar = dropArguments(init == null ? zero(void.class) : identity(init.type().returnType()),
                 0, int.class, int.class);
         MethodHandle[] indexVar = {start, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep)};
         MethodHandle[] loopLimit = {end, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred), returnVar};
@@ -4449,7 +4575,7 @@
         Class<?> ttype = body.type().parameterType(0);
 
         MethodHandle returnVar =
-                dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), 0, itype);
+                dropArguments(init == null ? zero(void.class) : identity(init.type().returnType()), 0, itype);
         MethodHandle initnx = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext);
         MethodHandle nextVal = initnx.asType(initnx.type().changeReturnType(ttype));
 
@@ -4543,15 +4669,11 @@
         checkTryFinally(target, cleanup);
 
         // Match parameter lists: if the cleanup has a shorter parameter list than the target, add ignored arguments.
-        int tpSize = targetParamTypes.size();
-        int cpPrefixLength = rtype == void.class ? 1 : 2;
-        int cpSize = cleanupParamTypes.size();
-        MethodHandle aCleanup = cpSize - cpPrefixLength < tpSize ?
-                dropArguments(cleanup, cpSize, targetParamTypes.subList(tpSize - (cpSize - cpPrefixLength), tpSize)) :
-                cleanup;
-
+        // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the
+        // target parameter list.
+        cleanup = dropArgumentsToMatch(cleanup, (rtype == void.class ? 1 : 2), targetParamTypes, 0);
         MethodHandle aTarget = target.asSpreader(Object[].class, target.type().parameterCount());
-        aCleanup = aCleanup.asSpreader(Object[].class, tpSize);
+        MethodHandle aCleanup = cleanup.asSpreader(Object[].class, targetParamTypes.size());
 
         return MethodHandleImpl.makeTryFinally(aTarget, aCleanup, rtype, targetParamTypes);
     }
@@ -4642,16 +4764,6 @@
         return result;
     }
 
-    /**
-     * Wrap creation of a proper zero handle for a given type.
-     *
-     * @param type the type.
-     *
-     * @return a zero value for the given type.
-     */
-    static MethodHandle zeroHandle(Class<?> type) {
-        return type.isPrimitive() ?  zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type);
-    }
 
     private static void checkLoop0(MethodHandle[][] clauses) {
         if (clauses == null || clauses.length == 0) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/ConstantIdentityMHTest.java	Wed Apr 13 15:48:14 2016 +0200
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @run testng/othervm -ea -esa test.java.lang.invoke.ConstantIdentityMHTest
+ */
+package test.java.lang.invoke;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.List;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+import static org.testng.AssertJUnit.*;
+import org.testng.annotations.*;
+
+public class ConstantIdentityMHTest {
+
+    @DataProvider(name = "testZeroData")
+    private Object[][] testZeroData() {
+       return new Object[][] {
+           {void.class, "()void"},
+           {int.class, "()int"},
+           {byte.class, "()byte"},
+           {short.class, "()short"},
+           {long.class, "()long"},
+           {float.class, "()float"},
+           {double.class, "()double"},
+           {boolean.class, "()boolean"},
+           {char.class, "()char"},
+           {Integer.class, "()Integer"}
+       };
+    }
+
+    @Test(dataProvider = "testZeroData")
+    public void testZero(Class<?> expectedtype, String expected) throws Throwable {
+        assertEquals(MethodHandles.zero(expectedtype).type().toString(), expected);
+    }
+
+    @Test
+    @ExpectedExceptions(NullPointerException.class)
+    public void testZeroNPE() {
+        MethodHandle mh = MethodHandles.zero(null);
+    }
+
+    @Test
+    void testEmpty() throws Throwable {
+        MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
+        assertEquals("xy", (String)cat.invoke("x","y"));
+        MethodHandle mhEmpty = MethodHandles.empty(cat.type());
+        assertEquals(null, (String)mhEmpty.invoke("x","y"));
+    }
+
+    @Test
+    @ExpectedExceptions(NullPointerException.class)
+    void testEmptyNPE() {
+        MethodHandle lenEmptyMH = MethodHandles.empty(null);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/DropArgumentsTest.java	Wed Apr 13 15:48:14 2016 +0200
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @run testng/othervm -ea -esa test.java.lang.invoke.DropArgumentsTest
+ */
+package test.java.lang.invoke;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.List;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+import static org.testng.AssertJUnit.*;
+import org.testng.annotations.*;
+
+public class DropArgumentsTest {
+
+    @Test
+    public void testDropArgumentsToMatch() throws Throwable {
+        MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
+        MethodType bigType = cat.type().insertParameterTypes(0, String.class, String.class, int.class);
+        MethodHandle d0 = MethodHandles.dropArgumentsToMatch(cat, 0, bigType.parameterList(), 3);
+        assertEquals("xy",(String)d0.invokeExact("m", "n", 1, "x", "y"));
+        MethodHandle d1 = MethodHandles.dropArgumentsToMatch(cat, 0, bigType.parameterList(), 0);
+        assertEquals("mn",(String)d1.invokeExact("m", "n", 1, "x", "y"));
+        MethodHandle d2 = MethodHandles.dropArgumentsToMatch(cat, 1, bigType.parameterList(), 4);
+        assertEquals("xy",(String)d2.invokeExact("x", "b", "c", 1, "a", "y"));
+
+    }
+
+    @DataProvider(name = "dropArgumentsToMatchNPEData")
+    private Object[][] dropArgumentsToMatchNPEData()
+            throws NoSuchMethodException, IllegalAccessException {
+        MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
+        return new Object[][] {
+                { (MethodHandle) null, 0, cat.type().parameterList(), 0 },
+                { cat, 0, null, 0 }
+        };
+    }
+
+    @Test(dataProvider = "dropArgumentsToMatchNPEData")
+    @ExpectedExceptions(NullPointerException.class)
+    public void dropArgumentsToMatchNPE(MethodHandle target, int pos, List<Class<?>> valueType, int skip) {
+        MethodHandles.dropArgumentsToMatch(target, pos, valueType , skip);
+    }
+
+    @DataProvider(name = "dropArgumentsToMatchIAEData")
+    private Object[][] dropArgumentsToMatchIAEData()
+        throws NoSuchMethodException, IllegalAccessException {
+        MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
+        MethodType bigType = cat.type().insertParameterTypes(0, String.class, String.class, int.class);
+        return new Object[][] {
+            {cat, -1, bigType.parameterList(), 0},
+            {cat, 0, bigType.parameterList(), -1},
+            {cat, 3, bigType.parameterList(), 0},
+            {cat, 0, bigType.parameterList(), 2}
+        };
+    }
+
+    @Test(dataProvider = "dropArgumentsToMatchIAEData")
+    @ExpectedExceptions(IllegalArgumentException.class)
+    public void dropArgumentsToMatchIAE(MethodHandle target, int pos, List<Class<?>> valueType, int skip) {
+        MethodHandles.dropArgumentsToMatch(target, pos, valueType , skip);
+    }
+}
--- a/jdk/test/java/lang/invoke/JavaDocExamplesTest.java	Wed Apr 13 14:29:25 2016 +0100
+++ b/jdk/test/java/lang/invoke/JavaDocExamplesTest.java	Wed Apr 13 15:48:14 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2016, 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
@@ -57,7 +57,9 @@
         testFindVirtual();
         testFindSpecial();
         testPermuteArguments();
+        testZero();
         testDropArguments();
+        testDropArgumentsToMatch();
         testFilterArguments();
         testFoldArguments();
         testFoldArguments2();
@@ -235,6 +237,17 @@
             }}
     }
 
+@Test public void testZero() throws Throwable {
+        {{
+{} /// JAVADOC
+Class<?> type = Double.class;
+MethodHandle mh1 = MethodHandles.explicitCastArguments(MethodHandles.constant(Object.class, null), methodType(type));
+assertEquals("()Double", mh1.type().toString());
+MethodHandle mh2 = MethodHandles.empty(methodType(type));
+assertEquals("()Double", mh2.type().toString());
+        }}
+   }
+
     @Test public void testDropArguments() throws Throwable {
         {{
 {} /// JAVADOC
@@ -262,6 +275,24 @@
             }}
     }
 
+    @Test public void testDropArgumentsToMatch() throws Throwable {
+        {{
+{} /// JAVADOC
+MethodHandle h0= constant(boolean.class, true);
+MethodHandle h1 = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
+MethodType bigType = h1.type().insertParameterTypes(1, String.class, int.class);
+MethodHandle h2 = dropArguments(h1, 0, bigType.parameterList());
+if (h1.type().parameterCount() < h2.type().parameterCount()) {
+    h1 = dropArgumentsToMatch(h1, 0, h2.type().parameterList(), 0);  // lengthen h1
+}
+else {
+    h2 = dropArgumentsToMatch(h2, 0, h1.type().parameterList(), 0);    // lengthen h2
+}
+MethodHandle h3 = guardWithTest(h0, h1, h2);
+assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
+        }}
+    }
+
     @Test public void testFilterArguments() throws Throwable {
         {{
 {} /// JAVADOC
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/VarArgsTest.java	Wed Apr 13 15:48:14 2016 +0200
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @run testng/othervm -ea -esa test.java.lang.invoke.VarArgsTest
+ */
+package test.java.lang.invoke;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+import java.util.List;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+import static org.testng.AssertJUnit.*;
+import org.testng.annotations.*;
+
+public class VarArgsTest {
+
+    @Test
+    public void testWithVarargs() throws Throwable {
+        MethodHandle deepToString = publicLookup()
+            .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
+        assertFalse(deepToString.isVarargsCollector());
+        MethodHandle ts = deepToString.withVarargs(false);
+        assertFalse(ts.isVarargsCollector());
+        MethodHandle ts1 = deepToString.withVarargs(true);
+        assertTrue(ts1.isVarargsCollector());
+        assertEquals("[won]", (String) ts1.invokeExact(new Object[]{"won"}));
+        assertEquals("[won]", (String) ts1.invoke(new Object[]{"won"}));
+        assertEquals("[won]", (String) ts1.invoke("won"));
+        assertEquals("[won, won]", (String) ts1.invoke("won", "won"));
+        assertEquals("[won, won]", (String) ts1.invoke(new Object[]{"won", "won"}));
+        assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
+    }
+
+    @Test
+    public void testWithVarargs2() throws Throwable {
+        MethodHandle asList = publicLookup()
+            .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
+        MethodHandle asListWithVarargs = asList.withVarargs(asList.isVarargsCollector());
+        assert(asListWithVarargs.isVarargsCollector());
+        assertEquals("[]", asListWithVarargs.invoke().toString());
+        assertEquals("[1]", asListWithVarargs.invoke(1).toString());
+        assertEquals("[two, too]", asListWithVarargs.invoke("two", "too").toString());
+    }
+
+    @Test
+    @ExpectedExceptions(IllegalArgumentException.class)
+    public void testWithVarargsIAE() throws Throwable {
+        MethodHandle lenMH = publicLookup()
+            .findVirtual(String.class, "length", methodType(int.class));
+        MethodHandle lenMHWithVarargs = lenMH.withVarargs(true);
+    }
+
+}