8185993: MethodHandle.invokeWithArguments jumbo-arity
Reviewed-by: psandoz, vlivanov
--- a/src/java.base/share/classes/java/lang/invoke/CallSite.java Fri Sep 22 11:49:53 2017 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/CallSite.java Fri Sep 22 15:28:16 2017 -0700
@@ -344,13 +344,21 @@
break;
default:
final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type)
- if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY)
- throw new BootstrapMethodError("too many bootstrap method arguments");
-
- MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
- MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
- MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
- binding = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, (Object) type, argv);
+ final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
+ if (argv.length >= MAX_SAFE_SIZE) {
+ // to be on the safe side, use invokeWithArguments which handles jumbo lists
+ Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
+ newargv[0] = caller;
+ newargv[1] = name;
+ newargv[2] = type;
+ System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
+ binding = bootstrapMethod.invokeWithArguments(newargv);
+ } else {
+ MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
+ MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
+ MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
+ binding = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, (Object) type, argv);
+ }
}
}
if (binding instanceof CallSite) {
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Fri Sep 22 11:49:53 2017 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Fri Sep 22 15:28:16 2017 -0700
@@ -584,10 +584,10 @@
/*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args) throws Throwable;
/**
- * Performs a variable arity invocation, passing the arguments in the given list
+ * Performs a variable arity invocation, passing the arguments in the given array
* to the method handle, as if via an inexact {@link #invoke invoke} from a call site
- * which mentions only the type {@code Object}, and whose arity is the length
- * of the argument list.
+ * which mentions only the type {@code Object}, and whose actual argument count is the length
+ * of the argument array.
* <p>
* Specifically, execution proceeds as if by the following steps,
* although the methods are not guaranteed to be called if the JVM
@@ -595,36 +595,104 @@
* <ul>
* <li>Determine the length of the argument array as {@code N}.
* For a null reference, {@code N=0}. </li>
- * <li>Determine the general type {@code TN} of {@code N} arguments as
- * as {@code TN=MethodType.genericMethodType(N)}.</li>
+ * <li>Collect the {@code N} elements of the array as a logical
+ * argument list, each argument statically typed as an {@code Object}. </li>
+ * <li>Determine, as {@code M}, the parameter count of the type of this
+ * method handle. </li>
+ * <li>Determine the general type {@code TN} of {@code N} arguments or
+ * {@code M} arguments, if smaller than {@code N}, as
+ * {@code TN=MethodType.genericMethodType(Math.min(N, M))}.</li>
+ * <li>If {@code N} is greater than {@code M}, perform the following
+ * checks and actions to shorten the logical argument list: <ul>
+ * <li>Check that this method handle has variable arity with a
+ * {@linkplain MethodType#lastParameterType trailing parameter}
+ * of some array type {@code A[]}. If not, fail with a
+ * {@code WrongMethodTypeException}. </li>
+ * <li>Collect the trailing elements (there are {@code N-M+1} of them)
+ * from the logical argument list into a single array of
+ * type {@code A[]}, using {@code asType} conversions to
+ * convert each trailing argument to type {@code A}. </li>
+ * <li>If any of these conversions proves impossible, fail with either
+ * a {@code ClassCastException} if any trailing element cannot be
+ * cast to {@code A} or a {@code NullPointerException} if any
+ * trailing element is {@code null} and {@code A} is not a reference
+ * type. </li>
+ * <li>Replace the logical arguments gathered into the array of
+ * type {@code A[]} with the array itself, thus shortening
+ * the argument list to length {@code M}. This final argument
+ * retains the static type {@code A[]}.</li>
+ * <li>Adjust the type {@code TN} by changing the {@code N}th
+ * parameter type from {@code Object} to {@code A[]}.
+ * </ul>
* <li>Force the original target method handle {@code MH0} to the
* required type, as {@code MH1 = MH0.asType(TN)}. </li>
- * <li>Spread the array into {@code N} separate arguments {@code A0, ...}. </li>
+ * <li>Spread the argument list into {@code N} separate arguments {@code A0, ...}. </li>
* <li>Invoke the type-adjusted method handle on the unpacked arguments:
* MH1.invokeExact(A0, ...). </li>
* <li>Take the return value as an {@code Object} reference. </li>
* </ul>
* <p>
+ * If the target method handle has variable arity, and the argument list is longer
+ * than that arity, the excess arguments, starting at the position of the trailing
+ * array argument, will be gathered (if possible, as if by {@code asType} conversions)
+ * into an array of the appropriate type, and invocation will proceed on the
+ * shortened argument list.
+ * In this way, <em>jumbo argument lists</em> which would spread into more
+ * than 254 slots can still be processed uniformly.
+ * <p>
+ * Unlike the {@link #invoke(Object...) generic} invocation mode, which can
+ * "recycle" an array argument, passing it directly to the target method,
+ * this invocation mode <em>always</em> creates a new array parameter, even
+ * if the original array passed to {@code invokeWithArguments} would have
+ * been acceptable as a direct argument to the target method.
+ * Even if the number {@code M} of actual arguments is the arity {@code N},
+ * and the last argument is dynamically a suitable array of type {@code A[]},
+ * it will still be boxed into a new one-element array, since the call
+ * site statically types the argument as {@code Object}, not an array type.
+ * This is not a special rule for this method, but rather a regular effect
+ * of the {@linkplain #asVarargsCollector rules for variable-arity invocation}.
+ * <p>
* Because of the action of the {@code asType} step, the following argument
* conversions are applied as necessary:
* <ul>
* <li>reference casting
* <li>unboxing
* <li>widening primitive conversions
+ * <li>variable arity conversion
* </ul>
* <p>
* The result returned by the call is boxed if it is a primitive,
* or forced to null if the return type is void.
* <p>
- * This call is equivalent to the following code:
+ * Unlike the signature polymorphic methods {@code invokeExact} and {@code invoke},
+ * {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI.
+ * It can therefore be used as a bridge between native or reflective code and method handles.
+ * @apiNote
+ * This call is approximately equivalent to the following code:
* <blockquote><pre>{@code
+ * // for jumbo argument lists, adapt varargs explicitly:
+ * int N = (arguments == null? 0: arguments.length);
+ * int M = this.type.parameterCount();
+ * int MAX_SAFE = 127; // 127 longs require 254 slots, which is OK
+ * if (N > MAX_SAFE && N > M && this.isVarargsCollector()) {
+ * Class<?> arrayType = this.type().lastParameterType();
+ * Class<?> elemType = arrayType.getComponentType();
+ * if (elemType != null) {
+ * Object args2 = Array.newInstance(elemType, M);
+ * MethodHandle arraySetter = MethodHandles.arrayElementSetter(arrayType);
+ * for (int i = 0; i < M; i++) {
+ * arraySetter.invoke(args2, i, arguments[M-1 + i]);
+ * }
+ * arguments = Arrays.copyOf(arguments, M);
+ * arguments[M-1] = args2;
+ * return this.asFixedArity().invokeWithArguments(arguments);
+ * }
+ * } // done with explicit varargs processing
+ *
+ * // Handle fixed arity and non-jumbo variable arity invocation.
* MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0);
* Object result = invoker.invokeExact(this, arguments);
* }</pre></blockquote>
- * <p>
- * Unlike the signature polymorphic methods {@code invokeExact} and {@code invoke},
- * {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI.
- * It can therefore be used as a bridge between native or reflective code and method handles.
*
* @param arguments the arguments to pass to the target
* @return the result returned by the target
@@ -634,20 +702,24 @@
* @see MethodHandles#spreadInvoker
*/
public Object invokeWithArguments(Object... arguments) throws Throwable {
+ // Note: Jumbo argument lists are handled in the variable-arity subclass.
MethodType invocationType = MethodType.genericMethodType(arguments == null ? 0 : arguments.length);
return invocationType.invokers().spreadInvoker(0).invokeExact(asType(invocationType), arguments);
}
/**
- * Performs a variable arity invocation, passing the arguments in the given array
+ * Performs a variable arity invocation, passing the arguments in the given list
* to the method handle, as if via an inexact {@link #invoke invoke} from a call site
- * which mentions only the type {@code Object}, and whose arity is the length
- * of the argument array.
+ * which mentions only the type {@code Object}, and whose actual argument count is the length
+ * of the argument list.
* <p>
* This method is also equivalent to the following code:
* <blockquote><pre>{@code
* invokeWithArguments(arguments.toArray())
* }</pre></blockquote>
+ * <p>
+ * Jumbo-sized lists are acceptable if this method handle has variable arity.
+ * See {@link #invokeWithArguments(Object[])} for details.
*
* @param arguments the arguments to pass to the target
* @return the result returned by the target
@@ -987,6 +1059,16 @@
* disturbing its variable arity property:
* {@code mh.asType(mh.type().changeParameterType(0,int.class))
* .withVarargs(mh.isVarargsCollector())}
+ * <p>
+ * This call is approximately equivalent to the following code:
+ * <blockquote><pre>{@code
+ * if (makeVarargs == isVarargsCollector())
+ * return this;
+ * else if (makeVarargs)
+ * return asVarargsCollector(type().lastParameterType());
+ * else
+ * return return asFixedArity();
+ * }</pre></blockquote>
* @param makeVarargs true if the return method handle should have variable arity behavior
* @return a method handle of the same type, with possibly adjusted variable arity behavior
* @throws IllegalArgumentException if {@code makeVarargs} is true and
@@ -995,11 +1077,10 @@
* @see #asVarargsCollector
* @see #asFixedArity
*/
- public MethodHandle withVarargs(boolean makeVarargs) {
- if (!makeVarargs) {
- return asFixedArity();
- } else if (!isVarargsCollector()) {
- return asVarargsCollector(type().lastParameterType());
+ public MethodHandle withVarargs(boolean makeVarargs) {
+ assert(!isVarargsCollector()); // subclass responsibility
+ if (makeVarargs) {
+ return asVarargsCollector(type().lastParameterType());
} else {
return this;
}
@@ -1026,8 +1107,9 @@
* <p>
* (The array may also be a shared constant when {@code arrayLength} is zero.)
* <p>
- * (<em>Note:</em> The {@code arrayType} is often identical to the last
- * parameter type of the original target.
+ * (<em>Note:</em> The {@code arrayType} is often identical to the
+ * {@linkplain MethodType#lastParameterType last parameter type}
+ * of the original target.
* It is an explicit argument for symmetry with {@code asSpreader}, and also
* to allow the target to use a simple {@code Object} as its last parameter type.)
* <p>
@@ -1168,7 +1250,9 @@
* {@code invoke} and {@code asType} requests can lead to
* trailing positional arguments being collected into target's
* trailing parameter.
- * Also, the last parameter type of the adapter will be
+ * Also, the
+ * {@linkplain MethodType#lastParameterType last parameter type}
+ * of the adapter will be
* {@code arrayType}, even if the target has a different
* last parameter type.
* <p>
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Fri Sep 22 11:49:53 2017 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Fri Sep 22 15:28:16 2017 -0700
@@ -524,6 +524,12 @@
}
@Override
+ public MethodHandle withVarargs(boolean makeVarargs) {
+ if (makeVarargs) return this;
+ return asFixedArity();
+ }
+
+ @Override
public MethodHandle asTypeUncached(MethodType newType) {
MethodType type = this.type();
int collectArg = type.parameterCount() - 1;
@@ -561,6 +567,49 @@
: Arrays.asList(this, newType);
return true;
}
+
+ @Override
+ public Object invokeWithArguments(Object... arguments) throws Throwable {
+ MethodType type = this.type();
+ int argc;
+ final int MAX_SAFE = 127; // 127 longs require 254 slots, which is safe to spread
+ if (arguments == null
+ || (argc = arguments.length) <= MAX_SAFE
+ || argc < type.parameterCount()) {
+ return super.invokeWithArguments(arguments);
+ }
+
+ // a jumbo invocation requires more explicit reboxing of the trailing arguments
+ int uncollected = type.parameterCount() - 1;
+ Class<?> elemType = arrayType.getComponentType();
+ int collected = argc - uncollected;
+ Object collArgs = (elemType == Object.class)
+ ? new Object[collected] : Array.newInstance(elemType, collected);
+ if (!elemType.isPrimitive()) {
+ // simple cast: just do some casting
+ try {
+ System.arraycopy(arguments, uncollected, collArgs, 0, collected);
+ } catch (ArrayStoreException ex) {
+ return super.invokeWithArguments(arguments);
+ }
+ } else {
+ // corner case of flat array requires reflection (or specialized copy loop)
+ MethodHandle arraySetter = MethodHandles.arrayElementSetter(arrayType);
+ try {
+ for (int i = 0; i < collected; i++) {
+ arraySetter.invoke(collArgs, i, arguments[uncollected + i]);
+ }
+ } catch (WrongMethodTypeException|ClassCastException ex) {
+ return super.invokeWithArguments(arguments);
+ }
+ }
+
+ // chop the jumbo list down to size and call in non-varargs mode
+ Object[] newArgs = new Object[uncollected + 1];
+ System.arraycopy(arguments, 0, newArgs, 0, uncollected);
+ newArgs[uncollected] = collArgs;
+ return asFixedArity().invokeWithArguments(newArgs);
+ }
}
/** Factory method: Spread selected argument. */
--- a/src/java.base/share/classes/java/lang/invoke/MethodType.java Fri Sep 22 11:49:53 2017 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/MethodType.java Fri Sep 22 15:28:16 2017 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2017, 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
@@ -759,7 +759,23 @@
return Collections.unmodifiableList(Arrays.asList(ptypes.clone()));
}
- /*non-public*/ Class<?> lastParameterType() {
+ /**
+ * Returns the last parameter type of this method type.
+ * If this type has no parameters, the sentinel value
+ * {@code void.class} is returned instead.
+ * @apiNote
+ * <p>
+ * The sentinel value is chosen so that reflective queries can be
+ * made directly against the result value.
+ * The sentinel value cannot be confused with a real parameter,
+ * since {@code void} is never acceptable as a parameter type.
+ * For variable arity invocation modes, the expression
+ * {@link Class#getComponentType lastParameterType().getComponentType()}
+ * is useful to query the type of the "varargs" parameter.
+ * @return the last parameter type if any, else {@code void.class}
+ * @since 10
+ */
+ public Class<?> lastParameterType() {
int len = ptypes.length;
return len == 0 ? void.class : ptypes[len-1];
}
--- a/src/java.base/share/classes/java/lang/invoke/package-info.java Fri Sep 22 11:49:53 2017 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/package-info.java Fri Sep 22 15:28:16 2017 -0700
@@ -81,12 +81,19 @@
* in which dynamic call site occurs </li>
* <li>a {@code String}, the method name mentioned in the call site </li>
* <li>a {@code MethodType}, the resolved type descriptor of the call </li>
- * <li>optionally, between 1 and 251 additional static arguments taken from the constant pool </li>
+ * <li>optionally, any number of additional static arguments taken from the constant pool </li>
* </ul>
- * Invocation is as if by
- * {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}.
- * The returned result must be a {@link java.lang.invoke.CallSite CallSite}
- * (or a subclass), otherwise a
+ * <p>
+ * In all cases, bootstrap method invocation is as if by
+ * {@link java.lang.invoke.MethodHandle#invokeWithArguments MethodHandle.invokeWithArguments},
+ * (This is also equivalent to
+ * {@linkplain java.lang.invoke.MethodHandle#invoke generic invocation}
+ * if the number of arguments is small enough.)
+ * <p>
+ * For an {@code invokedynamic} instruction, the
+ * returned result must be convertible to a non-null reference to a
+ * {@link java.lang.invoke.CallSite CallSite}.
+ * If the returned result cannot be converted to the expected type,
* {@link java.lang.BootstrapMethodError BootstrapMethodError} is thrown.
* The type of the call site's target must be exactly equal to the type
* derived from the dynamic call site's type descriptor and passed to
@@ -150,10 +157,12 @@
* If the {@code invokedynamic} instruction specifies one or more static arguments,
* those values will be passed as additional arguments to the method handle.
* (Note that because there is a limit of 255 arguments to any method,
- * at most 251 extra arguments can be supplied, since the bootstrap method
+ * at most 251 extra arguments can be supplied to a non-varargs bootstrap method,
+ * since the bootstrap method
* handle itself and its first three arguments must also be stacked.)
- * The bootstrap method will be invoked as if by either {@code MethodHandle.invoke}
- * or {@code invokeWithArguments}. (There is no way to tell the difference.)
+ * The bootstrap method will be invoked as if by {@code MethodHandle.invokeWithArguments}.
+ * A variable-arity bootstrap method can accept thousands of static arguments,
+ * subject only by limits imposed by the class-file format.
* <p>
* The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments.
* For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
@@ -194,9 +203,9 @@
* </tbody>
* </table>
* The last example assumes that the extra arguments are of type
- * {@code CONSTANT_String} and {@code CONSTANT_Integer}, respectively.
+ * {@code String} and {@code Integer} (or {@code int}), respectively.
* The second-to-last example assumes that all extra arguments are of type
- * {@code CONSTANT_String}.
+ * {@code String}.
* The other examples work with all types of extra arguments.
* <p>
* As noted above, the actual method type of the bootstrap method can vary.
@@ -220,7 +229,7 @@
* to safely and compactly encode metadata.
* In principle, the name and extra arguments are redundant,
* since each call site could be given its own unique bootstrap method.
- * Such a practice is likely to produce large class files and constant pools.
+ * Such a practice would be likely to produce large class files and constant pools.
*
* @author John Rose, JSR 292 EG
* @since 1.7
--- a/test/jdk/java/lang/invoke/BigArityTest.java Fri Sep 22 11:49:53 2017 -0700
+++ b/test/jdk/java/lang/invoke/BigArityTest.java Fri Sep 22 15:28:16 2017 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -22,13 +22,15 @@
*/
/* @test
- * @summary High arity invocations, up to the maximum of 255 arguments
+ * @summary High arity invocations
* @compile BigArityTest.java
* @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -esa -DBigArityTest.ITERATION_COUNT=1 test.java.lang.invoke.BigArityTest
*/
package test.java.lang.invoke;
+import org.junit.Test;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -37,8 +39,8 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
+
import static org.junit.Assert.assertEquals;
-import org.junit.Test;
public class BigArityTest {
@@ -63,12 +65,70 @@
static Object hashArguments(Object... args) {
return Objects.hash(args);
}
+ static Object hashArguments(int... args) {
+ Object[] wrappedArgs = new Object[args.length];
+ for (int i = 0; i < args.length; i++) {
+ wrappedArgs[i] = args[i];
+ }
+ return hashArguments(wrappedArgs);
+ }
+ static Object hashArguments(long... args) {
+ Object[] wrappedArgs = new Object[args.length];
+ for (int i = 0; i < args.length; i++) {
+ wrappedArgs[i] = (int) args[i];
+ }
+ return hashArguments(wrappedArgs);
+ }
+
+ static Object hashArguments1(Object o, Object... args) {
+ Object[] arr = new Object[args.length + 1];
+ arr[0] = 0;
+ System.arraycopy(args, 0, arr, 1, args.length);
+ return Objects.hash(arr);
+ }
+ static Object hashArguments1(int i0, int... args) {
+ Object[] wrappedArgs = new Object[args.length + 1];
+ wrappedArgs[0] = i0;
+ for (int i = 0; i < args.length; i++) {
+ wrappedArgs[i + 1] = args[i];
+ }
+ return hashArguments(wrappedArgs);
+ }
+ static Object hashArguments1(long l0, long... args) {
+ Object[] wrappedArgs = new Object[args.length + 1];
+ wrappedArgs[0] = l0;
+ for (int i = 0; i < args.length; i++) {
+ wrappedArgs[i + 1] = (int) args[i];
+ }
+ return hashArguments(wrappedArgs);
+ }
+
static final MethodHandle MH_hashArguments_VA;
+ static final MethodHandle MH_hashArguments_IVA;
+ static final MethodHandle MH_hashArguments_JVA;
+ static final MethodHandle MH_hashArguments1_VA;
+ static final MethodHandle MH_hashArguments1_IVA;
+ static final MethodHandle MH_hashArguments1_JVA;
static {
try {
MH_hashArguments_VA =
MethodHandles.lookup().unreflect(
BigArityTest.class.getDeclaredMethod("hashArguments", Object[].class));
+ MH_hashArguments_IVA =
+ MethodHandles.lookup().unreflect(
+ BigArityTest.class.getDeclaredMethod("hashArguments", int[].class));
+ MH_hashArguments_JVA =
+ MethodHandles.lookup().unreflect(
+ BigArityTest.class.getDeclaredMethod("hashArguments", long[].class));
+ MH_hashArguments1_VA =
+ MethodHandles.lookup().unreflect(
+ BigArityTest.class.getDeclaredMethod("hashArguments1", Object.class, Object[].class));
+ MH_hashArguments1_IVA =
+ MethodHandles.lookup().unreflect(
+ BigArityTest.class.getDeclaredMethod("hashArguments1", int.class, int[].class));
+ MH_hashArguments1_JVA =
+ MethodHandles.lookup().unreflect(
+ BigArityTest.class.getDeclaredMethod("hashArguments1", long.class, long[].class));
} catch (ReflectiveOperationException ex) {
throw new Error(ex);
}
@@ -345,6 +405,28 @@
assertEquals("arity=MAX_ARITY", r0, r);
}
+ @Test
+ public void testInvokeWithArgumentsJumbo() throws Throwable {
+ System.out.println("testing invokeWithArguments on jumbo arities");
+ ArrayList<Integer> arities = new ArrayList<>();
+ for (int arity = 125; arity < 1000; arity += (arity < MAX_ARITY+10 ? 1 : arity/7)) {
+ arities.add(arity);
+ Object[] args = testArgs(arity);
+ Object r0 = Objects.hash(args);
+
+ assertEquals("jumbo arity="+arity, r0, MH_hashArguments_VA.invokeWithArguments(args));
+ assertEquals("jumbo arity="+arity, r0, MH_hashArguments1_VA.invokeWithArguments(args));
+
+ // use primitive typed argument lists also:
+ assertEquals("jumbo int arity="+arity, r0, MH_hashArguments_IVA.invokeWithArguments(args));
+ assertEquals("jumbo int arity="+arity, r0, MH_hashArguments1_IVA.invokeWithArguments(args));
+
+ assertEquals("jumbo long arity="+arity, r0, MH_hashArguments_JVA.invokeWithArguments(args));
+ assertEquals("jumbo long arity="+arity, r0, MH_hashArguments1_JVA.invokeWithArguments(args));
+ }
+ System.out.println(" jumbo arities = "+arities);
+ }
+
static Object[] cat(Object a, Object[] b) {
int alen = 1, blen = b.length;
Object[] c = new Object[alen + blen];