mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8263087: Add a MethodHandle combinator that switches over a set of MethodHandles
Reviewed-by: redestad
This commit is contained in:
parent
85f616522b
commit
3623abb7f6
10 changed files with 974 additions and 26 deletions
|
@ -7751,4 +7751,90 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table switch method handle, which can be used to switch over a set of target
|
||||
* method handles, based on a given target index, called selector.
|
||||
* <p>
|
||||
* For a selector value of {@code n}, where {@code n} falls in the range {@code [0, N)},
|
||||
* and where {@code N} is the number of target method handles, the table switch method
|
||||
* handle will invoke the n-th target method handle from the list of target method handles.
|
||||
* <p>
|
||||
* For a selector value that does not fall in the range {@code [0, N)}, the table switch
|
||||
* method handle will invoke the given fallback method handle.
|
||||
* <p>
|
||||
* All method handles passed to this method must have the same type, with the additional
|
||||
* requirement that the leading parameter be of type {@code int}. The leading parameter
|
||||
* represents the selector.
|
||||
* <p>
|
||||
* Any trailing parameters present in the type will appear on the returned table switch
|
||||
* method handle as well. Any arguments assigned to these parameters will be forwarded,
|
||||
* together with the selector value, to the selected method handle when invoking it.
|
||||
*
|
||||
* @apiNote Example:
|
||||
* The cases each drop the {@code selector} value they are given, and take an additional
|
||||
* {@code String} argument, which is concatenated (using {@link String#concat(String)})
|
||||
* to a specific constant label string for each case:
|
||||
* <blockquote><pre>{@code
|
||||
* MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
* MethodHandle caseMh = lookup.findVirtual(String.class, "concat",
|
||||
* MethodType.methodType(String.class, String.class));
|
||||
* caseMh = MethodHandles.dropArguments(caseMh, 0, int.class);
|
||||
*
|
||||
* MethodHandle caseDefault = MethodHandles.insertArguments(caseMh, 1, "default: ");
|
||||
* MethodHandle case0 = MethodHandles.insertArguments(caseMh, 1, "case 0: ");
|
||||
* MethodHandle case1 = MethodHandles.insertArguments(caseMh, 1, "case 1: ");
|
||||
*
|
||||
* MethodHandle mhSwitch = MethodHandles.tableSwitch(
|
||||
* caseDefault,
|
||||
* case0,
|
||||
* case1
|
||||
* );
|
||||
*
|
||||
* assertEquals("default: data", (String) mhSwitch.invokeExact(-1, "data"));
|
||||
* assertEquals("case 0: data", (String) mhSwitch.invokeExact(0, "data"));
|
||||
* assertEquals("case 1: data", (String) mhSwitch.invokeExact(1, "data"));
|
||||
* assertEquals("default: data", (String) mhSwitch.invokeExact(2, "data"));
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* @param fallback the fallback method handle that is called when the selector is not
|
||||
* within the range {@code [0, N)}.
|
||||
* @param targets array of target method handles.
|
||||
* @return the table switch method handle.
|
||||
* @throws NullPointerException if {@code fallback}, the {@code targets} array, or any
|
||||
* any of the elements of the {@code targets} array are
|
||||
* {@code null}.
|
||||
* @throws IllegalArgumentException if the {@code targets} array is empty, if the leading
|
||||
* parameter of the fallback handle or any of the target
|
||||
* handles is not {@code int}, or if the types of
|
||||
* the fallback handle and all of target handles are
|
||||
* not the same.
|
||||
*/
|
||||
public static MethodHandle tableSwitch(MethodHandle fallback, MethodHandle... targets) {
|
||||
Objects.requireNonNull(fallback);
|
||||
Objects.requireNonNull(targets);
|
||||
targets = targets.clone();
|
||||
MethodType type = tableSwitchChecks(fallback, targets);
|
||||
return MethodHandleImpl.makeTableSwitch(type, fallback, targets);
|
||||
}
|
||||
|
||||
private static MethodType tableSwitchChecks(MethodHandle defaultCase, MethodHandle[] caseActions) {
|
||||
if (caseActions.length == 0)
|
||||
throw new IllegalArgumentException("Not enough cases: " + Arrays.toString(caseActions));
|
||||
|
||||
MethodType expectedType = defaultCase.type();
|
||||
|
||||
if (!(expectedType.parameterCount() >= 1) || expectedType.parameterType(0) != int.class)
|
||||
throw new IllegalArgumentException(
|
||||
"Case actions must have int as leading parameter: " + Arrays.toString(caseActions));
|
||||
|
||||
for (MethodHandle mh : caseActions) {
|
||||
Objects.requireNonNull(mh);
|
||||
if (mh.type() != expectedType)
|
||||
throw new IllegalArgumentException(
|
||||
"Case actions must have the same type: " + Arrays.toString(caseActions));
|
||||
}
|
||||
|
||||
return expectedType;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue