< prev index next >

src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java

Print this page
rev 52749 : Bootstrap method consolidation
* clean up and simplify JDK support code for BSM invocation
* simplify JVM bootstrap handshake: use BootstrapCallInfo only
* remove unused JVM paths and data fields
* move bootstrap argument processing from MethodHandleNatives to ConstantPool
* remove ConstantGroup; merge argument access into BootstrapCallInfo
* adjust BSM argument access: remove copyArguments, add argumentRef API
* add metadata-free BSM modes, including symbolic arguments from CP

@@ -22,166 +22,72 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 package java.lang.invoke;
 
-import sun.invoke.util.Wrapper;
+import jdk.internal.reflect.ConstantPool;
+import jdk.internal.vm.annotation.Stable;
 
-import java.lang.invoke.AbstractConstantGroup.BSCIWithCache;
+import java.lang.constant.Constable;
+import java.lang.constant.ConstantDesc;
+import java.lang.constant.DynamicConstantDesc;
+import java.lang.reflect.Array;
 import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
 
-import static java.lang.invoke.BootstrapCallInfo.makeBootstrapCallInfo;
-import static java.lang.invoke.ConstantGroup.makeConstantGroup;
-import static java.lang.invoke.MethodHandleNatives.*;
+import java.lang.invoke.AbstractBootstrapCallInfo.*;
+
+import static java.lang.invoke.AbstractBootstrapCallInfo.maybeShareArguments;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
 import static java.lang.invoke.MethodHandles.Lookup;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import static java.util.Objects.requireNonNull;
 
 final class BootstrapMethodInvoker {
-
     /**
      * Factored code for invoking a bootstrap method for invokedynamic
      * or a dynamic constant.
-     * @param resultType the expected return type (either CallSite or a constant type)
-     * @param bootstrapMethod the BSM to call
-     * @param name the method name or constant name
-     * @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 <T> the expected return type
+     * @param lookup the capability to access the caller class's constants
+     * @param bsci the BootstrapCallInfo provided by the JVM
+     * @param <T> the kind of the type argument, {@code Class} or {@code MethodType}
      * @return the expected value, either a CallSite or a constant value
      */
-    static <T> T invoke(Class<T> resultType,
-                        MethodHandle bootstrapMethod,
-                        // Callee information:
-                        String name, Object type,
-                        // Extra arguments for BSM, if any:
-                        Object info,
-                        // Caller information:
-                        Class<?> callerClass) {
-        MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
+    static <T extends TypeDescriptor & Constable<T>>
+    Object invoke(Lookup lookup,
+                  BootstrapCallInfo<T> bsci) {
+        Objects.requireNonNull(lookup);
+        MethodHandle bsm = bsci.bootstrapMethod();
+        MethodType bsmType = bsm.type();
+        boolean bsmIsVarargs = bsm.isVarargsCollector();
         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
-            pullModeBSM = null;
-            if (pullMode) {
-                bootstrapMethod = pushMePullYou(bootstrapMethod, true);
-            }
-        } else {
-            // VM wants us to pull args from it
-            pullModeBSM = pullMode ? bootstrapMethod :
-                    pushMePullYou(bootstrapMethod, false);
-            bootstrapMethod = null;
-        }
+
         try {
-            // As an optimization we special case various known BSMs,
-            // such as LambdaMetafactory::metafactory and
-            // StringConcatFactory::makeConcatWithConstants.
-            //
-            // By providing static type information or even invoking
-            // exactly, we avoid emitting code to perform runtime
-            // 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);
-            }
-            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())) {
-                    result = (CallSite)bootstrapMethod
-                            .invokeExact(caller, name, (MethodType)type,
-                                         (String)info, new Object[0]);
-                } else {
-                    result = invoke(bootstrapMethod, caller, name, type, info);
-                }
-            }
-            else if (info.getClass() == int[].class) {
-                // VM is allowed to pass up a pair {argc, index}
-                // referring to 'argc' BSM args at some place 'index'
-                // in the guts of the VM (associated with callerClass).
-                // The format of this index pair is private to the
-                // handshake between the VM and this class only.
-                // This supports "pulling" of arguments.
-                // The VM is allowed to do this for any reason.
-                // The code in this method makes up for any mismatches.
-                BootstrapCallInfo<Object> bsci
-                    = new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info);
+            // By using maybeShareArguments, we avoid extra copy steps
+            // on the argument list between a BSM created for the JVM
+            // (by MethodHandleNatives) and the invocation logic of
+            // invokeCommon.
+            if (isPullModeBSM(bsm)) {
                 // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
-                result = pullModeBSM.invoke(caller, bsci);
+                result = bsm.invoke(lookup, bsci);
+            } else if (isExpressionBSM(bsm)) {
+                if (bsci.invocationName().equals("invoke"))
+                    // short circuit in common case
+                    result = invokeCommon(bsm,
+                                          null, null, null,
+                                          maybeShareArguments(bsci),
+                                          bsci.argumentList());
+                else
+                    result = ConstantBootstraps.linkExpression(lookup, bsci);
+            } else {
+                // Push-mode BSM, needs to get (lookup, name, type, arg...).
+                result = invokeCommon(bsm, lookup,
+                                      bsci.invocationName(), bsci.invocationType(),
+                                      maybeShareArguments(bsci), bsci.argumentList());
             }
-            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) {
-                    result = bootstrapMethod
-                            .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
-                                    (MethodHandle)argv[1], (MethodType)argv[2]);
-                } 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)) {
-                    result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, argv);
-                } else {
-                    switch (argv.length) {
-                        case 0:
-                            result = invoke(bootstrapMethod, caller, name, type);
-                            break;
-                        case 1:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0]);
-                            break;
-                        case 2:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0], argv[1]);
-                            break;
-                        case 3:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0], argv[1], argv[2]);
-                            break;
-                        case 4:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0], argv[1], argv[2], argv[3]);
-                            break;
-                        case 5:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0], argv[1], argv[2], argv[3], argv[4]);
-                            break;
-                        case 6:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
-                            break;
-                        default:
-                            result = invokeWithManyArguments(bootstrapMethod, caller, name, type, argv);
-                    }
-                }
-            }
-            if (resultType.isPrimitive()) {
-                // Non-reference conversions are more than just plain casts.
-                // By pushing the value through a funnel of the form (T x)->x,
-                // the boxed result can be widened as needed.  See MH::asType.
-                MethodHandle funnel = MethodHandles.identity(resultType);
-                result = funnel.invoke(result);
-                // Now it is the wrapper type for resultType.
-                resultType = Wrapper.asWrapperType(resultType);
-            }
-            return resultType.cast(result);
+            return BootstrapCallInfo.convertResult(bsci.invocationType(), bsmType.returnType(), result);
         }
         catch (Error e) {
             // Pass through an Error, including BootstrapMethodError, any other
             // form of linkage error, such as IllegalAccessError if the bootstrap
             // method is inaccessible, or say ThreadDeath/OutOfMemoryError

@@ -193,229 +99,487 @@
             // Wrap anything else in BootstrapMethodError
             throw new BootstrapMethodError("bootstrap method initialization exception", ex);
         }
     }
 
-    // If we don't provide static type information for type, we'll generate runtime
-    // checks. Let's try not to...
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
-                                 String name, Object type) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class<?>)type);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type);
-        }
-    }
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
-                                 String name, Object type, Object arg0) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0);
-        }
-    }
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
-                                 Object type, Object arg0, Object arg1) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1);
-        }
-    }
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
-                                 Object type, Object arg0, Object arg1,
-                                 Object arg2) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2);
-        }
+    /** A BSM runs in pull mode if and only if its sole arguments
+     * are {@code (Lookup, BootstrapCallInfo)}.
+     * Since the trailing argument is not an array type, it cannot be of variable arity.
+     * The return type of the BSM is unconstrained, but it will often be {@code Object}.
+     */
+    public static boolean isPullModeBSM(MethodHandle bsm) {
+        MethodType mtype = bsm.type();
+        return mtype.parameterCount() == 2
+               && mtype.parameterType(0) == Lookup.class
+               && mtype.parameterType(1) == BootstrapCallInfo.class;
+    }
+
+    /** Most BSM calls take a standard set of metadata arguments,
+     *  one of {@code (Lookup, String, Class)}, {@code (Lookup, String, MethodType)},
+     *  or {@code (Lookup, BootstrapCallInfo)}.
+     *  But if the lead argument is <em>not</em> {@code Lookup} <em>and</em>
+     *  the invocation type being linked is a field type ({@code Class}),
+     *  then only the static arguments will be passed.
+     *  Such a BSM is called an <em>expression mode BSM</em>.
+     *  The standard bootstrap method {@link ConstantBootstraps#linkExpression}
+     *  is consulted to correctly invoke such an expression bootstrap method.
+     *  Often, it is just applied directly to the static arguments.
+     */
+    static boolean isExpressionBSM(MethodHandle bsm) {
+        MethodType mtype = bsm.type();
+        return (mtype.parameterCount() == 0
+                || mtype.parameterType(0) != Lookup.class);
     }
 
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
-                                 Object type, Object arg0, Object arg1,
-                                 Object arg2, Object arg3) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3);
+    /** Given a poorly typed BSM, wrap it in a BSM whose
+     * type starts with Lookup.  This will prevent it from
+     * being accidentally treated as an expression-mode BSM.
+     * We only do this, of course, if we know that the BSM
+     * is not supposed to have expression mode.  This is the
+     * case when the BSM links an invokedynamic instruction.
+     */
+    static MethodHandle forceLookupParameter(MethodHandle bsm) {
+        if (TRACE_METHOD_LINKAGE)
+            System.out.println("[TRACE_METHOD_LINKAGE] forceLookupParameter "+bsm);
+        MethodHandle mh = MH_forceLookupParameter;
+        if (mh == null) {
+            try {
+                mh = IMPL_LOOKUP.findStatic(BootstrapMethodInvoker.class, "forceLookupParameter",
+                                            MethodType.methodType(Object.class, MethodHandle.class,
+                                                                  Lookup.class, Object[].class));
+            } catch (ReflectiveOperationException ex) {
+                throw new InternalError(ex);
         }
+            MH_forceLookupParameter = mh;
     }
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
-                                 String name, Object type, Object arg0, Object arg1,
-                                 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 {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4);
+        return mh.bindTo(bsm).withVarargs(true);
         }
+    private static @Stable MethodHandle MH_forceLookupParameter;
+    private static Object forceLookupParameter(MethodHandle bsm,
+                                               Lookup lookup,
+                                               Object... otherArgs) throws Throwable {
+        // There are often faster ways to get this done, but this formulation
+        // covers all the corner cases.  Poorly typed BSMs are rare, anyway.
+        Object[] args = new Object[1 + otherArgs.length];
+        args[0] = lookup; System.arraycopy(otherArgs, 0, args, 1, otherArgs.length);
+        return bsm.invokeWithArguments(args);
     }
 
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
-                                 String name, Object type, Object arg0, Object arg1,
-                                 Object arg2, Object arg3, Object arg4, Object arg5) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4, arg5);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4, arg5);
-        }
-    }
-
-    private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Lookup caller,
-                                                  String name, Object type, Object[] argv) throws Throwable {
-        final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
-        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);
-            return 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);
-            return spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
-        }
-    }
+    /// Signatures which have bespoke invocation paths:
 
+    // Lambda metafactory, standard.
     private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
             Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
 
+    // Lambda metafactory, alternate.
     private static final MethodType LMF_ALT_MT = MethodType.methodType(CallSite.class,
             Lookup.class, String.class, MethodType.class, Object[].class);
 
+    // Lambda metafactory, condy.
     private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class,
             Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class);
 
+    // String concat metafactory.
     private static final MethodType SCF_MT = MethodType.methodType(CallSite.class,
             Lookup.class, String.class, MethodType.class, String.class, Object[].class);
 
-    /**
-     * @return true iff the BSM method type exactly matches
-     *         {@see java.lang.invoke.StringConcatFactory#makeConcatWithConstants(MethodHandles.Lookup,
-     *                 String,MethodType,String,Object...))}
-     */
-    private static boolean isStringConcatFactoryBSM(MethodType bsmType) {
-        return bsmType == SCF_MT;
-    }
-
-    /**
-     * @return true iff the BSM method type exactly matches
-     *         {@see java.lang.invoke.LambdaMetafactory#metafactory(
-     *          MethodHandles.Lookup,String,Class,MethodType,MethodHandle,MethodType)}
-     */
-    private static boolean isLambdaMetafactoryCondyBSM(MethodType bsmType) {
-        return bsmType == LMF_CONDY_MT;
-    }
-
-    /**
-     * @return true iff the BSM method type exactly matches
-     *         {@see java.lang.invoke.LambdaMetafactory#metafactory(
-     *          MethodHandles.Lookup,String,MethodType,MethodType,MethodHandle,MethodType)}
-     */
-    private static boolean isLambdaMetafactoryIndyBSM(MethodType bsmType) {
-        return bsmType == LMF_INDY_MT;
-    }
-
-    /**
-     * @return true iff the BSM method type exactly matches
-     *         {@see java.lang.invoke.LambdaMetafactory#altMetafactory(
-     *          MethodHandles.Lookup,String,MethodType,Object[])}
-     */
-    private static boolean isLambdaMetafactoryAltMetafactoryBSM(MethodType bsmType) {
-        return bsmType == LMF_ALT_MT;
-    }
-
     /** The JVM produces java.lang.Integer values to box
      *  CONSTANT_Integer boxes but does not intern them.
      *  Let's intern them.  This is slightly wrong for
      *  a {@code CONSTANT_Dynamic} which produces an
      *  un-interned integer (e.g., {@code new Integer(0)}).
      */
-    private static Object maybeReBox(Object x) {
+    static Object maybeReBox(Object x) {
         if (x instanceof Integer) {
             int xi = (int) x;
             if (xi == (byte) xi)
                 x = xi;  // must rebox; see JLS 5.1.7
         }
         return x;
     }
 
-    private static void maybeReBoxElements(Object[] xa) {
+    static void maybeReBoxElements(Object[] xa) {
         for (int i = 0; i < xa.length; i++) {
             xa[i] = maybeReBox(xa[i]);
         }
     }
 
+    static Object invokeCommon(MethodHandle handle,
+                               Lookup lookup,
+                               String name,
+                               TypeDescriptor type,
+                               Object[] argv,
+                               List<Object> args)
+            throws Throwable {
+        // Efficiently invoke a method (probably a BSM) on a particular
+        // set of bootstrap arguments, perhaps including the usual
+        // metadata values of (lookup, name, type).
+        // To avoid copying, source the arguments from whatever
+        // the caller has on hand:  A Java array, a List, or both.
+        //
+        // Support fast arity-specific invocation paths for both
+        // method types and field types (Class and MethodType), and
+        // no-metadata calls also.  To get to these paths as often
+        // as possible, executet some varargs processing directly.
+        //
+        // For further optimization we special-case various known BSMs,
+        // including LambdaMetafactory::metafactory and
+        // StringConcatFactory::makeConcatWithConstants.
+        // (Actually, we special-case their *types*, which amounts
+        // to the same thing.)
+        //
+        // By providing static type information or even invoking
+        // exactly, we avoid emitting code to perform runtime
+        // checking.
+        //
+        // Because we usually don't examine the BSM return type,
+        // we can't use the MH.invokeExact mode as much as we would
+        // like, but at least the MH.invoke path has a reasonably
+        // small type mismatch.
+
+        if (TRACE_METHOD_LINKAGE) {
+            System.out.println("invoking " + handle + " on " + (argv == null ? args : Arrays.asList(argv)));
+        }
+        int argc = argv != null ? argv.length : args.size();
+
+        boolean isFieldType  = (type instanceof Class);
+        boolean isMethodType = (type instanceof MethodType);
+
+        boolean passMetadata = (lookup != null);
+        final int MAX_ARITY = 7, FT = MAX_ARITY+1, MT = FT*2, NMD = 0;
+        final int METADATA_ARG_COUNT = 3;  // (lookup, name, type)
+        final int metaArgc = passMetadata ? METADATA_ARG_COUNT : 0;
+        if (passMetadata) {
+            requireNonNull(name);
+            requireNonNull(type);
+        }
+
+        MethodType handleType = handle.type();
+        boolean handleVarargs = handle.isVarargsCollector();
+
+        if (!passMetadata) {
+            // Must not lead with a Lookup formal parameter type.
+            // Lookup leading types are reserved for metadata-using BSMs.
+            assert(handleType.parameterCount() == 0 ||
+                   handleType.parameterType(0) != Lookup.class);
+            assert(name == null);
+            assert(type == null);
+        } else if (isFieldType) {
+            // With condy, we require an explicit leading type of Lookup,
+            // if metadata is to be passed.
+            assert(handleType.parameterCount() == 0 ||
+                   handleType.parameterType(0) == Lookup.class);
+            // Also, the caller should not try to handle a BSCI call here.
+            assert(handleType.parameterCount() != 2 ||
+                   handleType.parameterType(1) != BootstrapCallInfo.class);
+            // Otherwise, we are doing an old-style indy bootstrap,
+            // which tolerates Object in the metadata positions.
+        }
+
+        // Some varargs methods can be handled quickly here,
+        // rather than going through more dynamic invocation paths.
+        // Do the varargs spreading if:  the BSM is varargs, the BSM
+        // directly accepts at least the metadata arguments (if applicable),
+        // the given static arguments fill in the BSM's inital set
+        // of arguments, and the BSM's trailing argument is a
+        // reference array.  In that case, split the static argument
+        // list into head and tail arrays, and link the latter onto the
+        // end of the former.
+        if (handleVarargs) {
+            int headArgc = (handleType.parameterCount() - 1) - metaArgc;
+            Class<?> tailType = handleType.lastParameterType();
+            Class<?> tailElementType = tailType.getComponentType();
+            if (headArgc >= 0 &&
+                headArgc <= argc &&
+                Object[].class.isAssignableFrom(tailType) &&
+                tailElementType != null) {
+                // Make a shortened argument list for a fixed-arity call.
+                Object[] headArgv = new Object[headArgc + 1];
+                if (argv != null) {
+                    System.arraycopy(argv, 0, headArgv, 0, headArgc);
+                } else {
+                    List<Object> headArgs = args.subList(0, headArgc);
+                    headArgv = headArgs.toArray(headArgv);
+                }
+                // Collect the trailing arguments into an array of the
+                // required type.  Also, make sure they fit into the array.
+                int tailArgc = argc - headArgc;
+                Object[] tailArgv = (tailElementType == Object.class)
+                        ? new Object[tailArgc]
+                        : (Object[]) Array.newInstance(tailElementType, tailArgc);
+                try {
+                    if (argv != null) {
+                        System.arraycopy(argv, headArgc, tailArgv, 0, tailArgc);
+                    } else {
+                        List<Object> tailArgs = args.subList(headArgc, argc);
+                        tailArgv = tailArgs.toArray(tailArgv);
+                    }
+                } catch (ArrayStoreException ex) {
+                    // Oops, one of the actuals doesn't fit in the varargs formal.
+                    // For example, f(String...) is passed ("foo", 42, "bar").
+                    tailArgv = null;  // bail out
+                }
+                if (tailArgv != null) {
+                    // Link the head to the tail:
+                    headArgv[headArgc] = tailArgv;
+                    // Now swap in a new MH and arglist:
+                    handle = handle.asFixedArity();
+                    assert(handleType == handle.type());  // still true
+                    handleVarargs = false;
+                    argv = headArgv;
+                    args = null;
+                    argc = headArgc + 1;
+                }
+            }
+        }
+
+        // Now try to make an inline call:
+        if (argc <= MAX_ARITY && argv == null)
+            argv = args.toArray();
+
+        assert(argv == null || argc == argv.length);
+        assert(args == null || argc == args.size());
+
+        // If we don't provide static type information for the type,
+        // we'll generate runtime checks.  The cases marked FT and MT
+        // avoid that problem.
+
+        switch (argc > MAX_ARITY ? -1 :
+                argc + (isFieldType ? FT : isMethodType ? MT : NMD)) {
+            // Fixed-arity cases for a Class (field) type:
+            case 0+FT:
+                return handle.invoke(lookup, name, (Class<?>)type);
+            case 1+FT:
+                return handle.invoke(lookup, name, (Class<?>)type,
+                                     argv[0]);
+            case 2+FT:
+                return handle.invoke(lookup, name, (Class<?>)type,
+                                     argv[0], argv[1]);
+            case 3+FT:
+                if (handleType == LMF_CONDY_MT && !handleVarargs) {
+                    return handle.invokeExact(lookup, name, (Class<?>)type,
+                                              (MethodType)argv[0],
+                                              (MethodHandle)argv[1],
+                                              (MethodType)argv[2]);
+                }
+                return handle.invoke(lookup, name, (Class<?>)type,
+                                     argv[0], argv[1], argv[2]);
+            case 4+FT:
+                return handle.invoke(lookup, name, (Class<?>)type,
+                                     argv[0], argv[1], argv[2], argv[3]);
+            case 5+FT:
+                return handle.invoke(lookup, name, (Class<?>)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4]);
+            case 6+FT:
+                return handle.invoke(lookup, name, (Class<?>)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5]);
+            case MAX_ARITY+FT:
+                return handle.invoke(lookup, name, (Class<?>)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5], argv[6]);
+
+            // Same cases, but for a MethodType argument:
+            case 0+MT:
+                return handle.invoke(lookup, name, (MethodType)type);
+            case 1+MT:
+                if (handleType == LMF_ALT_MT && !handleVarargs) {
+                    return (CallSite)
+                        handle.invokeExact(lookup, name, (MethodType)type,
+                                           (Object[])argv[0]);
+                }
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0]);
+            case 2+MT:
+                if (handleType == SCF_MT && !handleVarargs) {
+                    return (CallSite)
+                        handle.invokeExact(lookup, name, (MethodType)type,
+                                           (String)argv[0],
+                                           (Object[])argv[1]);
+                }
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1]);
+            case 3+MT:
+                if (handleType == LMF_INDY_MT && !handleVarargs) {
+                    return (CallSite)
+                        handle.invokeExact(lookup, name, (MethodType)type,
+                                           (MethodType)argv[0],
+                                           (MethodHandle)argv[1],
+                                           (MethodType)argv[2]);
+                }
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1], argv[2]);
+            case 4+MT:
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1], argv[2], argv[3]);
+            case 5+MT:
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4]);
+            case 6+MT:
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5]);
+            case MAX_ARITY+MT:
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5], argv[6]);
+
+            // Same cases again, but without metadata:
+            case 0+NMD:
+                return handle.invoke();
+            case 1+NMD:
+                return handle.invoke(argv[0]);
+            case 2+NMD:
+                return handle.invoke(argv[0], argv[1]);
+            case 3+NMD:
+                return handle.invoke(argv[0], argv[1], argv[2]);
+            case 4+NMD:
+                return handle.invoke(argv[0], argv[1], argv[2], argv[3]);
+            case 5+NMD:
+                return handle.invoke(argv[0], argv[1], argv[2], argv[3],
+                                     argv[4]);
+            case 6+NMD:
+                return handle.invoke(argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5]);
+            case MAX_ARITY+NMD:
+                return handle.invoke(argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5], argv[6]);
+        }
+
+        // We have to stop at some point and use invokeWithArguments instead.
+        // Note that invokeWithArguments handles jumbo argument lists.
+
+        if (passMetadata) {
+            Object[] allArgv = new Object[metaArgc + argc];
+            int pos = 0;
+            allArgv[pos++] = lookup;
+            allArgv[pos++] = name;
+            allArgv[pos++] = type;
+            assert(pos == metaArgc);
+            if (argv != null) {
+                System.arraycopy(argv, 0, allArgv, pos, argc);
+                pos += argc;
+            } else {
+                for (Object arg : args) {
+                    allArgv[pos++] = arg;
+                }
+            }
+            assert(pos == allArgv.length);
+            return handle.invokeWithArguments(allArgv);
+        } else if (argv != null) {
+            // It's simple if there are no metadata arguments to prepend.
+            return handle.invokeWithArguments(argv);
+        } else {
+            return handle.invokeWithArguments(args);
+        }
+    }
+
     /** Canonical VM-aware implementation of BootstrapCallInfo.
      * Knows how to dig into the JVM for lazily resolved (pull-mode) constants.
      */
-    private static final class VM_BSCI<T> extends BSCIWithCache<T> {
-        private final int[] indexInfo;
-        private final Class<?> caller;  // for index resolution only
-
-        VM_BSCI(MethodHandle bsm, String name, T type,
-                Lookup lookup, int[] indexInfo) {
-            super(bsm, name, type, indexInfo[0]);
-            if (!lookup.hasPrivateAccess())  //D.I.D.
-                throw new AssertionError("bad Lookup object");
-            this.caller = lookup.lookupClass();
-            this.indexInfo = indexInfo;
-            // scoop up all the easy stuff right away:
-            prefetchIntoCache(0, size());
+    static final class VM_BSCI<T extends TypeDescriptor & Constable<T>> extends WithCache<T> {
+        private final int bssIndex;
+        private final int indyIndex;
+        private final ConstantPool pool;
+        private int[] argIndexes;  // lazy
+
+        public VM_BSCI(ConstantPool pool, int bssIndex, int indyIndex,
+                       MethodHandle bsm, String name, TypeView<T> type, Object[] arguments) {
+            super(bsm, name, type, arguments);
+            this.pool = pool;
+            this.bssIndex = bssIndex;
+            this.indyIndex = indyIndex;
         }
 
-        @Override Object fillCache(int i) {
+        /** Special VM-specific hook to resolve static arguments. */
+        @Override Object resolveConstant(int i) {
             Object[] buf = { null };
-            copyConstants(i, i+1, buf, 0);
-            Object res = wrapNull(buf[0]);
-            cache[i] = res;
+            copyVMArguments(i, i+1, buf, 0, BAR_FORCE, null);
+            Object res = buf[0];
             int next = i + 1;
             if (next < cache.length && cache[next] == null)
-                maybePrefetchIntoCache(next, false);  // try to prefetch
+                maybePrefetchIntoCache(cache, next, false);  // try to prefetch
             return res;
         }
 
-        @Override public int copyConstants(int start, int end,
-                                           Object[] buf, int pos) {
+        /** Special VM-specific hook to find symbolic references. */
+        @Override ConstantDesc<?> findSymbol(int i) {
+            Object[] buf = { null };
+            copyVMArguments(i, i+1, buf, 0, BAR_SYMREF, null);
+            ConstantDesc<?> res = (ConstantDesc<?>) buf[0];
+            //int next = i + 1;
+            //if (next < symCache.length && symCache[next] == null)
+            //    maybePrefetchIntoCache(symCache, next, false);  // try to prefetch
+            return res;
+        }
+
+        private Class<?> caller() { return pool.getHolder(); }
+
+        /*non-public contract with ArgList*/
+        void copyVMArguments(int start, int end,
+                             Object[] buf, int pos,
+                             byte resolving, Object ifNotPresent) {
+            if (buf.getClass() != Object[].class)  throw new InternalError();
             int i = start, bufi = pos;
+            if (resolving == BAR_SYMREF) {
             while (i < end) {
+                    Object x = symCache[i];
+                    if (x == null)  break;
+                    buf[bufi++] = x;
+                    i++;
+                }
+                if (i >= end)  return;
+                // pull in all of the symbols into symCache
+                int[] temp = new int[end - i];
+                int tempi = 0;
+                pool.copyOutBootstrapArgumentsAt(bssIndex,
+                        start, end, temp, 0,
+                        BAR_SYMREF, null, null, false);
+                for (; i < end; i++) {
+                    ConstantDesc<?> x = symCache[i];
+                    int symIndex = temp[tempi++];
+                    if (x == null) {
+                        x = makeConstantFromPool(symIndex);
+                        symCache[i] = x;
+                    }
+                    buf[bufi++] = x;
+                }
+                return;
+            }
+            assert(resolving == BAR_IFPRESENT || resolving == BAR_FORCE);
+            for (; i < end; i++) {
                 Object x = cache[i];
                 if (x == null)  break;
                 buf[bufi++] = unwrapNull(x);
-                i++;
             }
             // give up at first null and grab the rest in one big block
-            if (i >= end)  return i;
-            Object[] temp = new Object[end - i];
+            if (i >= end)  return;
             if (TRACE_METHOD_LINKAGE) {
                 System.out.println("resolving more BSM arguments: " +
-                        Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end));
+                        Arrays.asList(caller().getSimpleName(), bssIndex, indyIndex, i, end));
             }
-            copyOutBootstrapArguments(caller, indexInfo,
-                                      i, end, temp, 0,
-                                      true, null);
-            for (Object x : temp) {
-                x = maybeReBox(x);
-                buf[bufi++] = x;
-                cache[i++] = wrapNull(x);
+            pool.copyOutBootstrapArgumentsAt(bssIndex,
+                    i, end, cache, i,
+                    resolving, null, wrapNull(null), true);
+            for (; i < end; i++) {
+                Object x = cache[i];
+                Object x2 = maybeReBox(x);
+                if (x2 != x)  cache[i] = x = x2;
+                buf[bufi++] = (x == null) ? ifNotPresent : unwrapNull(x);
             }
             if (end < cache.length && cache[end] == null)
-                maybePrefetchIntoCache(end, true);  // try to prefetch
-            return i;
+                maybePrefetchIntoCache(cache, end, true);  // try to prefetch
+        }
+
+        private ConstantDesc<?> makeConstantFromPool(int symIndex) {
+            return pool.getConstantDescAt(symIndex);
         }
 
         private static final int MIN_PF = 4;
-        private void maybePrefetchIntoCache(int i, boolean bulk) {
+        private void maybePrefetchIntoCache(Object[] cache, int i, boolean bulk) {
             int len = cache.length;
             assert(0 <= i && i <= len);
             int pfLimit = i;
             if (bulk)  pfLimit += i;  // exponential prefetch expansion
             // try to prefetch at least MIN_PF elements

@@ -436,139 +600,90 @@
                     if (pfLimit < len)  pfLimit++;
                 }
             }
             if (bulk && empty < MIN_PF && pfLimit < len)
                 return;  // not worth the effort
-            prefetchIntoCache(i, pfLimit);
+            prefetchIntoCache(cache, i, pfLimit);
         }
 
-        private void prefetchIntoCache(int i, int pfLimit) {
+        private void prefetchIntoCache(Object[] cache, int i, int pfLimit) {
             if (pfLimit <= i)  return;  // corner case
-            Object[] temp = new Object[pfLimit - i];
             if (TRACE_METHOD_LINKAGE) {
                 System.out.println("prefetching BSM arguments: " +
-                        Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit));
-            }
-            copyOutBootstrapArguments(caller, indexInfo,
-                                      i, pfLimit, temp, 0,
-                                      false, NOT_PRESENT);
-            for (Object x : temp) {
-                if (x != NOT_PRESENT && cache[i] == null) {
-                    cache[i] = wrapNull(maybeReBox(x));
+                        Arrays.asList(caller().getSimpleName(), bssIndex, indyIndex, i, pfLimit));
                 }
-                i++;
+            pool.copyOutBootstrapArgumentsAt(bssIndex,
+                                             i, pfLimit, cache, i,
+                                             BAR_IFPRESENT, null, wrapNull(null), true);
             }
+
+        void setArgIndexes(int[] argIndexes) {
+            assert(this.argIndexes == null);
+            this.argIndexes = argIndexes;
         }
     }
 
-    /*non-public*/ static final
-    class PushAdapter {
-        // skeleton for push-mode BSM which wraps a pull-mode BSM:
-        static Object pushToBootstrapMethod(MethodHandle pullModeBSM,
-                                            MethodHandles.Lookup lookup, String name, Object type,
-                                            Object... arguments) throws Throwable {
-            ConstantGroup cons = makeConstantGroup(Arrays.asList(arguments));
-            BootstrapCallInfo<?> bsci = makeBootstrapCallInfo(pullModeBSM, name, type, cons);
-            if (TRACE_METHOD_LINKAGE)
-                System.out.println("pull-mode BSM gets pushed arguments from fake BSCI");
-            return pullModeBSM.invoke(lookup, bsci);
+    /** Canonical DynamicConstantDesc implementation of BootstrapCallInfo.
+     */
+    static final class DCD_BSCI extends WithCache<Class<?>> {
+        private Lookup lookup;
+        private DynamicConstantDesc<?> desc;
+        private final List<ConstantDesc<?>> args;
+
+        public DCD_BSCI(Lookup lookup, DynamicConstantDesc<?> desc) throws ReflectiveOperationException {
+            super(desc.bootstrapMethod().resolveConstantDesc(lookup),
+                  desc.constantName(),
+                  new TypeView<Class<?>>(desc.constantType(), lookup),
+                  desc.bootstrapArgs());
+            this.lookup = lookup;
+            this.desc = desc;
+            this.args = desc.bootstrapArgsList();
         }
 
-        static final MethodHandle MH_pushToBootstrapMethod;
-        static {
-            final Class<?> THIS_CLASS = PushAdapter.class;
+        @Override Object resolveConstant(int i) throws BootstrapMethodError {
             try {
-                MH_pushToBootstrapMethod = IMPL_LOOKUP
-                    .findStatic(THIS_CLASS, "pushToBootstrapMethod",
-                                MethodType.methodType(Object.class, MethodHandle.class,
-                                        Lookup.class, String.class, Object.class, Object[].class));
-            } catch (Throwable ex) {
-                throw new InternalError(ex);
-            }
+                return argumentDesc(i).resolveConstantDesc(lookup);
+            } catch (ReflectiveOperationException ex) {
+                throw new BootstrapMethodError(ex);
         }
     }
 
-    /*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);
+        @Override
+        ConstantDesc<?> findSymbol(int i) {
+            return args.get(i);
                 }
         }
 
-        static final MethodHandle MH_pullFromBootstrapMethod;
-
-        static {
-            final Class<?> THIS_CLASS = PullAdapter.class;
-            try {
-                MH_pullFromBootstrapMethod = IMPL_LOOKUP
-                    .findStatic(THIS_CLASS, "pullFromBootstrapMethod",
-                                MethodType.methodType(Object.class, MethodHandle.class,
-                                        Lookup.class, BootstrapCallInfo.class));
-            } catch (Throwable ex) {
-                throw new InternalError(ex);
-            }
-        }
+    /**
+     * Create a BootstrapCallInfo whose symbolic content is derived from the given
+     * descriptor.  When it resolves constants, it will use the given Lookup object.
+     * @param desc the dynamic constant descriptor
+     * @param lookup a lookup object which is capable of resolving the dynamic constant
+     * @return a BootstrapCallInfo which carries the given
+     * @throws ReflectiveOperationException if the bootstrap method fails to resolve
+     */
+    static BootstrapCallInfo<Class<?>> ofConstantDesc(DynamicConstantDesc<?> desc, Lookup lookup)
+                throws ReflectiveOperationException {
+        return new BootstrapMethodInvoker.DCD_BSCI(lookup, desc);
     }
 
-    /** 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.)
+    /**
+     * Resolve a dynamic constant, by creating a temporary BootstrapCallInfo for it
+     * and immediately invoking its resolution behavior.  Any cached resolution state
+     * will be lost after the temporary BootstrapCallInfo is discarded.
+     * @param desc the dynamic constant descriptor
+     * @param lookup a lookup object which is capable of resolving the dynamic constant
+     * @return the result of resolving the descriptor in the lookup class
+     * @throws ReflectiveOperationException
      */
-    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);
+    static Object resolveDynamicConstant(DynamicConstantDesc<?> desc, Lookup lookup)
+            throws ReflectiveOperationException {
+        try {
+            return invoke(lookup, ofConstantDesc(desc, lookup));
+        } catch (LinkageError ex) {
+            var roe = ex.getCause();
+            if (roe instanceof ReflectiveOperationException)
+                throw (ReflectiveOperationException) roe;
+            throw ex;
         }
     }
 }
< prev index next >