8185993: MethodHandle.invokeWithArguments jumbo-arity
authorjrose
Fri, 22 Sep 2017 15:28:16 -0700
changeset 47250 a0f26f0da4f1
parent 47249 82c43b3c0ea0
child 47251 4fe50ead4783
8185993: MethodHandle.invokeWithArguments jumbo-arity Reviewed-by: psandoz, vlivanov
src/java.base/share/classes/java/lang/invoke/CallSite.java
src/java.base/share/classes/java/lang/invoke/MethodHandle.java
src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
src/java.base/share/classes/java/lang/invoke/MethodType.java
src/java.base/share/classes/java/lang/invoke/package-info.java
test/jdk/java/lang/invoke/BigArityTest.java
--- 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];