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