Add MethodHandles.foldArguments(MH, int, MH) from OpenJDK 11.0.13

Bug: 191446452
Test: atest CtsLibcoreOjTestCases:test.java.lang.invoke
Test: atest CtsLibcoreTestCases:libcore.java.lang.invoke
Change-Id: Iaf22026c04b619407c624f9ab992aa484574ee4c
diff --git a/api/current.txt b/api/current.txt
index dc1b700..1cd7841 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -4356,6 +4356,7 @@
     method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
     method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
     method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle);
     method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
     method public static java.lang.invoke.MethodHandle identity(Class<?>);
     method public static java.lang.invoke.MethodHandle insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...);
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
index e80c1cc..2780a11 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
@@ -3632,7 +3632,15 @@
 // also prints "boo":
 assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
      * }</pre></blockquote>
-     * <p> Here is pseudocode for the resulting adapter:
+     * <p>Here is pseudocode for the resulting adapter. In the code, {@code T}
+     * represents the result type of the {@code target} and resulting adapter.
+     * {@code V}/{@code v} represent the type and value of the parameter and argument
+     * of {@code target} that precedes the folding position; {@code V} also is
+     * the result type of the {@code combiner}. {@code A}/{@code a} denote the
+     * types and values of the {@code N} parameters and arguments at the folding
+     * position. {@code B}/{@code b} represent the types and values of the
+     * {@code target} parameters and arguments that follow the folded parameters
+     * and arguments.
      * <blockquote><pre>{@code
      * // there are N arguments in A...
      * T target(V, A[N]..., B...);
@@ -3649,6 +3657,9 @@
      *   return target2(a..., b...);
      * }
      * }</pre></blockquote>
+     * <p>
+     * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+     * variable-arity method handle}, even if the original target method handle was.
      * @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
@@ -3662,12 +3673,96 @@
      */
     public static
     MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
-        int foldPos = 0;
+        return foldArguments(target, 0, combiner);
+    }
+
+    /**
+     * Adapts a target method handle by pre-processing some of its arguments, starting at a given position, and then
+     * calling the target with the result of the pre-processing, inserted into the original sequence of arguments just
+     * before the folded arguments.
+     * <p>
+     * This method is closely related to {@link #foldArguments(MethodHandle, MethodHandle)}, but allows to control the
+     * position in the parameter list at which folding takes place. The argument controlling this, {@code pos}, is a
+     * zero-based index. The aforementioned method {@link #foldArguments(MethodHandle, MethodHandle)} assumes position
+     * 0.
+     *
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+    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, 1, trace);
+    // also prints "jum":
+    assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+     * }</pre></blockquote>
+     * <p>Here is pseudocode for the resulting adapter. In the code, {@code T}
+     * represents the result type of the {@code target} and resulting adapter.
+     * {@code V}/{@code v} represent the type and value of the parameter and argument
+     * of {@code target} that precedes the folding position; {@code V} also is
+     * the result type of the {@code combiner}. {@code A}/{@code a} denote the
+     * types and values of the {@code N} parameters and arguments at the folding
+     * position. {@code Z}/{@code z} and {@code B}/{@code b} represent the types
+     * and values of the {@code target} parameters and arguments that precede and
+     * follow the folded parameters and arguments starting at {@code pos},
+     * respectively.
+     * <blockquote><pre>{@code
+     * // there are N arguments in A...
+     * T target(Z..., V, A[N]..., B...);
+     * V combiner(A...);
+     * T adapter(Z... z, A... a, B... b) {
+     *   V v = combiner(a...);
+     *   return target(z..., v, a..., b...);
+     * }
+     * // and if the combiner has a void return:
+     * T target2(Z..., A[N]..., B...);
+     * void combiner2(A...);
+     * T adapter2(Z... z, A... a, B... b) {
+     *   combiner2(a...);
+     *   return target2(z..., a..., b...);
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+     * variable-arity method handle}, even if the original target method handle was.
+     *
+     * @param target the method handle to invoke after arguments are combined
+     * @param pos the position at which to start folding and at which to insert the folding result; if this is {@code
+     *            0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}.
+     * @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 either of the following two conditions holds:
+     *          (1) {@code combiner}'s return type is non-{@code void} and not the same as the argument type at position
+     *              {@code pos} of the target signature;
+     *          (2) the {@code N} argument types at position {@code pos} of the target signature (skipping one matching
+     *              the {@code combiner}'s return type) are not identical with the argument types of {@code combiner}.
+     *
+     * @see #foldArguments(MethodHandle, MethodHandle)
+     * @since 9
+     */
+    public static
+    MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner) {
         MethodType targetType = target.type();
         MethodType combinerType = combiner.type();
-        Class<?> rtype = foldArgumentChecks(foldPos, targetType, combinerType);
+        Class<?> rtype = foldArgumentChecks(pos, targetType, combinerType);
+        // Android-changed: // Android-changed: transformer implementation.
+        // BoundMethodHandle result = target.rebind();
+        // boolean dropResult = rtype == void.class;
+        // LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType());
+        // MethodType newType = targetType;
+        // if (!dropResult) {
+        //     newType = newType.dropParameterTypes(pos, pos + 1);
+        // }
+        // result = result.copyWithExtendL(newType, lform, combiner);
+        // return result;
 
-        return new Transformers.FoldArguments(target, combiner);
+        return new Transformers.FoldArguments(target, pos, combiner);
     }
 
     private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
@@ -3676,11 +3771,15 @@
         int foldVals = rtype == void.class ? 0 : 1;
         int afterInsertPos = foldPos + foldVals;
         boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
-        if (ok && !(combinerType.parameterList()
-                    .equals(targetType.parameterList().subList(afterInsertPos,
-                                                               afterInsertPos + foldArgs))))
-            ok = false;
-        if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0))
+        if (ok) {
+            for (int i = 0; i < foldArgs; i++) {
+                if (combinerType.parameterType(i) != targetType.parameterType(i + afterInsertPos)) {
+                    ok = false;
+                    break;
+                }
+            }
+        }
+        if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos))
             ok = false;
         if (!ok)
             throw misMatchedTypes("target and combiner types", targetType, combinerType);
diff --git a/ojluni/src/main/java/java/lang/invoke/Transformers.java b/ojluni/src/main/java/java/lang/invoke/Transformers.java
index 5dda0e3..0751cfe 100644
--- a/ojluni/src/main/java/java/lang/invoke/Transformers.java
+++ b/ojluni/src/main/java/java/lang/invoke/Transformers.java
@@ -1758,21 +1758,31 @@
     static class FoldArguments extends Transformer {
         private final MethodHandle target;
         private final MethodHandle combiner;
+        private final int position;
 
+        /** The range of arguments in our frame passed to the combiner. */
         private final Range combinerArgs;
-        private final Range targetArgs;
+
+        /** The range of arguments in our frame copied to the start of the target frame. */
+        private final Range leadingArgs;
+
+        /** The range of arguments in our frame copied to the end of the target frame. */
+        private final Range trailingArgs;
 
         private final int referencesOffset;
         private final int stackFrameOffset;
 
-        FoldArguments(MethodHandle target, MethodHandle combiner) {
-            super(deriveType(target, combiner));
+        FoldArguments(MethodHandle target, int position, MethodHandle combiner) {
+            super(deriveType(target, position, combiner));
 
             this.target = target;
             this.combiner = combiner;
+            this.position = position;
 
-            combinerArgs = Range.all(combiner.type());
-            targetArgs = Range.all(type());
+            this.combinerArgs =
+                    Range.of(type(), position, position + combiner.type().parameterCount());
+            this.leadingArgs = Range.of(type(), 0, position);
+            this.trailingArgs = Range.from(type(), position);
 
             final Class<?> combinerRType = combiner.type().rtype();
             if (combinerRType == void.class) {
@@ -1782,6 +1792,7 @@
                 stackFrameOffset = EmulatedStackFrame.getSize(combinerRType);
                 referencesOffset = 0;
             } else {
+                // combinerRType is a reference.
                 stackFrameOffset = 0;
                 referencesOffset = 1;
             }
@@ -1789,35 +1800,47 @@
 
         @Override
         public void transform(EmulatedStackFrame stackFrame) throws Throwable {
-            // First construct the combiner frame and invoke it.
+            // First construct the combiner frame and invoke the combiner.
             EmulatedStackFrame combinerFrame = EmulatedStackFrame.create(combiner.type());
             stackFrame.copyRangeTo(combinerFrame, combinerArgs, 0, 0);
-            invokeFromTransform(combiner, combinerFrame);
+            invokeExactFromTransform(combiner, combinerFrame);
 
-            // Create the stack frame for the target.
+            // Create the stack frame for the target and copy leading arguments to it.
             EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
+            stackFrame.copyRangeTo(targetFrame, leadingArgs, 0, 0);
 
-            // If one of these offsets is not zero, we have a return value to copy.
+            // If one of these offsets is not zero, we have to slot the return value from the
+            // combiner into the target frame.
             if (referencesOffset != 0 || stackFrameOffset != 0) {
                 final StackFrameReader reader = new StackFrameReader();
                 reader.attach(combinerFrame).makeReturnValueAccessor();
                 final StackFrameWriter writer = new StackFrameWriter();
-                writer.attach(targetFrame);
-                copyNext(reader, writer, target.type().ptypes()[0]);
+                writer.attach(targetFrame,
+                              position,
+                              leadingArgs.numReferences,
+                              leadingArgs.numBytes);
+                copyNext(reader, writer, target.type().ptypes()[position]);
             }
 
-            stackFrame.copyRangeTo(targetFrame, targetArgs, referencesOffset, stackFrameOffset);
-            invokeFromTransform(target, targetFrame);
+            // Copy the arguments provided to the combiner to the tail of the target frame.
+            stackFrame.copyRangeTo(
+                targetFrame,
+                trailingArgs,
+                leadingArgs.numReferences + referencesOffset,
+                leadingArgs.numBytes + stackFrameOffset);
 
+            // Call the target and propagate return value.
+            invokeExactFromTransform(target, targetFrame);
             targetFrame.copyReturnValueTo(stackFrame);
         }
 
-        private static MethodType deriveType(MethodHandle target, MethodHandle combiner) {
+        private static MethodType deriveType(MethodHandle target,
+                                             int position,
+                                             MethodHandle combiner) {
             if (combiner.type().rtype() == void.class) {
                 return target.type();
             }
-
-            return target.type().dropParameterTypes(0, 1);
+            return target.type().dropParameterTypes(position, position + 1);
         }
     }
 
diff --git a/ojluni/src/test/java/lang/invoke/FoldTest.java b/ojluni/src/test/java/lang/invoke/FoldTest.java
new file mode 100644
index 0000000..1e8e4f5
--- /dev/null
+++ b/ojluni/src/test/java/lang/invoke/FoldTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015, 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 8139885
+ * @run testng/othervm -ea -esa test.java.lang.invoke.FoldTest
+ */
+
+package test.java.lang.invoke;
+
+import java.io.StringWriter;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+
+import static java.lang.invoke.MethodType.methodType;
+
+import static org.testng.AssertJUnit.*;
+
+import org.testng.annotations.*;
+
+/**
+ * Tests for the new fold method handle combinator added in JEP 274.
+ */
+public class FoldTest {
+
+    static final Lookup LOOKUP = MethodHandles.lookup();
+
+    @Test
+    public static void testFold0a() throws Throwable {
+        // equivalence to foldArguments(MethodHandle,MethodHandle)
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 0, Fold.MH_adder);
+        assertEquals(Fold.MT_folded1, fold.type());
+        assertEquals(720, (int) fold.invoke(3, 4, 5));
+    }
+
+    @Test
+    public static void testFold1a() throws Throwable {
+        // test foldArguments for folding position 1
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 1, Fold.MH_adder1);
+        assertEquals(Fold.MT_folded1, fold.type());
+        assertEquals(540, (int) fold.invoke(3, 4, 5));
+    }
+
+    @Test
+    public static void testFold0b() throws Throwable {
+        // test foldArguments equivalence with multiple types
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 0, Fold.MH_comb);
+        assertEquals(Fold.MT_folded2, fold.type());
+        assertEquals(23, (int) fold.invoke("true", true, 23));
+    }
+
+    @Test
+    public static void testFold1b() throws Throwable {
+        // test folgArguments for folding position 1, with multiple types
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 1, Fold.MH_comb2);
+        assertEquals(Fold.MT_folded3, fold.type());
+        assertEquals(1, (int) fold.invoke(true, true, 1));
+        assertEquals(-1, (int) fold.invoke(true, false, -1));
+    }
+
+    @Test
+    public static void testFoldArgumentsExample() throws Throwable {
+        // test the JavaDoc foldArguments-with-pos example
+        StringWriter swr = new StringWriter();
+        MethodHandle trace = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, String.class)).bindTo(swr);
+        MethodHandle cat = LOOKUP.findVirtual(String.class, "concat", methodType(String.class, String.class));
+        assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
+        MethodHandle catTrace = MethodHandles.foldArguments(cat, 1, trace);
+        assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+        assertEquals("jum", swr.toString());
+    }
+
+    static class Fold {
+
+        static int adder(int a, int b, int c) {
+            return a + b + c;
+        }
+
+        static int adder1(int a, int b) {
+            return a + b;
+        }
+
+        static int multer(int x, int q, int r, int s) {
+            return x * q * r * s;
+        }
+
+        static int str(boolean b1, String s, boolean b2, int x) {
+            return b1 && s.equals(String.valueOf(b2)) ? x : -x;
+        }
+
+        static boolean comb(String s, boolean b2) {
+            return !s.equals(b2);
+        }
+
+        static String comb2(boolean b2, int x) {
+            int ib = b2 ? 1 : 0;
+            return ib == x ? "true" : "false";
+        }
+
+        static final Class<Fold> FOLD = Fold.class;
+
+        static final MethodType MT_adder = methodType(int.class, int.class, int.class, int.class);
+        static final MethodType MT_adder1 = methodType(int.class, int.class, int.class);
+        static final MethodType MT_multer = methodType(int.class, int.class, int.class, int.class, int.class);
+        static final MethodType MT_str = methodType(int.class, boolean.class, String.class, boolean.class, int.class);
+        static final MethodType MT_comb = methodType(boolean.class, String.class, boolean.class);
+        static final MethodType MT_comb2 = methodType(String.class, boolean.class, int.class);
+
+        static final MethodHandle MH_adder;
+        static final MethodHandle MH_adder1;
+        static final MethodHandle MH_multer;
+        static final MethodHandle MH_str;
+        static final MethodHandle MH_comb;
+        static final MethodHandle MH_comb2;
+
+        static final MethodType MT_folded1 = methodType(int.class, int.class, int.class, int.class);
+        static final MethodType MT_folded2 = methodType(int.class, String.class, boolean.class, int.class);
+        static final MethodType MT_folded3 = methodType(int.class, boolean.class, boolean.class, int.class);
+
+        static {
+            try {
+                MH_adder = LOOKUP.findStatic(FOLD, "adder", MT_adder);
+                MH_adder1 = LOOKUP.findStatic(FOLD, "adder1", MT_adder1);
+                MH_multer = LOOKUP.findStatic(FOLD, "multer", MT_multer);
+                MH_str = LOOKUP.findStatic(FOLD, "str", MT_str);
+                MH_comb = LOOKUP.findStatic(FOLD, "comb", MT_comb);
+                MH_comb2 = LOOKUP.findStatic(FOLD, "comb2", MT_comb2);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/lang/invoke/JavaDocExamplesTest.java b/ojluni/src/test/java/lang/invoke/JavaDocExamplesTest.java
index ae7ae63..f87868e 100644
--- a/ojluni/src/test/java/lang/invoke/JavaDocExamplesTest.java
+++ b/ojluni/src/test/java/lang/invoke/JavaDocExamplesTest.java
@@ -686,14 +686,14 @@
             }}
     }
 
+// BEGIN Android-removed: removed until supported (b/191446452).
+/*
     static int one(int k) { return 1; }
     static int inc(int i, int acc, int k) { return i + 1; }
     static int mult(int i, int acc, int k) { return i * acc; }
     static boolean pred(int i, int acc, int k) { return i < k; }
     static int fin(int i, int acc, int k) { return acc; }
 
-// BEGIN Android-removed: removed until supported (b/191446452).
-/*
     @Test public void testLoop() throws Throwable {
         MethodHandle MH_inc, MH_one, MH_mult, MH_pred, MH_fin;
         Class<?> I = int.class;
@@ -905,6 +905,8 @@
 {}
         }}
     }
+*/
+// END Android-removed: removed until supported (b/191446452).
 
     @Test public void testFoldArguments3() throws Throwable {
         {{
@@ -951,8 +953,7 @@
 {}
         }}
     }
-*/
-// END Android-removed: removed until supported (b/191446452).
+
     /* ---- TEMPLATE ----
     @Test public void testFoo() throws Throwable {
         {{