diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index ca4f050553f..05ef3c983f0 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -1962,12 +1962,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; * This method is bound as the predicate in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, * MethodHandle) counting loops}. * - * @param counter the counter parameter, passed in during loop execution. * @param limit the upper bound of the parameter, statically bound at loop creation time. + * @param counter the counter parameter, passed in during loop execution. * * @return whether the counter has reached the limit. */ - static boolean countedLoopPredicate(int counter, int limit) { + static boolean countedLoopPredicate(int limit, int counter) { return counter < limit; } @@ -1975,26 +1975,15 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; * This method is bound as the step function in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, * MethodHandle) counting loops} to increment the counter. * + * @param limit the upper bound of the loop counter (ignored). * @param counter the loop counter. * * @return the loop counter incremented by 1. */ - static int countedLoopStep(int counter, int limit) { + static int countedLoopStep(int limit, int counter) { return counter + 1; } - /** - * This method is bound as a filter in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, MethodHandle, - * MethodHandle) counting loops} to pass the correct counter value to the body. - * - * @param counter the loop counter. - * - * @return the loop counter decremented by 1. - */ - static int decrementCounter(int counter) { - return counter - 1; - } - /** * This is bound to initialize the loop-local iterator in {@linkplain MethodHandles#iteratedLoop iterating loops}. * @@ -2164,12 +2153,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; MH_arrayIdentity = 5, MH_countedLoopPred = 6, MH_countedLoopStep = 7, - MH_iteratePred = 8, - MH_initIterator = 9, + MH_initIterator = 8, + MH_iteratePred = 9, MH_iterateNext = 10, - MH_decrementCounter = 11, - MH_Array_newInstance = 12, - MH_LIMIT = 13; + MH_Array_newInstance = 11, + MH_LIMIT = 12; static MethodHandle getConstantHandle(int idx) { MethodHandle handle = HANDLES[idx]; @@ -2220,18 +2208,15 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; case MH_countedLoopStep: return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopStep", MethodType.methodType(int.class, int.class, int.class)); - case MH_iteratePred: - return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate", - MethodType.methodType(boolean.class, Iterator.class)); case MH_initIterator: return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "initIterator", MethodType.methodType(Iterator.class, Iterable.class)); + case MH_iteratePred: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate", + MethodType.methodType(boolean.class, Iterator.class)); case MH_iterateNext: return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iterateNext", MethodType.methodType(Object.class, Iterator.class)); - case MH_decrementCounter: - return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "decrementCounter", - MethodType.methodType(int.class, int.class)); case MH_Array_newInstance: return IMPL_LOOKUP.findStatic(Array.class, "newInstance", MethodType.methodType(Object.class, Class.class, int.class)); diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index 28364e53ae9..104aa220932 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -44,8 +44,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ReflectPermission; import java.nio.ByteOrder; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@ -3114,7 +3112,7 @@ assert((int)twice.invokeExact(21) == 42); * @see MethodHandles#explicitCastArguments * @since 9 */ - public static MethodHandle zero(Class type) { + public static MethodHandle zero(Class type) { Objects.requireNonNull(type); return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type); } @@ -3403,7 +3401,8 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); throw newIllegalArgumentException("illegal pos", pos, newTypes); } addTypes = addTypes.subList(pos, add); - add -= pos; assert(addTypes.size() == 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))) { @@ -3413,7 +3412,8 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); throw newIllegalArgumentException("argument lists do not match", oldTypes, newTypes); } addTypes = addTypes.subList(match, add); - add -= match; assert(addTypes.size() == add); + add -= match; + assert(addTypes.size() == add); // newTypes: ( P*[pos], M*[match], A*[add] ) // target: ( S*[skip], M*[match] ) MethodHandle adapter = target; @@ -3423,26 +3423,37 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); // 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; } /** - * 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. + * Adapts a target method handle to match the given parameter type list. If necessary, adds dummy 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[])}. + *

+ * The resulting handle will have the same return type as the target handle. + *

+ * In more formal terms, assume these two type lists:

+ * 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: + * 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: *

{@code
 import static java.lang.invoke.MethodHandles.*;
 import static java.lang.invoke.MethodType.*;
@@ -3461,14 +3472,15 @@ assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
      * }
* @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 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 the non-skipped target parameter types match the new types at {@code pos} + * or if {@code newTypes} does not contain the {@code target}'s non-skipped parameter types at position + * {@code pos}. * @since 9 */ public static @@ -3922,6 +3934,113 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); 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. + *

+ * 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: + *

{@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"));
+     * }
+ *

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. + *

{@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...);
+     * }
+     * }
+ *

+ * Note: 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(pos, targetType, combinerType); + 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; + } + + /** + * As {@see foldArguments(MethodHandle, int, MethodHandle)}, but with the + * added capability of selecting the arguments from the targets parameters + * to call the combiner with. This allows us to avoid some simple cases of + * permutations and padding the combiner with dropArguments to select the + * right argument, which may ultimately produce fewer intermediaries. + */ + static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner, int ... argPositions) { + MethodType targetType = target.type(); + MethodType combinerType = combiner.type(); + Class rtype = foldArgumentChecks(pos, targetType, combinerType, argPositions); + BoundMethodHandle result = target.rebind(); + boolean dropResult = rtype == void.class; + LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType(), argPositions); + MethodType newType = targetType; + if (!dropResult) { + newType = newType.dropParameterTypes(pos, pos + 1); + } + result = result.copyWithExtendL(newType, lform, combiner); + return result; + } + private static Class foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) { int foldArgs = combinerType.parameterCount(); Class rtype = combinerType.returnType(); @@ -4125,32 +4244,69 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); * iteration. Upon termination of the loop due to one of the predicates, a corresponding finalizer is run and * delivers the loop's result, which is the return value of the resulting handle. *

- * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration value and/or a loop + * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration variable and/or a loop * exit. Each iteration of the loop executes each clause in order. A clause can optionally update its iteration * variable; it can also optionally perform a test and conditional loop exit. In order to express this logic in - * terms of method handles, each clause will determine four actions: