1 /*
   2  * Copyright (c) 2017, 2018, 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 sun.invoke.util.Wrapper;
  28 
  29 import java.lang.invoke.AbstractConstantGroup.BSCIWithCache;
  30 import java.util.Arrays;
  31 
  32 import static java.lang.invoke.BootstrapCallInfo.makeBootstrapCallInfo;
  33 import static java.lang.invoke.ConstantGroup.makeConstantGroup;
  34 import static java.lang.invoke.MethodHandleNatives.*;
  35 import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
  36 import static java.lang.invoke.MethodHandles.Lookup;
  37 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
  38 
  39 final class BootstrapMethodInvoker {
  40 
  41     /**
  42      * Factored code for invoking a bootstrap method for invokedynamic
  43      * or a dynamic constant.
  44      * @param resultType the expected return type (either CallSite or a constant type)
  45      * @param bootstrapMethod the BSM to call
  46      * @param name the method name or constant name
  47      * @param type the method type or constant type
  48      * @param info information passed up from the JVM, to derive static arguments
  49      * @param callerClass the class containing the resolved method call or constant load
  50      * @param includeMetadata true if the lookup, name and type metadata arguments should
  51      *                        be included when invoking the BSM
  52      * @param <T> the expected return type
  53      * @return the expected value, either a CallSite or a constant value
  54      */
  55     static <T> T invoke(Class<T> resultType,
  56                         MethodHandle bootstrapMethod,
  57                         // Callee information:
  58                         String name, Object type,
  59                         // Extra arguments for BSM, if any:
  60                         Object info,
  61                         // Caller information:
  62                         Class<?> callerClass,
  63                         boolean includeMetadata) {
  64         MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
  65         MethodType bsmType = bootstrapMethod.type();
  66         Object result;
  67         boolean pullMode = isPullModeBSM(bootstrapMethod);  // default value is false
  68         boolean vmIsPushing = !staticArgumentsPulled(info); // default value is true
  69         MethodHandle pullModeBSM;
  70 
  71         // match the VM with the BSM
  72         if (vmIsPushing) {
  73             // VM is pushing arguments at us
  74             // Need to transform if pull-mode BSM
  75             pullModeBSM = null;
  76             if (pullMode) {
  77                 bootstrapMethod = PushAdapter.toPullBootstrapMethod(bootstrapMethod);
  78                 bsmType = bootstrapMethod.type();
  79             }
  80         }
  81         else {
  82             // VM wants us to pull args from it
  83             // Need to transform if push-mode BSM
  84             pullModeBSM = pullMode ? bootstrapMethod :
  85                     PullAdapter.toPushBootstrapMethod(bootstrapMethod, includeMetadata);
  86         }
  87         try {
  88             // As an optimization we special case various known BSMs,
  89             // such as LambdaMetafactory::metafactory and
  90             // StringConcatFactory::makeConcatWithConstants.
  91             //
  92             // By providing static type information or even invoking
  93             // exactly, we avoid emitting code to perform runtime
  94             // checking.
  95             info = maybeReBox(info);
  96             if (info == null) {
  97                 if (includeMetadata) {
  98                     // VM is allowed to pass up a null meaning no BSM args
  99                     result = invoke(bootstrapMethod, caller, name, type);
 100                 }
 101                 else {
 102                     result = bootstrapMethod.invoke();
 103                 }
 104             }
 105             else if (!info.getClass().isArray()) {
 106                 // VM is allowed to pass up a single BSM arg directly
 107 
 108                 // Call to StringConcatFactory::makeConcatWithConstants
 109                 // with empty constant arguments?
 110                 if (isStringConcatFactoryBSM(bsmType)) {
 111                     result = (CallSite)bootstrapMethod
 112                             .invokeExact(caller, name, (MethodType)type,
 113                                          (String)info, new Object[0]);
 114                 }
 115                 else if (includeMetadata) {
 116                     result = invoke(bootstrapMethod, caller, name, type, info);
 117                 }
 118                 else {
 119                     result = bootstrapMethod.invoke(info);
 120                 }
 121             }
 122             else if (info.getClass() == int[].class) {
 123                 // VM is allowed to pass up a pair {argc, index}
 124                 // referring to 'argc' BSM args at some place 'index'
 125                 // in the guts of the VM (associated with callerClass).
 126                 // The format of this index pair is private to the
 127                 // handshake between the VM and this class only.
 128                 // This supports "pulling" of arguments.
 129                 // The VM is allowed to do this for any reason.
 130                 // The code in this method makes up for any mismatches.
 131                 BootstrapCallInfo<Object> bsci
 132                     = new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info);
 133                 if (includeMetadata) {
 134                     // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
 135                     result = pullModeBSM.invoke(caller, bsci);
 136                 }
 137                 else {
 138                     // Pull-mode API is (BootstrapCallInfo) -> Object
 139                     result = pullModeBSM.invoke(bsci);
 140                 }
 141             }
 142             else {
 143                 // VM is allowed to pass up a full array of resolved BSM args
 144                 Object[] argv = (Object[]) info;
 145                 maybeReBoxElements(argv);
 146 
 147                 if (isLambdaMetafactoryIndyBSM(bsmType) && argv.length == 3) {
 148                     result = (CallSite)bootstrapMethod
 149                             .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
 150                                     (MethodHandle)argv[1], (MethodType)argv[2]);
 151                 }
 152                 else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) {
 153                     result = bootstrapMethod
 154                             .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
 155                                     (MethodHandle)argv[1], (MethodType)argv[2]);
 156                 }
 157                 else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) {
 158                     String recipe = (String)argv[0];
 159                     Object[] shiftedArgs = Arrays.copyOfRange(argv, 1, argv.length);
 160                     result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, recipe, shiftedArgs);
 161                 }
 162                 else if (isLambdaMetafactoryAltMetafactoryBSM(bsmType)) {
 163                     result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, argv);
 164                 }
 165                 else if (includeMetadata) {
 166                     switch (argv.length) {
 167                         case 0:
 168                             result = invoke(bootstrapMethod, caller, name, type);
 169                             break;
 170                         case 1:
 171                             result = invoke(bootstrapMethod, caller, name, type,
 172                                             argv[0]);
 173                             break;
 174                         case 2:
 175                             result = invoke(bootstrapMethod, caller, name, type,
 176                                             argv[0], argv[1]);
 177                             break;
 178                         case 3:
 179                             result = invoke(bootstrapMethod, caller, name, type,
 180                                             argv[0], argv[1], argv[2]);
 181                             break;
 182                         case 4:
 183                             result = invoke(bootstrapMethod, caller, name, type,
 184                                             argv[0], argv[1], argv[2], argv[3]);
 185                             break;
 186                         case 5:
 187                             result = invoke(bootstrapMethod, caller, name, type,
 188                                             argv[0], argv[1], argv[2], argv[3], argv[4]);
 189                             break;
 190                         case 6:
 191                             result = invoke(bootstrapMethod, caller, name, type,
 192                                             argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
 193                             break;
 194                         default:
 195                             result = invokeWithManyArguments(bootstrapMethod, caller, name, type, argv);
 196                     }
 197                 }
 198                 else {
 199                     switch (argv.length) {
 200                         case 0:
 201                             result = bootstrapMethod.invoke();
 202                             break;
 203                         case 1:
 204                             result = bootstrapMethod.invoke(
 205                                             argv[0]);
 206                             break;
 207                         case 2:
 208                             result = bootstrapMethod.invoke(
 209                                             argv[0], argv[1]);
 210                             break;
 211                         case 3:
 212                             result = bootstrapMethod.invoke(
 213                                             argv[0], argv[1], argv[2]);
 214                             break;
 215                         case 4:
 216                             result = bootstrapMethod.invoke(
 217                                             argv[0], argv[1], argv[2], argv[3]);
 218                             break;
 219                         case 5:
 220                             result = bootstrapMethod.invoke(
 221                                             argv[0], argv[1], argv[2], argv[3], argv[4]);
 222                             break;
 223                         case 6:
 224                             result = bootstrapMethod.invoke(
 225                                             argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
 226                             break;
 227                         default:
 228                             result = invokeWithManyArguments(bootstrapMethod, argv);
 229                     }
 230                 }
 231             }
 232             if (resultType.isPrimitive()) {
 233                 // Non-reference conversions are more than just plain casts.
 234                 // By pushing the value through a funnel of the form (T x)->x,
 235                 // the boxed result can be widened as needed.  See MH::asType.
 236                 MethodHandle funnel = MethodHandles.identity(resultType);
 237                 result = funnel.invoke(result);
 238                 // Now it is the wrapper type for resultType.
 239                 resultType = Wrapper.asWrapperType(resultType);
 240             }
 241             return resultType.cast(result);
 242         }
 243         catch (Error e) {
 244             // Pass through an Error, including BootstrapMethodError, any other
 245             // form of linkage error, such as IllegalAccessError if the bootstrap
 246             // method is inaccessible, or say ThreadDeath/OutOfMemoryError
 247             // See the "Linking Exceptions" section for the invokedynamic
 248             // instruction in JVMS 6.5.
 249             throw e;
 250         }
 251         catch (Throwable ex) {
 252             // Wrap anything else in BootstrapMethodError
 253             throw new BootstrapMethodError("bootstrap method initialization exception", ex);
 254         }
 255     }
 256 
 257     // If we don't provide static type information for type, we'll generate runtime
 258     // checks. Let's try not to...
 259 
 260     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
 261                                  String name, Object type) throws Throwable {
 262         if (type instanceof Class) {
 263             return bootstrapMethod.invoke(caller, name, (Class<?>)type);
 264         }
 265         else {
 266             return bootstrapMethod.invoke(caller, name, (MethodType)type);
 267         }
 268     }
 269 
 270     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
 271                                  String name, Object type, Object arg0) throws Throwable {
 272         if (type instanceof Class) {
 273             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0);
 274         }
 275         else {
 276             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0);
 277         }
 278     }
 279 
 280     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
 281                                  Object type, Object arg0, Object arg1) throws Throwable {
 282         if (type instanceof Class) {
 283             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1);
 284         }
 285         else {
 286             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1);
 287         }
 288     }
 289 
 290     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
 291                                  Object type, Object arg0, Object arg1,
 292                                  Object arg2) throws Throwable {
 293         if (type instanceof Class) {
 294             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2);
 295         }
 296         else {
 297             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2);
 298         }
 299     }
 300 
 301     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
 302                                  Object type, Object arg0, Object arg1,
 303                                  Object arg2, Object arg3) throws Throwable {
 304         if (type instanceof Class) {
 305             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3);
 306         }
 307         else {
 308             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3);
 309         }
 310     }
 311 
 312     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
 313                                  String name, Object type, Object arg0, Object arg1,
 314                                  Object arg2, Object arg3, Object arg4) throws Throwable {
 315         if (type instanceof Class) {
 316             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4);
 317         }
 318         else {
 319             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4);
 320         }
 321     }
 322 
 323     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
 324                                  String name, Object type, Object arg0, Object arg1,
 325                                  Object arg2, Object arg3, Object arg4, Object arg5) throws Throwable {
 326         if (type instanceof Class) {
 327             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4, arg5);
 328         } else {
 329             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4, arg5);
 330         }
 331     }
 332 
 333     private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Lookup caller,
 334                                                   String name, Object type, Object[] argv) throws Throwable {
 335         final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
 336         final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
 337         if (argv.length >= MAX_SAFE_SIZE) {
 338             // to be on the safe side, use invokeWithArguments which handles jumbo lists
 339             Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
 340             newargv[0] = caller;
 341             newargv[1] = name;
 342             newargv[2] = type;
 343             System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
 344             return bootstrapMethod.invokeWithArguments(newargv);
 345         }
 346         else {
 347             MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
 348             MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
 349             MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
 350             return spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
 351         }
 352     }
 353 
 354     private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Object[] argv) throws Throwable {
 355         final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2;
 356         if (argv.length >= MAX_SAFE_SIZE) {
 357             // to be on the safe side, use invokeWithArguments which handles jumbo lists
 358             return bootstrapMethod.invokeWithArguments(argv);
 359         }
 360         else {
 361             MethodType invocationType = MethodType.genericMethodType(argv.length);
 362             MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
 363             MethodHandle spreader = invocationType.invokers().spreadInvoker(0);
 364             return spreader.invokeExact(typedBSM, argv);
 365         }
 366     }
 367 
 368     private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
 369             Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
 370 
 371     private static final MethodType LMF_ALT_MT = MethodType.methodType(CallSite.class,
 372             Lookup.class, String.class, MethodType.class, Object[].class);
 373 
 374     private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class,
 375             Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class);
 376 
 377     private static final MethodType SCF_MT = MethodType.methodType(CallSite.class,
 378             Lookup.class, String.class, MethodType.class, String.class, Object[].class);
 379 
 380     /**
 381      * @return true iff the BSM method type exactly matches
 382      *         {@see java.lang.invoke.StringConcatFactory#makeConcatWithConstants(MethodHandles.Lookup,
 383      *                 String,MethodType,String,Object...))}
 384      */
 385     private static boolean isStringConcatFactoryBSM(MethodType bsmType) {
 386         return bsmType == SCF_MT;
 387     }
 388 
 389     /**
 390      * @return true iff the BSM method type exactly matches
 391      *         {@see java.lang.invoke.LambdaMetafactory#metafactory(
 392      *          MethodHandles.Lookup,String,Class,MethodType,MethodHandle,MethodType)}
 393      */
 394     private static boolean isLambdaMetafactoryCondyBSM(MethodType bsmType) {
 395         return bsmType == LMF_CONDY_MT;
 396     }
 397 
 398     /**
 399      * @return true iff the BSM method type exactly matches
 400      *         {@see java.lang.invoke.LambdaMetafactory#metafactory(
 401      *          MethodHandles.Lookup,String,MethodType,MethodType,MethodHandle,MethodType)}
 402      */
 403     private static boolean isLambdaMetafactoryIndyBSM(MethodType bsmType) {
 404         return bsmType == LMF_INDY_MT;
 405     }
 406 
 407     /**
 408      * @return true iff the BSM method type exactly matches
 409      *         {@see java.lang.invoke.LambdaMetafactory#altMetafactory(
 410      *          MethodHandles.Lookup,String,MethodType,Object[])}
 411      */
 412     private static boolean isLambdaMetafactoryAltMetafactoryBSM(MethodType bsmType) {
 413         return bsmType == LMF_ALT_MT;
 414     }
 415 
 416     /** The JVM produces java.lang.Integer values to box
 417      *  CONSTANT_Integer boxes but does not intern them.
 418      *  Let's intern them.  This is slightly wrong for
 419      *  a {@code CONSTANT_Dynamic} which produces an
 420      *  un-interned integer (e.g., {@code new Integer(0)}).
 421      */
 422     private static Object maybeReBox(Object x) {
 423         if (x instanceof Integer) {
 424             int xi = (int) x;
 425             if (xi == (byte) xi)
 426                 x = xi;  // must rebox; see JLS 5.1.7
 427         }
 428         return x;
 429     }
 430 
 431     private static void maybeReBoxElements(Object[] xa) {
 432         for (int i = 0; i < xa.length; i++) {
 433             xa[i] = maybeReBox(xa[i]);
 434         }
 435     }
 436 
 437     /** Canonical VM-aware implementation of BootstrapCallInfo.
 438      * Knows how to dig into the JVM for lazily resolved (pull-mode) constants.
 439      */
 440     private static final class VM_BSCI<T> extends BSCIWithCache<T> {
 441         private final int[] indexInfo;
 442         private final Class<?> caller;  // for index resolution only
 443 
 444         VM_BSCI(MethodHandle bsm, String name, T type,
 445                 Lookup lookup, int[] indexInfo) {
 446             super(bsm, name, type, indexInfo[0]);
 447             if (!lookup.hasPrivateAccess())  //D.I.D.
 448                 throw new AssertionError("bad Lookup object");
 449             this.caller = lookup.lookupClass();
 450             this.indexInfo = indexInfo;
 451             // scoop up all the easy stuff right away:
 452             prefetchIntoCache(0, size());
 453         }
 454 
 455         @Override Object fillCache(int i) {
 456             Object[] buf = { null };
 457             copyArguments(i, i+1, buf, 0);
 458             Object res = wrapNull(buf[0]);
 459             cache[i] = res;
 460             int next = i + 1;
 461             if (next < cache.length && cache[next] == null)
 462                 maybePrefetchIntoCache(next, false);  // try to prefetch
 463             return res;
 464         }
 465 
 466         @Override public int copyArguments(int start, int end,
 467                                            Object[] buf, int pos) {
 468             int i = start, bufi = pos;
 469             while (i < end) {
 470                 Object x = cache[i];
 471                 if (x == null)  break;
 472                 buf[bufi++] = unwrapNull(x);
 473                 i++;
 474             }
 475             // give up at first null and grab the rest in one big block
 476             if (i >= end)  return i;
 477             Object[] temp = new Object[end - i];
 478             if (TRACE_METHOD_LINKAGE) {
 479                 System.out.println("resolving more BSM arguments: " +
 480                         Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end));
 481             }
 482             copyOutBootstrapArguments(caller, indexInfo,
 483                                       i, end, temp, 0,
 484                                       true, null);
 485             for (Object x : temp) {
 486                 x = maybeReBox(x);
 487                 buf[bufi++] = x;
 488                 cache[i++] = wrapNull(x);
 489             }
 490             if (end < cache.length && cache[end] == null)
 491                 maybePrefetchIntoCache(end, true);  // try to prefetch
 492             return i;
 493         }
 494 
 495         private static final int MIN_PF = 4;
 496         private void maybePrefetchIntoCache(int i, boolean bulk) {
 497             int len = cache.length;
 498             assert(0 <= i && i <= len);
 499             int pfLimit = i;
 500             if (bulk)  pfLimit += i;  // exponential prefetch expansion
 501             // try to prefetch at least MIN_PF elements
 502             if (pfLimit < i + MIN_PF)  pfLimit = i + MIN_PF;
 503             if (pfLimit > len || pfLimit < 0)  pfLimit = len;
 504             // stop prefetching where cache is more full than empty
 505             int empty = 0, nonEmpty = 0, lastEmpty = i;
 506             for (int j = i; j < pfLimit; j++) {
 507                 if (cache[j] == null) {
 508                     empty++;
 509                     lastEmpty = j;
 510                 } else {
 511                     nonEmpty++;
 512                     if (nonEmpty > empty) {
 513                         pfLimit = lastEmpty + 1;
 514                         break;
 515                     }
 516                     if (pfLimit < len)  pfLimit++;
 517                 }
 518             }
 519             if (bulk && empty < MIN_PF && pfLimit < len)
 520                 return;  // not worth the effort
 521             prefetchIntoCache(i, pfLimit);
 522         }
 523 
 524         private void prefetchIntoCache(int i, int pfLimit) {
 525             if (pfLimit <= i)  return;  // corner case
 526             Object[] temp = new Object[pfLimit - i];
 527             if (TRACE_METHOD_LINKAGE) {
 528                 System.out.println("prefetching BSM arguments: " +
 529                         Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit));
 530             }
 531             copyOutBootstrapArguments(caller, indexInfo,
 532                                       i, pfLimit, temp, 0,
 533                                       false, NOT_PRESENT);
 534             for (Object x : temp) {
 535                 if (x != NOT_PRESENT && cache[i] == null) {
 536                     cache[i] = wrapNull(maybeReBox(x));
 537                 }
 538                 i++;
 539             }
 540         }
 541     }
 542 
 543     /*non-public*/ static final
 544     class PushAdapter {
 545         static final MethodHandle MH_pushToBootstrapMethod;
 546         static {
 547             try {
 548                 MH_pushToBootstrapMethod = IMPL_LOOKUP
 549                         .findStatic(BootstrapCallInfo.class, "invokeFromArgumentsToCallInfo",
 550                                 MethodType.methodType(Object.class, MethodHandle.class,
 551                                         Lookup.class, String.class, Object.class, Object[].class));
 552             }
 553             catch (Throwable ex) {
 554                 throw new InternalError(ex);
 555             }
 556         }
 557 
 558         /**
 559          * Given a push-mode BSM (taking one BSCI argument) convert it to a
 560          * pull-mode BSM (taking N pre-resolved arguments).  This method is used
 561          * when the JVM is passing up pre-resolved arguments, but the BSM is
 562          * expecting to BSCI to resolve arguments lazily.
 563          */
 564         static MethodHandle toPullBootstrapMethod(MethodHandle bsm) {
 565             if (TRACE_METHOD_LINKAGE) {
 566                 System.out.println("converting a push-mode BSM of type " + bsm.type() + " to pull-mode");
 567             }
 568             return MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
 569         }
 570     }
 571 
 572     /*non-public*/ static final
 573     class PullAdapter {
 574         // skeleton for pull-mode BSM which wraps a push-mode BSM:
 575         static Object pullFromBootstrapMethod(MethodHandle pushModeBSM,
 576                                               MethodHandles.Lookup lookup,
 577                                               BootstrapCallInfo<?> bsci)
 578                 throws Throwable {
 579             int argc = bsci.size();
 580             switch (argc) {
 581                 case 0:
 582                     return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType());
 583                 case 1:
 584                     return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 585                             bsci.get(0));
 586                 case 2:
 587                     return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 588                             bsci.get(0), bsci.get(1));
 589                 case 3:
 590                     return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 591                             bsci.get(0), bsci.get(1), bsci.get(2));
 592                 case 4:
 593                     return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 594                             bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3));
 595                 case 5:
 596                     return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 597                             bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4));
 598                 case 6:
 599                     return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
 600                             bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5));
 601                 default:
 602                     final int NON_SPREAD_ARG_COUNT = 3;  // (lookup, name, type)
 603                     final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
 604                     if (argc >= MAX_SAFE_SIZE) {
 605                         // to be on the safe side, use invokeWithArguments which handles jumbo lists
 606                         Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argc];
 607                         newargv[0] = lookup;
 608                         newargv[1] = bsci.invocationName();
 609                         newargv[2] = bsci.invocationType();
 610                         bsci.copyConstants(0, argc, newargv, NON_SPREAD_ARG_COUNT);
 611                         return pushModeBSM.invokeWithArguments(newargv);
 612                     }
 613                     MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argc);
 614                     MethodHandle typedBSM = pushModeBSM.asType(invocationType);
 615                     MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
 616                     Object[] argv = new Object[argc];
 617                     bsci.copyConstants(0, argc, argv, 0);
 618                     return spreader.invokeExact(typedBSM, (Object) lookup, (Object) bsci.invocationName(), bsci.invocationType(), argv);
 619                 }
 620         }
 621 
 622         static Object pullFromBootstrapMethodExclude(MethodHandle pushModeBSM,
 623                                                      BootstrapCallInfo<?> bsci)
 624                 throws Throwable {
 625             int argc = bsci.size();
 626             switch (argc) {
 627                 case 0:
 628                     return pushModeBSM.invoke();
 629                 case 1:
 630                     return pushModeBSM.invoke(bsci.get(0));
 631                 case 2:
 632                     return pushModeBSM.invoke(bsci.get(0), bsci.get(1));
 633                 case 3:
 634                     return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2));
 635                 case 4:
 636                     return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3));
 637                 case 5:
 638                     return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4));
 639                 case 6:
 640                     return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5));
 641                 default:
 642                     final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2;
 643                     if (argc >= MAX_SAFE_SIZE) {
 644                         // to be on the safe side, use invokeWithArguments which handles jumbo lists
 645                         Object[] argv = new Object[argc];
 646                         bsci.copyConstants(0, argc, argv, 0);
 647                         return pushModeBSM.invokeWithArguments(argv);
 648                     }
 649                     MethodType invocationType = MethodType.genericMethodType(argc);
 650                     MethodHandle typedBSM = pushModeBSM.asType(invocationType);
 651                     MethodHandle spreader = invocationType.invokers().spreadInvoker(0);
 652                     Object[] argv = new Object[argc];
 653                     bsci.copyConstants(0, argc, argv, 0);
 654                     return spreader.invokeExact(typedBSM, argv);
 655             }
 656         }
 657 
 658         static final MethodHandle MH_pullFromBootstrapMethod;
 659 
 660         static final MethodHandle MH_pullFromBootstrapMethodExclude;
 661 
 662         static {
 663             final Class<?> THIS_CLASS = PullAdapter.class;
 664             try {
 665                 MH_pullFromBootstrapMethod = IMPL_LOOKUP
 666                     .findStatic(THIS_CLASS, "pullFromBootstrapMethod",
 667                                 MethodType.methodType(Object.class, MethodHandle.class,
 668                                         Lookup.class, BootstrapCallInfo.class));
 669 
 670                 MH_pullFromBootstrapMethodExclude = IMPL_LOOKUP
 671                         .findStatic(THIS_CLASS, "pullFromBootstrapMethodExclude",
 672                                     MethodType.methodType(Object.class, MethodHandle.class,
 673                                                           BootstrapCallInfo.class));
 674             }
 675             catch (Throwable ex) {
 676                 throw new InternalError(ex);
 677             }
 678         }
 679 
 680         /**
 681          * Given a pull-mode BSM (taking N pre-resolved arguments) convert it to
 682          * a push-mode BSM (taking one BSCI argument).  This method is used when
 683          * the JVM is passing up indexes to constant pool entries (possibly
 684          * unresolved), but the BSM is expecting pre-resolved arguments.
 685          */
 686         static MethodHandle toPushBootstrapMethod(MethodHandle bsm, boolean includeMetaData) {
 687             if (TRACE_METHOD_LINKAGE) {
 688                 System.out.println("converting a pull-mode BSM of type " + bsm.type() + " to push-mode"
 689                                    + (!includeMetaData ? ", exluding meta-data" : ""));
 690             }
 691             if (includeMetaData) {
 692                 return PullAdapter.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
 693             }
 694             else {
 695                 return PullAdapter.MH_pullFromBootstrapMethodExclude.bindTo(bsm).withVarargs(false);
 696             }
 697         }
 698     }
 699 }