changeset 51540:72c3a4b94fd3 condy-folding

Invoke dynamic constant bootstrap methods that are ordinary static methods (If zero parameters or first parameter type is not assignable to Lookup)
author psandoz
date Mon, 09 Jul 2018 12:55:55 -0700
parents 952990c8f3c2
children c661b8a1b310
files src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java src/java.base/share/classes/java/lang/invoke/CallSite.java src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java src/java.base/share/classes/java/lang/invoke/package-info.java test/jdk/java/lang/invoke/condy/BootstrapMethodJumboArgsTest.java test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java
diffstat 7 files changed, 495 insertions(+), 178 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java	Wed Jun 27 12:59:47 2018 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java	Mon Jul 09 12:55:55 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -29,6 +29,8 @@
 import java.lang.invoke.AbstractConstantGroup.BSCIWithCache;
 import java.util.Arrays;
 
+import static java.lang.invoke.BootstrapCallInfo.makeBootstrapCallInfo;
+import static java.lang.invoke.ConstantGroup.makeConstantGroup;
 import static java.lang.invoke.MethodHandleNatives.*;
 import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
 import static java.lang.invoke.MethodHandles.Lookup;
@@ -45,6 +47,8 @@
      * @param type the method type or constant type
      * @param info information passed up from the JVM, to derive static arguments
      * @param callerClass the class containing the resolved method call or constant load
+     * @param includeMetadata true if the lookup, name and type metadata arguments should
+     *                        be included when invoking the BSM
      * @param <T> the expected return type
      * @return the expected value, either a CallSite or a constant value
      */
@@ -55,24 +59,30 @@
                         // Extra arguments for BSM, if any:
                         Object info,
                         // Caller information:
-                        Class<?> callerClass) {
+                        Class<?> callerClass,
+                        boolean includeMetadata) {
         MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
+        MethodType bsmType = bootstrapMethod.type();
         Object result;
         boolean pullMode = isPullModeBSM(bootstrapMethod);  // default value is false
         boolean vmIsPushing = !staticArgumentsPulled(info); // default value is true
         MethodHandle pullModeBSM;
+
         // match the VM with the BSM
         if (vmIsPushing) {
             // VM is pushing arguments at us
+            // Need to transform if pull-mode BSM
             pullModeBSM = null;
             if (pullMode) {
-                bootstrapMethod = pushMePullYou(bootstrapMethod, true);
+                bootstrapMethod = PushAdapter.toPullBootstrapMethod(bootstrapMethod);
+                bsmType = bootstrapMethod.type();
             }
-        } else {
+        }
+        else {
             // VM wants us to pull args from it
+            // Need to transform if push-mode BSM
             pullModeBSM = pullMode ? bootstrapMethod :
-                    pushMePullYou(bootstrapMethod, false);
-            bootstrapMethod = null;
+                    PullAdapter.toPushBootstrapMethod(bootstrapMethod, includeMetadata);
         }
         try {
             // As an optimization we special case various known BSMs,
@@ -84,21 +94,30 @@
             // checking.
             info = maybeReBox(info);
             if (info == null) {
-                // VM is allowed to pass up a null meaning no BSM args
-                result = invoke(bootstrapMethod, caller, name, type);
+                if (includeMetadata) {
+                    // VM is allowed to pass up a null meaning no BSM args
+                    result = invoke(bootstrapMethod, caller, name, type);
+                }
+                else {
+                    result = bootstrapMethod.invoke();
+                }
             }
             else if (!info.getClass().isArray()) {
                 // VM is allowed to pass up a single BSM arg directly
 
                 // Call to StringConcatFactory::makeConcatWithConstants
                 // with empty constant arguments?
-                if (isStringConcatFactoryBSM(bootstrapMethod.type())) {
+                if (isStringConcatFactoryBSM(bsmType)) {
                     result = (CallSite)bootstrapMethod
                             .invokeExact(caller, name, (MethodType)type,
                                          (String)info, new Object[0]);
-                } else {
+                }
+                else if (includeMetadata) {
                     result = invoke(bootstrapMethod, caller, name, type, info);
                 }
+                else {
+                    result = bootstrapMethod.invoke(info);
+                }
             }
             else if (info.getClass() == int[].class) {
                 // VM is allowed to pass up a pair {argc, index}
@@ -111,30 +130,39 @@
                 // The code in this method makes up for any mismatches.
                 BootstrapCallInfo<Object> bsci
                     = new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info);
-                // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
-                result = pullModeBSM.invoke(caller, bsci);
+                if (includeMetadata) {
+                    // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
+                    result = pullModeBSM.invoke(caller, bsci);
+                }
+                else {
+                    // Pull-mode API is (BootstrapCallInfo) -> Object
+                    result = pullModeBSM.invoke(bsci);
+                }
             }
             else {
                 // VM is allowed to pass up a full array of resolved BSM args
                 Object[] argv = (Object[]) info;
                 maybeReBoxElements(argv);
 
-                MethodType bsmType = bootstrapMethod.type();
                 if (isLambdaMetafactoryIndyBSM(bsmType) && argv.length == 3) {
                     result = (CallSite)bootstrapMethod
                             .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
                                     (MethodHandle)argv[1], (MethodType)argv[2]);
-                } else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) {
+                }
+                else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) {
                     result = bootstrapMethod
                             .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
                                     (MethodHandle)argv[1], (MethodType)argv[2]);
-                } else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) {
+                }
+                else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) {
                     String recipe = (String)argv[0];
                     Object[] shiftedArgs = Arrays.copyOfRange(argv, 1, argv.length);
                     result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, recipe, shiftedArgs);
-                } else if (isLambdaMetafactoryAltMetafactoryBSM(bsmType)) {
+                }
+                else if (isLambdaMetafactoryAltMetafactoryBSM(bsmType)) {
                     result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, argv);
-                } else {
+                }
+                else if (includeMetadata) {
                     switch (argv.length) {
                         case 0:
                             result = invoke(bootstrapMethod, caller, name, type);
@@ -167,6 +195,39 @@
                             result = invokeWithManyArguments(bootstrapMethod, caller, name, type, argv);
                     }
                 }
+                else {
+                    switch (argv.length) {
+                        case 0:
+                            result = bootstrapMethod.invoke();
+                            break;
+                        case 1:
+                            result = bootstrapMethod.invoke(
+                                            argv[0]);
+                            break;
+                        case 2:
+                            result = bootstrapMethod.invoke(
+                                            argv[0], argv[1]);
+                            break;
+                        case 3:
+                            result = bootstrapMethod.invoke(
+                                            argv[0], argv[1], argv[2]);
+                            break;
+                        case 4:
+                            result = bootstrapMethod.invoke(
+                                            argv[0], argv[1], argv[2], argv[3]);
+                            break;
+                        case 5:
+                            result = bootstrapMethod.invoke(
+                                            argv[0], argv[1], argv[2], argv[3], argv[4]);
+                            break;
+                        case 6:
+                            result = bootstrapMethod.invoke(
+                                            argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
+                            break;
+                        default:
+                            result = invokeWithManyArguments(bootstrapMethod, argv);
+                    }
+                }
             }
             if (resultType.isPrimitive()) {
                 // Non-reference conversions are more than just plain casts.
@@ -200,7 +261,8 @@
                                  String name, Object type) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type);
         }
     }
@@ -209,7 +271,8 @@
                                  String name, Object type, Object arg0) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0);
         }
     }
@@ -218,7 +281,8 @@
                                  Object type, Object arg0, Object arg1) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1);
         }
     }
@@ -228,7 +292,8 @@
                                  Object arg2) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2);
         }
     }
@@ -238,7 +303,8 @@
                                  Object arg2, Object arg3) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3);
         }
     }
@@ -248,7 +314,8 @@
                                  Object arg2, Object arg3, Object arg4) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4);
         }
     }
@@ -275,7 +342,8 @@
             newargv[2] = type;
             System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
             return bootstrapMethod.invokeWithArguments(newargv);
-        } else {
+        }
+        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);
@@ -283,6 +351,20 @@
         }
     }
 
+    private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Object[] argv) throws Throwable {
+        final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2;
+        if (argv.length >= MAX_SAFE_SIZE) {
+            // to be on the safe side, use invokeWithArguments which handles jumbo lists
+            return bootstrapMethod.invokeWithArguments(argv);
+        }
+        else {
+            MethodType invocationType = MethodType.genericMethodType(argv.length);
+            MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
+            MethodHandle spreader = invocationType.invokers().spreadInvoker(0);
+            return spreader.invokeExact(typedBSM, argv);
+        }
+    }
+
     private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
             Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
 
@@ -372,7 +454,7 @@
 
         @Override Object fillCache(int i) {
             Object[] buf = { null };
-            copyArguments(i, i + 1, buf, 0);
+            copyArguments(i, i+1, buf, 0);
             Object res = wrapNull(buf[0]);
             cache[i] = res;
             int next = i + 1;
@@ -461,52 +543,157 @@
     /*non-public*/ static final
     class PushAdapter {
         static final MethodHandle MH_pushToBootstrapMethod;
-
         static {
             try {
                 MH_pushToBootstrapMethod = IMPL_LOOKUP
-                    .findStatic(BootstrapCallInfo.class, "invokeFromArgumentsToCallInfo",
+                        .findStatic(BootstrapCallInfo.class, "invokeFromArgumentsToCallInfo",
                                 MethodType.methodType(Object.class, MethodHandle.class,
                                         Lookup.class, String.class, Object.class, Object[].class));
-            } catch (Throwable ex) {
+            }
+            catch (Throwable ex) {
                 throw new InternalError(ex);
             }
         }
+
+        /**
+         * Given a push-mode BSM (taking one BSCI argument) convert it to a
+         * pull-mode BSM (taking N pre-resolved arguments).  This method is used
+         * when the JVM is passing up pre-resolved arguments, but the BSM is
+         * expecting to BSCI to resolve arguments lazily.
+         */
+        static MethodHandle toPullBootstrapMethod(MethodHandle bsm) {
+            if (TRACE_METHOD_LINKAGE) {
+                System.out.println("converting a push-mode BSM of type " + bsm.type() + " to pull-mode");
+            }
+            return MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
+        }
     }
 
     /*non-public*/ static final
     class PullAdapter {
+        // skeleton for pull-mode BSM which wraps a push-mode BSM:
+        static Object pullFromBootstrapMethod(MethodHandle pushModeBSM,
+                                              MethodHandles.Lookup lookup,
+                                              BootstrapCallInfo<?> bsci)
+                throws Throwable {
+            int argc = bsci.size();
+            switch (argc) {
+                case 0:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType());
+                case 1:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0));
+                case 2:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0), bsci.get(1));
+                case 3:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0), bsci.get(1), bsci.get(2));
+                case 4:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3));
+                case 5:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4));
+                case 6:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5));
+                default:
+                    final int NON_SPREAD_ARG_COUNT = 3;  // (lookup, name, type)
+                    final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
+                    if (argc >= MAX_SAFE_SIZE) {
+                        // to be on the safe side, use invokeWithArguments which handles jumbo lists
+                        Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argc];
+                        newargv[0] = lookup;
+                        newargv[1] = bsci.invocationName();
+                        newargv[2] = bsci.invocationType();
+                        bsci.copyConstants(0, argc, newargv, NON_SPREAD_ARG_COUNT);
+                        return pushModeBSM.invokeWithArguments(newargv);
+                    }
+                    MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argc);
+                    MethodHandle typedBSM = pushModeBSM.asType(invocationType);
+                    MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
+                    Object[] argv = new Object[argc];
+                    bsci.copyConstants(0, argc, argv, 0);
+                    return spreader.invokeExact(typedBSM, (Object) lookup, (Object) bsci.invocationName(), bsci.invocationType(), argv);
+                }
+        }
+
+        static Object pullFromBootstrapMethodExclude(MethodHandle pushModeBSM,
+                                                     BootstrapCallInfo<?> bsci)
+                throws Throwable {
+            int argc = bsci.size();
+            switch (argc) {
+                case 0:
+                    return pushModeBSM.invoke();
+                case 1:
+                    return pushModeBSM.invoke(bsci.get(0));
+                case 2:
+                    return pushModeBSM.invoke(bsci.get(0), bsci.get(1));
+                case 3:
+                    return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2));
+                case 4:
+                    return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3));
+                case 5:
+                    return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4));
+                case 6:
+                    return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5));
+                default:
+                    final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2;
+                    if (argc >= MAX_SAFE_SIZE) {
+                        // to be on the safe side, use invokeWithArguments which handles jumbo lists
+                        Object[] argv = new Object[argc];
+                        bsci.copyConstants(0, argc, argv, 0);
+                        return pushModeBSM.invokeWithArguments(argv);
+                    }
+                    MethodType invocationType = MethodType.genericMethodType(argc);
+                    MethodHandle typedBSM = pushModeBSM.asType(invocationType);
+                    MethodHandle spreader = invocationType.invokers().spreadInvoker(0);
+                    Object[] argv = new Object[argc];
+                    bsci.copyConstants(0, argc, argv, 0);
+                    return spreader.invokeExact(typedBSM, argv);
+            }
+        }
+
         static final MethodHandle MH_pullFromBootstrapMethod;
 
+        static final MethodHandle MH_pullFromBootstrapMethodExclude;
+
         static {
+            final Class<?> THIS_CLASS = PullAdapter.class;
             try {
                 MH_pullFromBootstrapMethod = IMPL_LOOKUP
-                    .findStatic(BootstrapCallInfo.class, "invokeFromCallInfoToArguments",
+                    .findStatic(THIS_CLASS, "pullFromBootstrapMethod",
                                 MethodType.methodType(Object.class, MethodHandle.class,
-                                                      Lookup.class, BootstrapCallInfo.class));
-            } catch (Throwable ex) {
+                                        Lookup.class, BootstrapCallInfo.class));
+
+                MH_pullFromBootstrapMethodExclude = IMPL_LOOKUP
+                        .findStatic(THIS_CLASS, "pullFromBootstrapMethodExclude",
+                                    MethodType.methodType(Object.class, MethodHandle.class,
+                                                          BootstrapCallInfo.class));
+            }
+            catch (Throwable ex) {
                 throw new InternalError(ex);
             }
         }
-    }
 
-    /** Given a push-mode BSM (taking one argument) convert it to a
-     *  pull-mode BSM (taking N pre-resolved arguments).
-     *  This method is used when, in fact, the JVM is passing up
-     *  pre-resolved arguments, but the BSM is expecting lazy stuff.
-     *  Or, when goToPushMode is true, do the reverse transform.
-     *  (The two transforms are exactly inverse.)
-     */
-    static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) {
-        if (TRACE_METHOD_LINKAGE) {
-            System.out.println("converting BSM of type " + bsm.type() + " to "
-                    + (goToPushMode ? "push mode" : "pull mode"));
-        }
-        assert(isPullModeBSM(bsm) == goToPushMode); // there must be a change
-        if (goToPushMode) {
-            return PushAdapter.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
-        } else {
-            return PullAdapter.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
+        /**
+         * Given a pull-mode BSM (taking N pre-resolved arguments) convert it to
+         * a push-mode BSM (taking one BSCI argument).  This method is used when
+         * the JVM is passing up indexes to constant pool entries (possibly
+         * unresolved), but the BSM is expecting pre-resolved arguments.
+         */
+        static MethodHandle toPushBootstrapMethod(MethodHandle bsm, boolean includeMetaData) {
+            if (TRACE_METHOD_LINKAGE) {
+                System.out.println("converting a pull-mode BSM of type " + bsm.type() + " to push-mode"
+                                   + (!includeMetaData ? ", exluding meta-data" : ""));
+            }
+            if (includeMetaData) {
+                return PullAdapter.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
+            }
+            else {
+                return PullAdapter.MH_pullFromBootstrapMethodExclude.bindTo(bsm).withVarargs(false);
+            }
         }
     }
 }
--- a/src/java.base/share/classes/java/lang/invoke/CallSite.java	Wed Jun 27 12:59:47 2018 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/CallSite.java	Mon Jul 09 12:55:55 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2018, 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
@@ -305,7 +305,8 @@
         CallSite site;
         try {
             Object binding = BootstrapMethodInvoker.invoke(
-                    CallSite.class, bootstrapMethod, name, type, info, callerClass);
+                    CallSite.class, bootstrapMethod, name, type, info, callerClass,
+                    true);
             if (binding instanceof CallSite) {
                 site = (CallSite) binding;
             } else {
--- a/src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java	Wed Jun 27 12:59:47 2018 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java	Mon Jul 09 12:55:55 2018 -0700
@@ -58,23 +58,19 @@
                                               Object info,
                                               // Caller information:
                                               Class<?> callerClass) {
-        // Restrict bootstrap methods to those whose first parameter is Lookup
-        // The motivation here is, in the future, to possibly support BSMs
-        // that do not accept the meta-data of lookup/name/type, thereby
-        // allowing the co-opting of existing methods to be used as BSMs as
-        // long as the static arguments can be passed as method arguments
-        MethodType mt = bootstrapMethod.type();
-        if (mt.parameterCount() < 2 ||
-            !MethodHandles.Lookup.class.isAssignableFrom(mt.parameterType(0))) {
-            throw new BootstrapMethodError(
-                    "Invalid bootstrap method declared for resolving a dynamic constant: " + bootstrapMethod);
-        }
+        // Lookup, name and type argument metadata should be included when
+        // invoking the BSM if there are one or more parameters and the first
+        // parameter type is MethodHandles.Lookup
+        MethodType bsmType = bootstrapMethod.type();
+        boolean includeMetadata = (bsmType.parameterCount() > 0 &&
+                                   MethodHandles.Lookup.class.isAssignableFrom(bsmType.parameterType(0)));
 
         // BSMI.invoke handles all type checking and exception translation.
         // If type is not a reference type, the JVM is expecting a boxed
         // version, and will manage unboxing on the other side.
         return BootstrapMethodInvoker.invoke(
-                type, bootstrapMethod, name, type, info, callerClass);
+                type, bootstrapMethod, name, type, info, callerClass,
+                includeMetadata);
     }
 
     /**
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java	Wed Jun 27 12:59:47 2018 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java	Mon Jul 09 12:55:55 2018 -0700
@@ -365,7 +365,8 @@
      * replaced with currently commented out code.
      */
     static boolean isPullModeBSM(MethodHandle bsm) {
-        return bsm.type().parameterCount() == 2 && !bsm.isVarargsCollector();
+        return bsm.type().parameterCount() == 2 && !bsm.isVarargsCollector()
+               && BootstrapCallInfo.class.isAssignableFrom(bsm.type().parameterType(1));
     }
 
     /**
--- a/src/java.base/share/classes/java/lang/invoke/package-info.java	Wed Jun 27 12:59:47 2018 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/package-info.java	Mon Jul 09 12:55:55 2018 -0700
@@ -96,23 +96,28 @@
  * following items:
  * <ul>
  * <li>the bootstrap method, a {@code CONSTANT_MethodHandle}</li>
- * <li>the {@code Class} or {@code MethodType} derived from
+ * <li>the {@code MethodType} or {@code Class} derived from
  * type component of the {@code CONSTANT_NameAndType} descriptor</li>
  * <li>static arguments, if any (note that static arguments can themselves be
  * dynamically-computed constants)</li>
  * </ul>
  * <p>
- * The bootstrap method is then invoked, as if by
+ * The bootstrap method is then invoked, in general, as if by
  * {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke},
  * with the following arguments:
  * <ul>
  * <li>a {@code MethodHandles.Lookup}, which is a lookup object on the <em>caller class</em>
  * in which dynamically-computed constant or call site occurs</li>
  * <li>a {@code String}, the name mentioned in the {@code CONSTANT_NameAndType}</li>
- * <li>a {@code MethodType} or {@code Class}, the resolved type descriptor of the {@code CONSTANT_NameAndType}</li>
- * <li>a {@code Class}, the resolved type descriptor of the constant, if it is a dynamic constant </li>
+ * <li>for a dynamically-computed call site a {@code MethodType} (the resolved
+ * type descriptor of the call site), or for a dynamically computed constant,
+ * a {@code Class} (the resolved type descriptor of the constant)</li>
  * <li>the additional resolved static arguments, if any</li>
  * </ul>
+ * For dynamically computed constant, if the bootstrap method has no parameters
+ * or the first parameter type is not assignable to {@code MethodHandles.Lookup}
+ * then the method is invoked as described above but with just the arguments
+ * that are the additional resolved static arguments, if any.
  * <p>
  * For a dynamically-computed call site, the returned result must be a non-null reference to a
  * {@link java.lang.invoke.CallSite CallSite}.
@@ -122,11 +127,11 @@
  * On success the call site then becomes permanently linked to the {@code invokedynamic}
  * instruction.
  * <p>
- * For a dynamically-computed constant, the first parameter of the bootstrap
- * method must be assignable to {@code MethodHandles.Lookup}. If this condition
- * is not met, a {@code BootstrapMethodError} is thrown.
- * On success the result of the bootstrap method is cached as the resolved
- * constant value.
+ * For a dynamically-computed constant, the result of the bootstrap method is
+ * cached as the resolved constant value.  The constant value must be
+ * convertible to the {@code Class} derived from type component of the
+ * {@code CONSTANT_NameAndType} descriptor, otherwise a
+ * {@code BootstrapMethodError} is thrown.
  * <p>
  * If an exception, {@code E} say, occurs during execution of the bootstrap method, then
  * resolution fails and terminates abnormally. {@code E} is rethrown if the type of
@@ -176,21 +181,18 @@
  * types {@code MethodHandles.Lookup}, {@code String}, {@code MethodType}, and the types
  * of any static arguments; the return type is {@code CallSite}.
  * <p>
- * For a dynamically-computed constant, the bootstrap method is invoked with parameter types
- * {@code MethodHandles.Lookup}, {@code String}, {@code Class}, and the types of any
- * static arguments; the return type is the type represented by the {@code Class}.
+ * For a dynamically-computed constant, if the first parameter of the bootstrap
+ * method is assignable to {@code MethodHandles.Lookup} then the bootstrap
+ * method is invoked with parameter types {@code MethodHandles.Lookup},
+ * {@code String}, {@code Class}, and the types of any static arguments.
+ * Otherwise, the bootstrap method is invoked with just the parameter types of
+ * any static arguments.  In either case the return type is the type represented
+ * by the {@code Class} (regardless of whether the bootstrap is invoked with
+ * just the static arguments).
  * <p>
  * Because {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke} allows for
  * adaptations between the invoked method type and the bootstrap method handle's method type,
  * there is flexibility in the declaration of the bootstrap method.
- * For a dynamically-computed constant the first parameter type of the bootstrap method handle
- * must be assignable to {@code MethodHandles.Lookup}, other than that constraint the same degree
- * of flexibility applies to bootstrap methods of dynamically-computed call sites and
- * dynamically-computed constants.
- * Note: this constraint allows for the future possibility where the bootstrap method is
- * invoked with just the parameter types of static arguments, thereby supporting a wider
- * range of methods compatible with the static arguments (such as methods that don't declare
- * or require the lookup, name, and type meta-data parameters).
  * <p> For example, for dynamically-computed call site, a the first argument
  * could be {@code Object} instead of {@code MethodHandles.Lookup}, and the return type
  * could also be {@code Object} instead of {@code CallSite}.
@@ -205,13 +207,15 @@
  * between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods,
  * and the {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector} transformation.)
  * <p>
- * Given these rules, here are examples of legal bootstrap method declarations for
+ * Given these rules the following examples and explanations are presented for
+ * legal bootstrap method declarations.
+ * Here are examples of legal bootstrap method declarations for
  * dynamically-computed call sites, given various numbers {@code N} of extra arguments.
  * The first row (marked {@code *}) will work for any number of extra arguments.
  * <table class="plain" style="vertical-align:top">
  * <caption style="display:none">Static argument types</caption>
  * <thead>
- * <tr><th scope="col">N</th><th scope="col">Sample bootstrap method</th></tr>
+ * <tr><th scope="col">N</th><th scope="col">Sample bootstrap method for a dynamically-computed call site</th></tr>
  * </thead>
  * <tbody>
  * <tr><th scope="row" style="font-weight:normal; vertical-align:top">*</th><td>
@@ -239,32 +243,73 @@
  * {@code String} and {@code Integer} (or {@code int}), respectively.
  * The second-to-last example assumes that all extra arguments are of type
  * {@code String}.
- * The other examples work with all types of extra arguments.  Note that all
- * the examples except the second and third also work with dynamically-computed
- * constants if the return type is changed to be compatible with the
- * constant's declared type (such as {@code Object}, which is always compatible).
+ * The other examples work with all types of extra arguments.
  * <p>
- * Since dynamically-computed constants can be provided as static arguments to bootstrap
- * methods, there are no limitations on the types of bootstrap arguments.
- * However, arguments of type {@code boolean}, {@code byte}, {@code short}, or {@code char}
- * cannot be <em>directly</em> supplied by {@code CONSTANT_Integer}
- * constant pool entries, since the {@code asType} conversions do
- * not perform the necessary narrowing primitive conversions.
+ * Since dynamically-computed constants can be provided as static arguments to
+ * bootstrap methods for both dynamically-computed call sites and
+ * dynamically-computed constants, there are no limitations on the types of
+ * bootstrap arguments.  However, arguments of type {@code boolean},
+ * {@code byte}, {@code short}, or {@code char} cannot be <em>directly</em>
+ * supplied by {@code CONSTANT_Integer} constant pool entries, since the
+ * {@code asType} conversions do not perform the necessary narrowing primitive
+ * conversions.
  * <p>
- * In the above examples, the return type is always {@code CallSite},
- * but that is not a necessary feature of bootstrap methods.
- * In the case of a dynamically-computed call site, the only requirement is that
- * the return type of the bootstrap method must be convertible
- * (using the {@code asType} conversions) to {@code CallSite}, which
+ * In the above examples, the return type is always {@code CallSite}, but that
+ * is not a necessary feature of call site bootstrap methods.  The only
+ * requirement is that the return type of the bootstrap method must be
+ * convertible (using the {@code asType} conversions) to {@code CallSite}, which
  * means the bootstrap method return type might be {@code Object} or
  * {@code ConstantCallSite}.
- * In the case of a dynamically-resolved constant, the return type of the bootstrap
- * method must be convertible to the type of the constant, as
- * represented by its field type descriptor.  For example, if the
- * dynamic constant has a field type descriptor of {@code "C"}
- * ({@code char}) then the bootstrap method return type could be
- * {@code Object}, {@code Character}, or {@code char}, but not
- * {@code int} or {@code Integer}.
+ * <p>
+ * Here are examples of legal bootstrap method declarations for
+ * dynamically-computed constants, given various numbers {@code N} of extra
+ * arguments.  The first row (marked {@code *}) will work for any number of
+ * extra arguments.
+ * <table class="plain" style="vertical-align:top">
+ * <caption style="display:none">Static argument types</caption>
+ * <thead>
+ * <tr><th scope="col">N</th><th scope="col">Sample bootstrap method for a dynamically-computed constant</th></tr>
+ * </thead>
+ * <tbody>
+ * <tr><th scope="row" style="font-weight:normal; vertical-align:top">*</th><td>
+ *     <ul style="list-style:none; padding-left: 0; margin:0">
+ *     <li><code>Object bootstrap(Lookup caller, String name, Class type, Object... args)</code>
+ *     <li><code>Object bootstrap(Object... args)</code>
+ *     <li><code>Object bootstrap(Object firstArg, Object... otherArgs)</code>
+ *     </ul></td></tr>
+ * <tr><th scope="row" style="font-weight:normal; vertical-align:top">0</th><td>
+ *     <ul style="list-style:none; padding-left: 0; margin:0">
+ *     <li><code>Object bootstrap(Lookup caller, String name, Class type)</code>
+ *     <li><code>Object bootstrap(Lookup caller, Object... nameAndType)</code>
+ *     <li><code>Object bootstrap()</code>
+ *     <li><code>Object bootstrap(Object... args)</code>
+ *     </ul></td></tr>
+ * <tr><th scope="row" style="font-weight:normal; vertical-align:top">1</th><td>
+ *     <ul style="list-style:none; padding-left: 0; margin:0">
+ *     <li><code>Object bootstrap(Lookup caller, String name, Class type, Object arg)</code>
+ *     <li><code>Object bootstrap(Object arg)</code>
+ *     </ul></td></tr>
+ * <tr><th scope="row" style="font-weight:normal; vertical-align:top">2</th><td>
+ *     <ul style="list-style:none; padding-left: 0; margin:0">
+ *     <li><code>Object bootstrap(Lookup caller, String name, Class type, Object... args)</code>
+ *     <li><code>Object bootstrap(Lookup caller, String name, Class type, String... args)</code>
+ *     <li><code>Object bootstrap(Lookup caller, String name, Class type, String x, int y)</code>
+ *     <li><code>Object bootstrap(Object... args)</code>
+ *     <li><code>Object bootstrap(String... args)</code>
+ *     <li><code>Object bootstrap(String x, int y)</code>
+ *     </ul></td></tr>
+ * </tbody>
+ * </table>
+ * The example methods whose first parameter type, if any, is not {@code Lookup}
+ * will be invoked with just the static arguments.
+ * <p>
+ * In the above examples, the return type is always {@code Object}, but that
+ * is not a necessary feature of dynamic constant bootstrap methods.  The only
+ * requirement is that method must be convertible to the type of the constant,
+ * as represented by its field type descriptor.  For example, if the dynamic
+ * constant has a field type descriptor of {@code "C"} ({@code char}) then the
+ * bootstrap method return type could be {@code Object}, {@code Character}, or
+ * {@code char}, but not {@code int} or {@code Integer}.
  *
  * @author John Rose, JSR 292 EG
  * @since 1.7
--- a/test/jdk/java/lang/invoke/condy/BootstrapMethodJumboArgsTest.java	Wed Jun 27 12:59:47 2018 -0700
+++ b/test/jdk/java/lang/invoke/condy/BootstrapMethodJumboArgsTest.java	Mon Jul 09 12:55:55 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8186046
+ * @bug 8186046 8206177
  * @summary Test bootstrap methods throwing an exception
  * @library /lib/testlibrary/bytecode /java/lang/invoke/common
  * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
@@ -48,6 +48,10 @@
     static final MethodHandles.Lookup L = MethodHandles.lookup();
 
 
+    static Object bsmZero(Object... args) {
+        Object[] a = args.clone();
+        return a;
+    }
     static Object bsmZero(MethodHandles.Lookup l, String name, Object type,
                       Object... args) {
         Object[] a = args.clone();
@@ -59,6 +63,12 @@
         }
     }
 
+    static Object bsmOne(Object first, Object... args) {
+        Object[] a = new Object[args.length + 1];
+        a[0] = first;
+        System.arraycopy(args, 0, a, 1, args.length);
+        return a;
+    }
     static Object bsmOne(MethodHandles.Lookup l, String name, Object type,
                          Object first, Object... args) {
         Object[] a = new Object[args.length + 1];
@@ -72,6 +82,13 @@
         }
     }
 
+    static Object bsmTwo(Object first, Object second, Object... args) {
+        Object[] a = new Object[args.length + 2];
+        a[0] = first;
+        a[1] = second;
+        System.arraycopy(args, 0, a, 2, args.length);
+        return a;
+    }
     static Object bsmTwo(MethodHandles.Lookup l, String name, Object type,
                          Object first, Object second, Object... args) {
         Object[] a = new Object[args.length + 2];
@@ -93,7 +110,7 @@
     }
 
     @Test
-    public void testCondyWithJumboArgs() throws Throwable {
+    public void testCondyWithJumboArgsWithMetaData() throws Throwable {
         String[] expected = IntStream.range(0, 1000).mapToObj(Integer::toString).toArray(String[]::new);
 
         {
@@ -128,6 +145,41 @@
     }
 
     @Test
+    public void testCondyWithJumboArgsWithoutMetaData() throws Throwable {
+        String[] expected = IntStream.range(0, 1000).mapToObj(Integer::toString).toArray(String[]::new);
+
+        {
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object[].class,
+                    "bsmZero", methodType(Object.class, Object[].class),
+                    S -> manyStaticStrings(expected, S));
+
+            Object[] actual = (Object[]) mh.invoke();
+            Assert.assertEquals(actual, expected);
+        }
+
+        {
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object[].class,
+                    "bsmOne", methodType(Object.class, Object.class, Object[].class),
+                    S -> manyStaticStrings(expected, S));
+
+            Object[] actual = (Object[]) mh.invoke();
+            Assert.assertEquals(actual, expected);
+        }
+
+        {
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object[].class,
+                    "bsmTwo", methodType(Object.class, Object.class, Object.class, Object[].class),
+                    S -> manyStaticStrings(expected, S));
+
+            Object[] actual = (Object[]) mh.invoke();
+            Assert.assertEquals(actual, expected);
+        }
+    }
+
+    @Test
     public void testIndyWithJumboArgs() throws Throwable {
         String[] expected = IntStream.range(0, 1000).mapToObj(Integer::toString).toArray(String[]::new);
 
--- a/test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java	Wed Jun 27 12:59:47 2018 -0700
+++ b/test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java	Mon Jul 09 12:55:55 2018 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8186046 8199875
+ * @bug 8186046 8199875 8206177
  * @summary Test basic invocation of bootstrap methods
  * @library /lib/testlibrary/bytecode /java/lang/invoke/common
  * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
@@ -31,7 +31,6 @@
  * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMInvocation
  */
 
-
 import org.testng.Assert;
 import org.testng.annotations.Test;
 import test.java.lang.invoke.lib.InstructionHelper;
@@ -41,7 +40,6 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.WrongMethodTypeException;
-import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.stream.IntStream;
@@ -57,7 +55,7 @@
     public void testNonexistent() throws Throwable {
         MethodHandle mh = InstructionHelper.ldcDynamicConstant(
                 L, "name", Object.class,
-                "bsm", methodType(Object.class),
+                "bsm_non_existent", methodType(Object.class),
                 S -> {});
 
         try {
@@ -79,56 +77,17 @@
                 }).toArray(MethodHandle[]::new);
     }
 
-    public static Object shape_bsm() {
+    // No name and type
+    public static Object sig_bsm(MethodHandles.Lookup a1) {
         return "0";
     }
 
-    public static Object shape_bsm(Object a1) {
-        return "0";
-    }
-
-    public static Object shape_bsm(Object... args) {
-        return "0";
-    }
-
-    public static Object shape_bsm(Object a1, Object a2) {
-        return "0";
-    }
-
-    public static Object shape_bsm(Object a1, Object... args) {
-        return "0";
-    }
-
-    public static Object shape_bsm(Object a1, Object a2, Object a3) {
-        return "0";
-    }
-
-    public static Object shape_bsm(MethodHandles.Lookup a1) {
-        return "0";
-    }
-
-    @Test
-    public void testWrongShape() throws Throwable {
-        for (MethodHandle bsm : bsms("shape_bsm")) {
-            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
-                    L, "name", Object.class,
-                    "shape_bsm", bsm.type(),
-                    S -> {}
-            );
-
-            try {
-                Object r = mh.invoke();
-                Assert.fail("BootstrapMethodError expected to be thrown for " + bsm);
-            } catch (BootstrapMethodError e) {
-            }
-        }
-    }
-
-
+    // No type
     public static Object sig_bsm(MethodHandles.Lookup a1, String[] a2) {
         return "0";
     }
 
+    // Wrong class for type
     public static Object sig_bsm(MethodHandles.Lookup a1, String a2, String a3) {
         return "0";
     }
@@ -146,59 +105,85 @@
                 Object r = mh.invoke();
                 Assert.fail("BootstrapMethodError expected to be thrown for " + bsm);
             } catch (BootstrapMethodError e) {
-                Throwable t = e.getCause();
-                Assert.assertTrue(ClassCastException.class.isAssignableFrom(t.getClass()));
             }
         }
     }
 
 
-    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type) {
+    public static Object bsm() {
         return "0";
     }
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type) {
+        return bsm();
+    }
 
-    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
-                             Object a1) {
+    public static Object bsm(Object a1) {
         assertAll(a1);
         return "1";
     }
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1) {
+        return bsm(a1);
+    }
 
-    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
-                             Object a1, Object a2) {
+    public static Object bsm(Object a1, Object a2) {
         assertAll(a1, a2);
         return "2";
     }
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2) {
+        return bsm(a1, a2);
+    }
 
-    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
-                             Object a1, Object a2, Object a3) {
+    public static Object bsm(Object a1, Object a2, Object a3) {
         assertAll(a1, a2, a3);
         return "3";
     }
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2, Object a3) {
+        return bsm(a1, a2, a3);
+    }
 
-    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
-                             Object a1, Object a2, Object a3, Object a4) {
+    public static Object bsm(Object a1, Object a2, Object a3, Object a4) {
         assertAll(a1, a2, a3, a4);
         return "4";
     }
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2, Object a3, Object a4) {
+        return bsm(a1, a2, a3, a4);
+    }
 
-    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
-                             Object a1, Object a2, Object a3, Object a4, Object a5) {
+    public static Object bsm(Object a1, Object a2, Object a3, Object a4, Object a5) {
         assertAll(a1, a2, a3, a4, a5);
         return "5";
     }
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2, Object a3, Object a4, Object a5) {
+        return bsm(a1, a2, a3, a4, a5);
+    }
 
-    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
-                             Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) {
+    public static Object bsm(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) {
         assertAll(a1, a2, a3, a4, a5, a6);
         return "6";
     }
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) {
+        return bsm(a1, a2, a3, a4, a5, a6);
+    }
 
-    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
-                             Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
+    public static Object bsm(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
         assertAll(a1, a2, a3, a4, a5, a6, a7);
         return "7";
     }
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
+        return bsm(a1, a2, a3, a4, a5, a6, a7);
+    }
 
+    public static Object bsm(Object... args) {
+        assertAll(args);
+        return Integer.toString(args.length);
+    }
     public static Object bsm(MethodHandles.Lookup l, Object... args) {
         Object[] staticArgs = Arrays.copyOfRange(args, 2, args.length);
         assertAll(staticArgs);
@@ -212,7 +197,7 @@
     }
 
     @Test
-    public void testArity() throws Throwable {
+    public void testArityWithMetadata() throws Throwable {
         for (int i = 0; i < 8; i++) {
             final int n = i;
             MethodType mt = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class)
@@ -237,12 +222,40 @@
 
             Object r = mh.invoke();
             Assert.assertEquals(r, Integer.toString(9));
-
         }
     }
 
     @Test
-    public void testWrongNumberOfStaticArguments() throws Throwable {
+    public void testArityWithoutMetadata() throws Throwable {
+        for (int i = 0; i < 8; i++) {
+            final int n = i;
+            MethodType mt = methodType(Object.class)
+                    .appendParameterTypes(Collections.nCopies(n, Object.class));
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object.class,
+                    "bsm", mt,
+                    S -> IntStream.range(0, n).forEach(S::add)
+            );
+
+            Object r = mh.invoke();
+            Assert.assertEquals(r, Integer.toString(n));
+        }
+
+        {
+            MethodType mt = methodType(Object.class, Object[].class);
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object.class,
+                    "bsm", mt,
+                    S -> IntStream.range(0, 9).forEach(S::add)
+            );
+
+            Object r = mh.invoke();
+            Assert.assertEquals(r, Integer.toString(9));
+        }
+    }
+
+    @Test
+    public void testWrongNumberOfStaticArgumentsWithMetaData() throws Throwable {
         for (int i = 1; i < 8; i++) {
             final int n = i;
             MethodType mt = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class)
@@ -263,6 +276,28 @@
         }
     }
 
+    @Test
+    public void testWrongNumberOfStaticArgumentsWithoutMetaData() throws Throwable {
+        for (int i = 1; i < 8; i++) {
+            final int n = i;
+            MethodType mt = methodType(Object.class)
+                    .appendParameterTypes(Collections.nCopies(n, Object.class));
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object.class,
+                    "bsm", mt,
+                    S -> IntStream.range(0, n - 1).forEach(S::add)
+            );
+
+            try {
+                Object r = mh.invoke();
+                Assert.fail("BootstrapMethodError expected to be thrown for arrity " + n);
+            } catch (BootstrapMethodError e) {
+                Throwable t = e.getCause();
+                Assert.assertTrue(WrongMethodTypeException.class.isAssignableFrom(t.getClass()));
+            }
+        }
+    }
+
     public static Object bsm(MethodHandles.Lookup l, BootstrapCallInfo<Class<?>> bci) {
         assertAll(bci.asList().toArray());
         return Integer.toString(bci.size());