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