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 {
{{