Add MethodHandles.dropArgumentsToMatch() from OpenJDK 11.0.13

Bug: 191446452
Test: atest CtsLibcoreOjTestCases:test.java.lang.invoke.DropArgumentsTest
Change-Id: I8be5fe72b4890be2985cf688c3b152bfa57c3e04
diff --git a/api/current.txt b/api/current.txt
index d0374f0..dc1b700 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -4349,6 +4349,7 @@
     method public static java.lang.invoke.MethodHandle constant(Class<?>, Object);
     method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
     method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, Class<?>...);
+    method public static java.lang.invoke.MethodHandle dropArgumentsToMatch(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>, int);
     method public static java.lang.invoke.MethodHandle empty(java.lang.invoke.MethodType);
     method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
     method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType);
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
index 8e71d68..e80c1cc 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
@@ -3048,16 +3048,17 @@
         return oldType.ptypes();
     }
 
+    // Android-changed: inclusive language preference for 'placeholder'.
     /**
-     * Produces a method handle which will discard some dummy arguments
+     * Produces a method handle which will discard some placeholder 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,
+     * except it will also include the placeholder argument types,
      * at some given position.
      * <p>
      * 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
+     * If {@code pos} is zero, the placeholder arguments will precede
      * the target's real arguments; if {@code pos} is <i>N</i>
      * they will come after.
      * <p>
@@ -3092,21 +3093,29 @@
      */
     public static
     MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
-        valueTypes = copyTypes(valueTypes);
-        MethodType oldType = target.type();  // get NPE
-        int dropped = dropArgumentChecks(oldType, pos, valueTypes);
-
-        MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
-        if (dropped == 0) {
-            return target;
-        }
-
-        return new Transformers.DropArguments(newType, target, pos, valueTypes.size());
+        return dropArguments0(target, pos, copyTypes(valueTypes.toArray()));
     }
 
-    private static List<Class<?>> copyTypes(List<Class<?>> types) {
-        Object[] a = types.toArray();
-        return Arrays.asList(Arrays.copyOf(a, a.length, Class[].class));
+    private static List<Class<?>> copyTypes(Object[] array) {
+        return Arrays.asList(Arrays.copyOf(array, array.length, Class[].class));
+    }
+
+    private static
+    MethodHandle dropArguments0(MethodHandle target, int pos, List<Class<?>> valueTypes) {
+        MethodType oldType = target.type();  // get NPE
+        int dropped = dropArgumentChecks(oldType, pos, valueTypes);
+        MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
+        if (dropped == 0)  return target;
+        // Android-changed: transformer implementation.
+        // BoundMethodHandle result = target.rebind();
+        // LambdaForm lform = result.form;
+        // int insertFormArg = 1 + pos;
+        // for (Class<?> ptype : valueTypes) {
+        //     lform = lform.editor().addArgumentForm(insertFormArg++, BasicType.basicType(ptype));
+        // }
+        // result = result.copyWith(newType, lform);
+        // return result;
+        return new Transformers.DropArguments(newType, target, pos, dropped);
     }
 
     private static int dropArgumentChecks(MethodType oldType, int pos, List<Class<?>> valueTypes) {
@@ -3121,20 +3130,20 @@
         return dropped;
     }
 
+    // Android-changed: inclusive language preference for 'placeholder'.
     /**
-     * Produces a method handle which will discard some dummy arguments
+     * Produces a method handle which will discard some placeholder 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,
+     * except it will also include the placeholder argument types,
      * at some given position.
      * <p>
      * 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
+     * If {@code pos} is zero, the placeholder 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.*;
@@ -3170,7 +3179,117 @@
      */
     public static
     MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
-        return dropArguments(target, pos, Arrays.asList(valueTypes));
+        return dropArguments0(target, pos, copyTypes(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) {
+        newTypes = copyTypes(newTypes.toArray());
+        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 = dropArguments0(adapter, skip+ match, addTypes);
+        }
+        // adapter: (S*[skip],        M*[match], A*[add] )
+        if (pos > 0) {
+            adapter = dropArguments0(adapter, skip, newTypes.subList(0, pos));
+        }
+        // adapter: (S*[skip], P*[pos], M*[match], A*[add] )
+        return adapter;
+    }
+
+    // Android-changed: inclusive language preference for 'placeholder'.
+    /**
+     * Adapts a target method handle to match the given parameter type list. If necessary, adds placeholder arguments. Some
+     * leading parameters can be skipped before matching begins. The remaining types in the {@code target}'s parameter
+     * type list must be a sub-list of the {@code newTypes} type list at the starting position {@code pos}. The
+     * resulting handle will have the target handle's parameter type list, with any non-matching parameter types (before
+     * or after the matching sub-list) inserted in corresponding positions of the target's original parameters, as if by
+     * {@link #dropArguments(MethodHandle, int, Class[])}.
+     * <p>
+     * The resulting handle will have the same return type as the target handle.
+     * <p>
+     * In more formal terms, assume these two type lists:<ul>
+     * <li>The target handle has the parameter type list {@code S..., M...}, with as many types in {@code S} as
+     * indicated by {@code skip}. The {@code M} types are those that are supposed to match part of the given type list,
+     * {@code newTypes}.
+     * <li>The {@code newTypes} list contains types {@code P..., M..., A...}, with as many types in {@code P} as
+     * indicated by {@code pos}. The {@code M} types are precisely those that the {@code M} types in the target handle's
+     * parameter type list are supposed to match. The types in {@code A} are additional types found after the matching
+     * sub-list.
+     * </ul>
+     * Given these assumptions, the result of an invocation of {@code dropArgumentsToMatch} will have the parameter type
+     * list {@code S..., P..., M..., A...}, with the {@code P} and {@code A} types inserted as if by
+     * {@link #dropArguments(MethodHandle, int, Class[])}.
+     *
+     * @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 list of types to match {@code target}'s parameter type list to
+     * @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 any element of {@code newTypes} is {@code void.class},
+     *         or if {@code skip} is negative or greater than the arity of the target,
+     *         or if {@code pos} is negative or greater than the newTypes list size,
+     *         or if {@code newTypes} does not contain the {@code target}'s non-skipped parameter types at position
+     *         {@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);
     }
 
     /**
diff --git a/ojluni/src/main/java/java/lang/invoke/Transformers.java b/ojluni/src/main/java/java/lang/invoke/Transformers.java
index 17a2594..5dda0e3 100644
--- a/ojluni/src/main/java/java/lang/invoke/Transformers.java
+++ b/ojluni/src/main/java/java/lang/invoke/Transformers.java
@@ -115,14 +115,8 @@
     /** Implements {@code MethodHandles.dropArguments}. */
     public static class DropArguments extends Transformer {
         private final MethodHandle delegate;
-
         private final EmulatedStackFrame.Range range1;
-
-        /**
-         * Note that {@code range2} will be null if the arguments that are being dropped are the
-         * last {@code n}.
-         */
-        /* @Nullable */ private final EmulatedStackFrame.Range range2;
+        private final EmulatedStackFrame.Range range2;
 
         public DropArguments(MethodType type, MethodHandle delegate, int startPos, int numDropped) {
             super(type);
diff --git a/ojluni/src/test/java/lang/invoke/DropArgumentsTest.java b/ojluni/src/test/java/lang/invoke/DropArgumentsTest.java
new file mode 100644
index 0000000..be65b39
--- /dev/null
+++ b/ojluni/src/test/java/lang/invoke/DropArgumentsTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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
+ * @bug  8158169
+ * @summary unit tests for java.lang.invoke.MethodHandles
+ * @run testng 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.Collections;
+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(), 6},
+            {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);
+    }
+
+    @Test
+    @ExpectedExceptions(IllegalArgumentException.class)
+    public void dropArgumentsToMatchTestWithVoid() throws Throwable {
+        MethodHandle cat = lookup().findVirtual(String.class, "concat",
+                                   MethodType.methodType(String.class, String.class));
+        MethodType bigTypewithVoid = cat.type().insertParameterTypes(0, void.class, String.class, int.class);
+        MethodHandle handle2 = MethodHandles.dropArgumentsToMatch(cat, 0, bigTypewithVoid.parameterList(), 1);
+    }
+
+    public static class MethodSet {
+
+        static void mVoid() {
+
+        }
+
+        static void mVoid(int t) {
+
+        }
+    }
+
+    @Test
+    public void dropArgumentsToMatchPosSkipRange() throws Throwable {
+        // newTypes.size() == 1, pos == 1   &&   target.paramSize() == 0, skip == 0
+        MethodHandle mh1 = MethodHandles.lookup().findStatic(MethodSet.class, "mVoid",
+                                                             MethodType.methodType(void.class));
+        MethodHandle handle1 = dropArgumentsToMatch(mh1, 0, Collections.singletonList(int.class), 1);
+        assertEquals(1, handle1.type().parameterList().size());
+
+        // newTypes.size() == 1, pos == 0   &&   target.paramSize() == 1, skip == 1
+        MethodHandle mh2 = MethodHandles.lookup().findStatic(MethodSet.class, "mVoid",
+                                                             MethodType.methodType(void.class, int.class));
+        MethodHandle handle2 = dropArgumentsToMatch(mh2, 1, Collections.singletonList(int.class), 0);
+        assertEquals(2, handle2.type().parameterList().size());
+    }
+}
diff --git a/ojluni/src/test/java/lang/invoke/JavaDocExamplesTest.java b/ojluni/src/test/java/lang/invoke/JavaDocExamplesTest.java
index c9873df..ae7ae63 100644
--- a/ojluni/src/test/java/lang/invoke/JavaDocExamplesTest.java
+++ b/ojluni/src/test/java/lang/invoke/JavaDocExamplesTest.java
@@ -281,8 +281,6 @@
             }}
     }
 
-// Android-removed: dropArguments to match not yet supported (b/191446452).
-/*
     @Test public void testDropArgumentsToMatch() throws Throwable {
         {{
 {} /// JAVADOC
@@ -300,7 +298,7 @@
 assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
         }}
     }
-*/
+
     @Test public void testFilterArguments() throws Throwable {
         {{
 {} /// JAVADOC