1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package java.lang.invoke;
  26 
  27 import jdk.internal.reflect.ConstantPool;
  28 import jdk.internal.vm.annotation.Stable;
  29 
  30 import java.lang.constant.Constable;
  31 import java.lang.constant.ConstantDesc;
  32 import java.lang.constant.DynamicConstantDesc;
  33 import java.lang.reflect.Array;
  34 import java.util.Arrays;
  35 import java.util.List;
  36 import java.util.Objects;
  37 
  38 import java.lang.invoke.AbstractBootstrapCallInfo.*;
  39 
  40 import static java.lang.invoke.AbstractBootstrapCallInfo.maybeShareArguments;
  41 import static java.lang.invoke.MethodHandleNatives.Constants.*;
  42 import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
  43 import static java.lang.invoke.MethodHandles.Lookup;
  44 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
  45 import static java.util.Objects.requireNonNull;
  46 
  47 final class BootstrapMethodInvoker {
  48     /**
  49      * Factored code for invoking a bootstrap method for invokedynamic
  50      * or a dynamic constant.
  51      * @param lookup the capability to access the caller class's constants
  52      * @param bsci the BootstrapCallInfo provided by the JVM
  53      * @param <T> the kind of the type argument, {@code Class} or {@code MethodType}
  54      * @return the expected value, either a CallSite or a constant value
  55      */
  56     static <T extends TypeDescriptor & Constable<T>>
  57     Object invoke(Lookup lookup,
  58                   BootstrapCallInfo<T> bsci) {
  59         Objects.requireNonNull(lookup);
  60         MethodHandle bsm = bsci.bootstrapMethod();
  61         MethodType bsmType = bsm.type();
  62         boolean bsmIsVarargs = bsm.isVarargsCollector();
  63         Object result;
  64 
  65         try {
  66             // By using maybeShareArguments, we avoid extra copy steps
  67             // on the argument list between a BSM created for the JVM
  68             // (by MethodHandleNatives) and the invocation logic of
  69             // invokeCommon.
  70             if (isPullModeBSM(bsm)) {
  71                 // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
  72                 result = bsm.invoke(lookup, bsci);
  73             } else if (isExpressionBSM(bsm)) {
  74                 if (bsci.invocationName().equals("invoke"))
  75                     // short circuit in common case
  76                     result = invokeCommon(bsm,
  77                                           null, null, null,
  78                                           maybeShareArguments(bsci),
  79                                           bsci.argumentList());
  80                 else
  81                     result = ConstantBootstraps.linkExpression(lookup, bsci);
  82             } else {
  83                 // Push-mode BSM, needs to get (lookup, name, type, arg...).
  84                 result = invokeCommon(bsm, lookup,
  85                                       bsci.invocationName(), bsci.invocationType(),
  86                                       maybeShareArguments(bsci), bsci.argumentList());
  87             }
  88             return BootstrapCallInfo.convertResult(bsci.invocationType(), bsmType.returnType(), result);
  89         }
  90         catch (Error e) {
  91             // Pass through an Error, including BootstrapMethodError, any other
  92             // form of linkage error, such as IllegalAccessError if the bootstrap
  93             // method is inaccessible, or say ThreadDeath/OutOfMemoryError
  94             // See the "Linking Exceptions" section for the invokedynamic
  95             // instruction in JVMS 6.5.
  96             throw e;
  97         }
  98         catch (Throwable ex) {
  99             // Wrap anything else in BootstrapMethodError
 100             throw new BootstrapMethodError("bootstrap method initialization exception", ex);
 101         }
 102     }
 103 
 104     /** A BSM runs in pull mode if and only if its sole arguments
 105      * are {@code (Lookup, BootstrapCallInfo)}.
 106      * Since the trailing argument is not an array type, it cannot be of variable arity.
 107      * The return type of the BSM is unconstrained, but it will often be {@code Object}.
 108      */
 109     public static boolean isPullModeBSM(MethodHandle bsm) {
 110         MethodType mtype = bsm.type();
 111         return mtype.parameterCount() == 2
 112                && mtype.parameterType(0) == Lookup.class
 113                && mtype.parameterType(1) == BootstrapCallInfo.class;
 114     }
 115 
 116     /** Most BSM calls take a standard set of metadata arguments,
 117      *  one of {@code (Lookup, String, Class)}, {@code (Lookup, String, MethodType)},
 118      *  or {@code (Lookup, BootstrapCallInfo)}.
 119      *  But if the lead argument is <em>not</em> {@code Lookup} <em>and</em>
 120      *  the invocation type being linked is a field type ({@code Class}),
 121      *  then only the static arguments will be passed.
 122      *  Such a BSM is called an <em>expression mode BSM</em>.
 123      *  The standard bootstrap method {@link ConstantBootstraps#linkExpression}
 124      *  is consulted to correctly invoke such an expression bootstrap method.
 125      *  Often, it is just applied directly to the static arguments.
 126      */
 127     static boolean isExpressionBSM(MethodHandle bsm) {
 128         MethodType mtype = bsm.type();
 129         return (mtype.parameterCount() == 0
 130                 || mtype.parameterType(0) != Lookup.class);
 131     }
 132 
 133     /** Given a poorly typed BSM, wrap it in a BSM whose
 134      * type starts with Lookup.  This will prevent it from
 135      * being accidentally treated as an expression-mode BSM.
 136      * We only do this, of course, if we know that the BSM
 137      * is not supposed to have expression mode.  This is the
 138      * case when the BSM links an invokedynamic instruction.
 139      */
 140     static MethodHandle forceLookupParameter(MethodHandle bsm) {
 141         if (TRACE_METHOD_LINKAGE)
 142             System.out.println("[TRACE_METHOD_LINKAGE] forceLookupParameter "+bsm);
 143         MethodHandle mh = MH_forceLookupParameter;
 144         if (mh == null) {
 145             try {
 146                 mh = IMPL_LOOKUP.findStatic(BootstrapMethodInvoker.class, "forceLookupParameter",
 147                                             MethodType.methodType(Object.class, MethodHandle.class,
 148                                                                   Lookup.class, Object[].class));
 149             } catch (ReflectiveOperationException ex) {
 150                 throw new InternalError(ex);
 151             }
 152             MH_forceLookupParameter = mh;
 153         }
 154         return mh.bindTo(bsm).withVarargs(true);
 155     }
 156     private static @Stable MethodHandle MH_forceLookupParameter;
 157     private static Object forceLookupParameter(MethodHandle bsm,
 158                                                Lookup lookup,
 159                                                Object... otherArgs) throws Throwable {
 160         // There are often faster ways to get this done, but this formulation
 161         // covers all the corner cases.  Poorly typed BSMs are rare, anyway.
 162         Object[] args = new Object[1 + otherArgs.length];
 163         args[0] = lookup; System.arraycopy(otherArgs, 0, args, 1, otherArgs.length);
 164         return bsm.invokeWithArguments(args);
 165     }
 166 
 167     /// Signatures which have bespoke invocation paths:
 168 
 169     // Lambda metafactory, standard.
 170     private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
 171             Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
 172 
 173     // Lambda metafactory, alternate.
 174     private static final MethodType LMF_ALT_MT = MethodType.methodType(CallSite.class,
 175             Lookup.class, String.class, MethodType.class, Object[].class);
 176 
 177     // Lambda metafactory, condy.
 178     private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class,
 179             Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class);
 180 
 181     // String concat metafactory.
 182     private static final MethodType SCF_MT = MethodType.methodType(CallSite.class,
 183             Lookup.class, String.class, MethodType.class, String.class, Object[].class);
 184 
 185     /** The JVM produces java.lang.Integer values to box
 186      *  CONSTANT_Integer boxes but does not intern them.
 187      *  Let's intern them.  This is slightly wrong for
 188      *  a {@code CONSTANT_Dynamic} which produces an
 189      *  un-interned integer (e.g., {@code new Integer(0)}).
 190      */
 191     static Object maybeReBox(Object x) {
 192         if (x instanceof Integer) {
 193             int xi = (int) x;
 194             if (xi == (byte) xi)
 195                 x = xi;  // must rebox; see JLS 5.1.7
 196         }
 197         return x;
 198     }
 199 
 200     static void maybeReBoxElements(Object[] xa) {
 201         for (int i = 0; i < xa.length; i++) {
 202             xa[i] = maybeReBox(xa[i]);
 203         }
 204     }
 205 
 206     static Object invokeCommon(MethodHandle handle,
 207                                Lookup lookup,
 208                                String name,
 209                                TypeDescriptor type,
 210                                Object[] argv,
 211                                List<Object> args)
 212             throws Throwable {
 213         // Efficiently invoke a method (probably a BSM) on a particular
 214         // set of bootstrap arguments, perhaps including the usual
 215         // metadata values of (lookup, name, type).
 216         // To avoid copying, source the arguments from whatever
 217         // the caller has on hand:  A Java array, a List, or both.
 218         //
 219         // Support fast arity-specific invocation paths for both
 220         // method types and field types (Class and MethodType), and
 221         // no-metadata calls also.  To get to these paths as often
 222         // as possible, executet some varargs processing directly.
 223         //
 224         // For further optimization we special-case various known BSMs,
 225         // including LambdaMetafactory::metafactory and
 226         // StringConcatFactory::makeConcatWithConstants.
 227         // (Actually, we special-case their *types*, which amounts
 228         // to the same thing.)
 229         //
 230         // By providing static type information or even invoking
 231         // exactly, we avoid emitting code to perform runtime
 232         // checking.
 233         //
 234         // Because we usually don't examine the BSM return type,
 235         // we can't use the MH.invokeExact mode as much as we would
 236         // like, but at least the MH.invoke path has a reasonably
 237         // small type mismatch.
 238 
 239         if (TRACE_METHOD_LINKAGE) {
 240             System.out.println("invoking " + handle + " on " + (argv == null ? args : Arrays.asList(argv)));
 241         }
 242         int argc = argv != null ? argv.length : args.size();
 243 
 244         boolean isFieldType  = (type instanceof Class);
 245         boolean isMethodType = (type instanceof MethodType);
 246 
 247         boolean passMetadata = (lookup != null);
 248         final int MAX_ARITY = 7, FT = MAX_ARITY+1, MT = FT*2, NMD = 0;
 249         final int METADATA_ARG_COUNT = 3;  // (lookup, name, type)
 250         final int metaArgc = passMetadata ? METADATA_ARG_COUNT : 0;
 251         if (passMetadata) {
 252             requireNonNull(name);
 253             requireNonNull(type);
 254         }
 255 
 256         MethodType handleType = handle.type();
 257         boolean handleVarargs = handle.isVarargsCollector();
 258 
 259         if (!passMetadata) {
 260             // Must not lead with a Lookup formal parameter type.
 261             // Lookup leading types are reserved for metadata-using BSMs.
 262             assert(handleType.parameterCount() == 0 ||
 263                    handleType.parameterType(0) != Lookup.class);
 264             assert(name == null);
 265             assert(type == null);
 266         } else if (isFieldType) {
 267             // With condy, we require an explicit leading type of Lookup,
 268             // if metadata is to be passed.
 269             assert(handleType.parameterCount() == 0 ||
 270                    handleType.parameterType(0) == Lookup.class);
 271             // Also, the caller should not try to handle a BSCI call here.
 272             assert(handleType.parameterCount() != 2 ||
 273                    handleType.parameterType(1) != BootstrapCallInfo.class);
 274             // Otherwise, we are doing an old-style indy bootstrap,
 275             // which tolerates Object in the metadata positions.
 276         }
 277 
 278         // Some varargs methods can be handled quickly here,
 279         // rather than going through more dynamic invocation paths.
 280         // Do the varargs spreading if:  the BSM is varargs, the BSM
 281         // directly accepts at least the metadata arguments (if applicable),
 282         // the given static arguments fill in the BSM's inital set
 283         // of arguments, and the BSM's trailing argument is a
 284         // reference array.  In that case, split the static argument
 285         // list into head and tail arrays, and link the latter onto the
 286         // end of the former.
 287         if (handleVarargs) {
 288             int headArgc = (handleType.parameterCount() - 1) - metaArgc;
 289             Class<?> tailType = handleType.lastParameterType();
 290             Class<?> tailElementType = tailType.getComponentType();
 291             if (headArgc >= 0 &&
 292                 headArgc <= argc &&
 293                 Object[].class.isAssignableFrom(tailType) &&
 294                 tailElementType != null) {
 295                 // Make a shortened argument list for a fixed-arity call.
 296                 Object[] headArgv = new Object[headArgc + 1];
 297                 if (argv != null) {
 298                     System.arraycopy(argv, 0, headArgv, 0, headArgc);
 299                 } else {
 300                     List<Object> headArgs = args.subList(0, headArgc);
 301                     headArgv = headArgs.toArray(headArgv);
 302                 }
 303                 // Collect the trailing arguments into an array of the
 304                 // required type.  Also, make sure they fit into the array.
 305                 int tailArgc = argc - headArgc;
 306                 Object[] tailArgv = (tailElementType == Object.class)
 307                         ? new Object[tailArgc]
 308                         : (Object[]) Array.newInstance(tailElementType, tailArgc);
 309                 try {
 310                     if (argv != null) {
 311                         System.arraycopy(argv, headArgc, tailArgv, 0, tailArgc);
 312                     } else {
 313                         List<Object> tailArgs = args.subList(headArgc, argc);
 314                         tailArgv = tailArgs.toArray(tailArgv);
 315                     }
 316                 } catch (ArrayStoreException ex) {
 317                     // Oops, one of the actuals doesn't fit in the varargs formal.
 318                     // For example, f(String...) is passed ("foo", 42, "bar").
 319                     tailArgv = null;  // bail out
 320                 }
 321                 if (tailArgv != null) {
 322                     // Link the head to the tail:
 323                     headArgv[headArgc] = tailArgv;
 324                     // Now swap in a new MH and arglist:
 325                     handle = handle.asFixedArity();
 326                     assert(handleType == handle.type());  // still true
 327                     handleVarargs = false;
 328                     argv = headArgv;
 329                     args = null;
 330                     argc = headArgc + 1;
 331                 }
 332             }
 333         }
 334 
 335         // Now try to make an inline call:
 336         if (argc <= MAX_ARITY && argv == null)
 337             argv = args.toArray();
 338 
 339         assert(argv == null || argc == argv.length);
 340         assert(args == null || argc == args.size());
 341 
 342         // If we don't provide static type information for the type,
 343         // we'll generate runtime checks.  The cases marked FT and MT
 344         // avoid that problem.
 345 
 346         switch (argc > MAX_ARITY ? -1 :
 347                 argc + (isFieldType ? FT : isMethodType ? MT : NMD)) {
 348             // Fixed-arity cases for a Class (field) type:
 349             case 0+FT:
 350                 return handle.invoke(lookup, name, (Class<?>)type);
 351             case 1+FT:
 352                 return handle.invoke(lookup, name, (Class<?>)type,
 353                                      argv[0]);
 354             case 2+FT:
 355                 return handle.invoke(lookup, name, (Class<?>)type,
 356                                      argv[0], argv[1]);
 357             case 3+FT:
 358                 if (handleType == LMF_CONDY_MT && !handleVarargs) {
 359                     return handle.invokeExact(lookup, name, (Class<?>)type,
 360                                               (MethodType)argv[0],
 361                                               (MethodHandle)argv[1],
 362                                               (MethodType)argv[2]);
 363                 }
 364                 return handle.invoke(lookup, name, (Class<?>)type,
 365                                      argv[0], argv[1], argv[2]);
 366             case 4+FT:
 367                 return handle.invoke(lookup, name, (Class<?>)type,
 368                                      argv[0], argv[1], argv[2], argv[3]);
 369             case 5+FT:
 370                 return handle.invoke(lookup, name, (Class<?>)type,
 371                                      argv[0], argv[1], argv[2], argv[3],
 372                                      argv[4]);
 373             case 6+FT:
 374                 return handle.invoke(lookup, name, (Class<?>)type,
 375                                      argv[0], argv[1], argv[2], argv[3],
 376                                      argv[4], argv[5]);
 377             case MAX_ARITY+FT:
 378                 return handle.invoke(lookup, name, (Class<?>)type,
 379                                      argv[0], argv[1], argv[2], argv[3],
 380                                      argv[4], argv[5], argv[6]);
 381 
 382             // Same cases, but for a MethodType argument:
 383             case 0+MT:
 384                 return handle.invoke(lookup, name, (MethodType)type);
 385             case 1+MT:
 386                 if (handleType == LMF_ALT_MT && !handleVarargs) {
 387                     return (CallSite)
 388                         handle.invokeExact(lookup, name, (MethodType)type,
 389                                            (Object[])argv[0]);
 390                 }
 391                 return handle.invoke(lookup, name, (MethodType)type,
 392                                      argv[0]);
 393             case 2+MT:
 394                 if (handleType == SCF_MT && !handleVarargs) {
 395                     return (CallSite)
 396                         handle.invokeExact(lookup, name, (MethodType)type,
 397                                            (String)argv[0],
 398                                            (Object[])argv[1]);
 399                 }
 400                 return handle.invoke(lookup, name, (MethodType)type,
 401                                      argv[0], argv[1]);
 402             case 3+MT:
 403                 if (handleType == LMF_INDY_MT && !handleVarargs) {
 404                     return (CallSite)
 405                         handle.invokeExact(lookup, name, (MethodType)type,
 406                                            (MethodType)argv[0],
 407                                            (MethodHandle)argv[1],
 408                                            (MethodType)argv[2]);
 409                 }
 410                 return handle.invoke(lookup, name, (MethodType)type,
 411                                      argv[0], argv[1], argv[2]);
 412             case 4+MT:
 413                 return handle.invoke(lookup, name, (MethodType)type,
 414                                      argv[0], argv[1], argv[2], argv[3]);
 415             case 5+MT:
 416                 return handle.invoke(lookup, name, (MethodType)type,
 417                                      argv[0], argv[1], argv[2], argv[3],
 418                                      argv[4]);
 419             case 6+MT:
 420                 return handle.invoke(lookup, name, (MethodType)type,
 421                                      argv[0], argv[1], argv[2], argv[3],
 422                                      argv[4], argv[5]);
 423             case MAX_ARITY+MT:
 424                 return handle.invoke(lookup, name, (MethodType)type,
 425                                      argv[0], argv[1], argv[2], argv[3],
 426                                      argv[4], argv[5], argv[6]);
 427 
 428             // Same cases again, but without metadata:
 429             case 0+NMD:
 430                 return handle.invoke();
 431             case 1+NMD:
 432                 return handle.invoke(argv[0]);
 433             case 2+NMD:
 434                 return handle.invoke(argv[0], argv[1]);
 435             case 3+NMD:
 436                 return handle.invoke(argv[0], argv[1], argv[2]);
 437             case 4+NMD:
 438                 return handle.invoke(argv[0], argv[1], argv[2], argv[3]);
 439             case 5+NMD:
 440                 return handle.invoke(argv[0], argv[1], argv[2], argv[3],
 441                                      argv[4]);
 442             case 6+NMD:
 443                 return handle.invoke(argv[0], argv[1], argv[2], argv[3],
 444                                      argv[4], argv[5]);
 445             case MAX_ARITY+NMD:
 446                 return handle.invoke(argv[0], argv[1], argv[2], argv[3],
 447                                      argv[4], argv[5], argv[6]);
 448         }
 449 
 450         // We have to stop at some point and use invokeWithArguments instead.
 451         // Note that invokeWithArguments handles jumbo argument lists.
 452 
 453         if (passMetadata) {
 454             Object[] allArgv = new Object[metaArgc + argc];
 455             int pos = 0;
 456             allArgv[pos++] = lookup;
 457             allArgv[pos++] = name;
 458             allArgv[pos++] = type;
 459             assert(pos == metaArgc);
 460             if (argv != null) {
 461                 System.arraycopy(argv, 0, allArgv, pos, argc);
 462                 pos += argc;
 463             } else {
 464                 for (Object arg : args) {
 465                     allArgv[pos++] = arg;
 466                 }
 467             }
 468             assert(pos == allArgv.length);
 469             return handle.invokeWithArguments(allArgv);
 470         } else if (argv != null) {
 471             // It's simple if there are no metadata arguments to prepend.
 472             return handle.invokeWithArguments(argv);
 473         } else {
 474             return handle.invokeWithArguments(args);
 475         }
 476     }
 477 
 478     /** Canonical VM-aware implementation of BootstrapCallInfo.
 479      * Knows how to dig into the JVM for lazily resolved (pull-mode) constants.
 480      */
 481     static final class VM_BSCI<T extends TypeDescriptor & Constable<T>> extends WithCache<T> {
 482         private final int bssIndex;
 483         private final int indyIndex;
 484         private final ConstantPool pool;
 485         private int[] argIndexes;  // lazy
 486 
 487         public VM_BSCI(ConstantPool pool, int bssIndex, int indyIndex,
 488                        MethodHandle bsm, String name, TypeView<T> type, Object[] arguments) {
 489             super(bsm, name, type, arguments);
 490             this.pool = pool;
 491             this.bssIndex = bssIndex;
 492             this.indyIndex = indyIndex;
 493         }
 494 
 495         /** Special VM-specific hook to resolve static arguments. */
 496         @Override Object resolveConstant(int i) {
 497             Object[] buf = { null };
 498             copyVMArguments(i, i+1, buf, 0, BAR_FORCE, null);
 499             Object res = buf[0];
 500             int next = i + 1;
 501             if (next < cache.length && cache[next] == null)
 502                 maybePrefetchIntoCache(cache, next, false);  // try to prefetch
 503             return res;
 504         }
 505 
 506         /** Special VM-specific hook to find symbolic references. */
 507         @Override ConstantDesc<?> findSymbol(int i) {
 508             Object[] buf = { null };
 509             copyVMArguments(i, i+1, buf, 0, BAR_SYMREF, null);
 510             ConstantDesc<?> res = (ConstantDesc<?>) buf[0];
 511             //int next = i + 1;
 512             //if (next < symCache.length && symCache[next] == null)
 513             //    maybePrefetchIntoCache(symCache, next, false);  // try to prefetch
 514             return res;
 515         }
 516 
 517         private Class<?> caller() { return pool.getHolder(); }
 518 
 519         /*non-public contract with ArgList*/
 520         void copyVMArguments(int start, int end,
 521                              Object[] buf, int pos,
 522                              byte resolving, Object ifNotPresent) {
 523             if (buf.getClass() != Object[].class)  throw new InternalError();
 524             int i = start, bufi = pos;
 525             if (resolving == BAR_SYMREF) {
 526                 while (i < end) {
 527                     Object x = symCache[i];
 528                     if (x == null)  break;
 529                     buf[bufi++] = x;
 530                     i++;
 531                 }
 532                 if (i >= end)  return;
 533                 // pull in all of the symbols into symCache
 534                 int[] temp = new int[end - i];
 535                 int tempi = 0;
 536                 pool.copyOutBootstrapArgumentsAt(bssIndex,
 537                         start, end, temp, 0,
 538                         BAR_SYMREF, null, null, false);
 539                 for (; i < end; i++) {
 540                     ConstantDesc<?> x = symCache[i];
 541                     int symIndex = temp[tempi++];
 542                     if (x == null) {
 543                         x = makeConstantFromPool(symIndex);
 544                         symCache[i] = x;
 545                     }
 546                     buf[bufi++] = x;
 547                 }
 548                 return;
 549             }
 550             assert(resolving == BAR_IFPRESENT || resolving == BAR_FORCE);
 551             for (; i < end; i++) {
 552                 Object x = cache[i];
 553                 if (x == null)  break;
 554                 buf[bufi++] = unwrapNull(x);
 555             }
 556             // give up at first null and grab the rest in one big block
 557             if (i >= end)  return;
 558             if (TRACE_METHOD_LINKAGE) {
 559                 System.out.println("resolving more BSM arguments: " +
 560                         Arrays.asList(caller().getSimpleName(), bssIndex, indyIndex, i, end));
 561             }
 562             pool.copyOutBootstrapArgumentsAt(bssIndex,
 563                     i, end, cache, i,
 564                     resolving, null, wrapNull(null), true);
 565             for (; i < end; i++) {
 566                 Object x = cache[i];
 567                 Object x2 = maybeReBox(x);
 568                 if (x2 != x)  cache[i] = x = x2;
 569                 buf[bufi++] = (x == null) ? ifNotPresent : unwrapNull(x);
 570             }
 571             if (end < cache.length && cache[end] == null)
 572                 maybePrefetchIntoCache(cache, end, true);  // try to prefetch
 573         }
 574 
 575         private ConstantDesc<?> makeConstantFromPool(int symIndex) {
 576             return pool.getConstantDescAt(symIndex);
 577         }
 578 
 579         private static final int MIN_PF = 4;
 580         private void maybePrefetchIntoCache(Object[] cache, int i, boolean bulk) {
 581             int len = cache.length;
 582             assert(0 <= i && i <= len);
 583             int pfLimit = i;
 584             if (bulk)  pfLimit += i;  // exponential prefetch expansion
 585             // try to prefetch at least MIN_PF elements
 586             if (pfLimit < i + MIN_PF)  pfLimit = i + MIN_PF;
 587             if (pfLimit > len || pfLimit < 0)  pfLimit = len;
 588             // stop prefetching where cache is more full than empty
 589             int empty = 0, nonEmpty = 0, lastEmpty = i;
 590             for (int j = i; j < pfLimit; j++) {
 591                 if (cache[j] == null) {
 592                     empty++;
 593                     lastEmpty = j;
 594                 } else {
 595                     nonEmpty++;
 596                     if (nonEmpty > empty) {
 597                         pfLimit = lastEmpty + 1;
 598                         break;
 599                     }
 600                     if (pfLimit < len)  pfLimit++;
 601                 }
 602             }
 603             if (bulk && empty < MIN_PF && pfLimit < len)
 604                 return;  // not worth the effort
 605             prefetchIntoCache(cache, i, pfLimit);
 606         }
 607 
 608         private void prefetchIntoCache(Object[] cache, int i, int pfLimit) {
 609             if (pfLimit <= i)  return;  // corner case
 610             if (TRACE_METHOD_LINKAGE) {
 611                 System.out.println("prefetching BSM arguments: " +
 612                         Arrays.asList(caller().getSimpleName(), bssIndex, indyIndex, i, pfLimit));
 613             }
 614             pool.copyOutBootstrapArgumentsAt(bssIndex,
 615                                              i, pfLimit, cache, i,
 616                                              BAR_IFPRESENT, null, wrapNull(null), true);
 617         }
 618 
 619         void setArgIndexes(int[] argIndexes) {
 620             assert(this.argIndexes == null);
 621             this.argIndexes = argIndexes;
 622         }
 623     }
 624 
 625     /** Canonical DynamicConstantDesc implementation of BootstrapCallInfo.
 626      */
 627     static final class DCD_BSCI extends WithCache<Class<?>> {
 628         private Lookup lookup;
 629         private DynamicConstantDesc<?> desc;
 630         private final List<ConstantDesc<?>> args;
 631 
 632         public DCD_BSCI(Lookup lookup, DynamicConstantDesc<?> desc) throws ReflectiveOperationException {
 633             super(desc.bootstrapMethod().resolveConstantDesc(lookup),
 634                   desc.constantName(),
 635                   new TypeView<Class<?>>(desc.constantType(), lookup),
 636                   desc.bootstrapArgs());
 637             this.lookup = lookup;
 638             this.desc = desc;
 639             this.args = desc.bootstrapArgsList();
 640         }
 641 
 642         @Override Object resolveConstant(int i) throws BootstrapMethodError {
 643             try {
 644                 return argumentDesc(i).resolveConstantDesc(lookup);
 645             } catch (ReflectiveOperationException ex) {
 646                 throw new BootstrapMethodError(ex);
 647             }
 648         }
 649 
 650         @Override
 651         ConstantDesc<?> findSymbol(int i) {
 652             return args.get(i);
 653         }
 654     }
 655 
 656     /**
 657      * Create a BootstrapCallInfo whose symbolic content is derived from the given
 658      * descriptor.  When it resolves constants, it will use the given Lookup object.
 659      * @param desc the dynamic constant descriptor
 660      * @param lookup a lookup object which is capable of resolving the dynamic constant
 661      * @return a BootstrapCallInfo which carries the given
 662      * @throws ReflectiveOperationException if the bootstrap method fails to resolve
 663      */
 664     static BootstrapCallInfo<Class<?>> ofConstantDesc(DynamicConstantDesc<?> desc, Lookup lookup)
 665                 throws ReflectiveOperationException {
 666         return new BootstrapMethodInvoker.DCD_BSCI(lookup, desc);
 667     }
 668 
 669     /**
 670      * Resolve a dynamic constant, by creating a temporary BootstrapCallInfo for it
 671      * and immediately invoking its resolution behavior.  Any cached resolution state
 672      * will be lost after the temporary BootstrapCallInfo is discarded.
 673      * @param desc the dynamic constant descriptor
 674      * @param lookup a lookup object which is capable of resolving the dynamic constant
 675      * @return the result of resolving the descriptor in the lookup class
 676      * @throws ReflectiveOperationException
 677      */
 678     static Object resolveDynamicConstant(DynamicConstantDesc<?> desc, Lookup lookup)
 679             throws ReflectiveOperationException {
 680         try {
 681             return invoke(lookup, ofConstantDesc(desc, lookup));
 682         } catch (LinkageError ex) {
 683             var roe = ex.getCause();
 684             if (roe instanceof ReflectiveOperationException)
 685                 throw (ReflectiveOperationException) roe;
 686             throw ex;
 687         }
 688     }
 689 }