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      */
invoke(Class<T> resultType, MethodHandle bootstrapMethod, String name, Object type, Object info, Class<?> callerClass)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 
invoke(MethodHandle bootstrapMethod, Lookup caller, String name, Object type)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 
invoke(MethodHandle bootstrapMethod, Lookup caller, String name, Object type, Object arg0)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 
invoke(MethodHandle bootstrapMethod, Lookup caller, String name, Object type, Object arg0, Object arg1)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 
invoke(MethodHandle bootstrapMethod, Lookup caller, String name, Object type, Object arg0, Object arg1, Object arg2)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 
invoke(MethodHandle bootstrapMethod, Lookup caller, String name, Object type, Object arg0, Object arg1, Object arg2, Object arg3)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 
invoke(MethodHandle bootstrapMethod, Lookup caller, String name, Object type, Object arg0, Object arg1, Object arg2, Object arg3, Object arg4)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 
invoke(MethodHandle bootstrapMethod, Lookup caller, String name, Object type, Object arg0, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)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 
invokeWithManyArguments(MethodHandle bootstrapMethod, Lookup caller, String name, Object type, Object[] argv)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      */
isStringConcatFactoryBSM(MethodType bsmType)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      */
isLambdaMetafactoryCondyBSM(MethodType bsmType)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      */
isLambdaMetafactoryIndyBSM(MethodType bsmType)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      */
isLambdaMetafactoryAltMetafactoryBSM(MethodType bsmType)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      */
maybeReBox(Object x)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 
maybeReBoxElements(Object[] xa)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 
VM_BSCI(MethodHandle bsm, String name, T type, Lookup lookup, int[] indexInfo)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 
fillCache(int i)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 
copyConstants(int start, int end, Object[] buf, int pos)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;
maybePrefetchIntoCache(int i, boolean bulk)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 
prefetchIntoCache(int i, int pfLimit)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:
pushToBootstrapMethod(MethodHandle pullModeBSM, MethodHandles.Lookup lookup, String name, Object type, Object... arguments)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:
pullFromBootstrapMethod(MethodHandle pushModeBSM, MethodHandles.Lookup lookup, BootstrapCallInfo<?> bsci)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      */
pushMePullYou(MethodHandle bsm, boolean goToPushMode)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 }
575