< prev index next >

src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -45,38 +45,46 @@
      * @param bootstrapMethod the BSM to call
      * @param name the method name or constant name
      * @param type the method type or constant type
      * @param info information passed up from the JVM, to derive static arguments
      * @param callerClass the class containing the resolved method call or constant load
+     * @param includeMetadata true if the lookup, name and type metadata arguments should
+     *                        be included when invoking the BSM
      * @param <T> the expected return type
      * @return the expected value, either a CallSite or a constant value
      */
     static <T> T invoke(Class<T> resultType,
                         MethodHandle bootstrapMethod,
                         // Callee information:
                         String name, Object type,
                         // Extra arguments for BSM, if any:
                         Object info,
                         // Caller information:
-                        Class<?> callerClass) {
+                        Class<?> callerClass,
+                        boolean includeMetadata) {
         MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
+        MethodType bsmType = bootstrapMethod.type();
         Object result;
         boolean pullMode = isPullModeBSM(bootstrapMethod);  // default value is false
         boolean vmIsPushing = !staticArgumentsPulled(info); // default value is true
         MethodHandle pullModeBSM;
+
         // match the VM with the BSM
         if (vmIsPushing) {
             // VM is pushing arguments at us
+            // Need to transform if pull-mode BSM
             pullModeBSM = null;
             if (pullMode) {
-                bootstrapMethod = pushMePullYou(bootstrapMethod, true);
+                bootstrapMethod = PushAdapter.toPullBootstrapMethod(bootstrapMethod);
+                bsmType = bootstrapMethod.type();
             }
-        } else {
+        }
+        else {
             // VM wants us to pull args from it
+            // Need to transform if push-mode BSM
             pullModeBSM = pullMode ? bootstrapMethod :
-                    pushMePullYou(bootstrapMethod, false);
-            bootstrapMethod = null;
+                    PullAdapter.toPushBootstrapMethod(bootstrapMethod, includeMetadata);
         }
         try {
             // As an optimization we special case various known BSMs,
             // such as LambdaMetafactory::metafactory and
             // StringConcatFactory::makeConcatWithConstants.

@@ -84,25 +92,34 @@
             // By providing static type information or even invoking
             // exactly, we avoid emitting code to perform runtime
             // checking.
             info = maybeReBox(info);
             if (info == null) {
-                // VM is allowed to pass up a null meaning no BSM args
-                result = invoke(bootstrapMethod, caller, name, type);
+                if (includeMetadata) {
+                    // VM is allowed to pass up a null meaning no BSM args
+                    result = invoke(bootstrapMethod, caller, name, type);
+                }
+                else {
+                    result = bootstrapMethod.invoke();
+                }
             }
             else if (!info.getClass().isArray()) {
                 // VM is allowed to pass up a single BSM arg directly
 
                 // Call to StringConcatFactory::makeConcatWithConstants
                 // with empty constant arguments?
-                if (isStringConcatFactoryBSM(bootstrapMethod.type())) {
+                if (isStringConcatFactoryBSM(bsmType)) {
                     result = (CallSite)bootstrapMethod
                             .invokeExact(caller, name, (MethodType)type,
                                          (String)info, new Object[0]);
-                } else {
+                }
+                else if (includeMetadata) {
                     result = invoke(bootstrapMethod, caller, name, type, info);
                 }
+                else {
+                    result = bootstrapMethod.invoke(info);
+                }
             }
             else if (info.getClass() == int[].class) {
                 // VM is allowed to pass up a pair {argc, index}
                 // referring to 'argc' BSM args at some place 'index'
                 // in the guts of the VM (associated with callerClass).

@@ -111,34 +128,43 @@
                 // This supports "pulling" of arguments.
                 // The VM is allowed to do this for any reason.
                 // The code in this method makes up for any mismatches.
                 BootstrapCallInfo<Object> bsci
                     = new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info);
-                // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
-                result = pullModeBSM.invoke(caller, bsci);
+                if (includeMetadata) {
+                    // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
+                    result = pullModeBSM.invoke(caller, bsci);
+                }
+                else {
+                    // Pull-mode API is (BootstrapCallInfo) -> Object
+                    result = pullModeBSM.invoke(bsci);
+                }
             }
             else {
                 // VM is allowed to pass up a full array of resolved BSM args
                 Object[] argv = (Object[]) info;
                 maybeReBoxElements(argv);
 
-                MethodType bsmType = bootstrapMethod.type();
                 if (isLambdaMetafactoryIndyBSM(bsmType) && argv.length == 3) {
                     result = (CallSite)bootstrapMethod
                             .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
                                     (MethodHandle)argv[1], (MethodType)argv[2]);
-                } else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) {
+                }
+                else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) {
                     result = bootstrapMethod
                             .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
                                     (MethodHandle)argv[1], (MethodType)argv[2]);
-                } else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) {
+                }
+                else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) {
                     String recipe = (String)argv[0];
                     Object[] shiftedArgs = Arrays.copyOfRange(argv, 1, argv.length);
                     result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, recipe, shiftedArgs);
-                } else if (isLambdaMetafactoryAltMetafactoryBSM(bsmType)) {
+                }
+                else if (isLambdaMetafactoryAltMetafactoryBSM(bsmType)) {
                     result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, argv);
-                } else {
+                }
+                else if (includeMetadata) {
                     switch (argv.length) {
                         case 0:
                             result = invoke(bootstrapMethod, caller, name, type);
                             break;
                         case 1:

@@ -167,10 +193,43 @@
                             break;
                         default:
                             result = invokeWithManyArguments(bootstrapMethod, caller, name, type, argv);
                     }
                 }
+                else {
+                    switch (argv.length) {
+                        case 0:
+                            result = bootstrapMethod.invoke();
+                            break;
+                        case 1:
+                            result = bootstrapMethod.invoke(
+                                            argv[0]);
+                            break;
+                        case 2:
+                            result = bootstrapMethod.invoke(
+                                            argv[0], argv[1]);
+                            break;
+                        case 3:
+                            result = bootstrapMethod.invoke(
+                                            argv[0], argv[1], argv[2]);
+                            break;
+                        case 4:
+                            result = bootstrapMethod.invoke(
+                                            argv[0], argv[1], argv[2], argv[3]);
+                            break;
+                        case 5:
+                            result = bootstrapMethod.invoke(
+                                            argv[0], argv[1], argv[2], argv[3], argv[4]);
+                            break;
+                        case 6:
+                            result = bootstrapMethod.invoke(
+                                            argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
+                            break;
+                        default:
+                            result = invokeWithManyArguments(bootstrapMethod, argv);
+                    }
+                }
             }
             if (resultType.isPrimitive()) {
                 // Non-reference conversions are more than just plain casts.
                 // By pushing the value through a funnel of the form (T x)->x,
                 // the boxed result can be widened as needed.  See MH::asType.

@@ -200,59 +259,65 @@
 
     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
                                  String name, Object type) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type);
         }
     }
 
     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
                                  String name, Object type, Object arg0) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0);
         }
     }
 
     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
                                  Object type, Object arg0, Object arg1) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1);
         }
     }
 
     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
                                  Object type, Object arg0, Object arg1,
                                  Object arg2) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2);
         }
     }
 
     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
                                  Object type, Object arg0, Object arg1,
                                  Object arg2, Object arg3) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3);
         }
     }
 
     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
                                  String name, Object type, Object arg0, Object arg1,
                                  Object arg2, Object arg3, Object arg4) throws Throwable {
         if (type instanceof Class) {
             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4);
-        } else {
+        }
+        else {
             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4);
         }
     }
 
     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,

@@ -275,18 +340,33 @@
             newargv[0] = caller;
             newargv[1] = name;
             newargv[2] = type;
             System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
             return bootstrapMethod.invokeWithArguments(newargv);
-        } else {
+        }
+        else {
             MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
             MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
             MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
             return spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
         }
     }
 
+    private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Object[] argv) throws Throwable {
+        final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2;
+        if (argv.length >= MAX_SAFE_SIZE) {
+            // to be on the safe side, use invokeWithArguments which handles jumbo lists
+            return bootstrapMethod.invokeWithArguments(argv);
+        }
+        else {
+            MethodType invocationType = MethodType.genericMethodType(argv.length);
+            MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
+            MethodHandle spreader = invocationType.invokers().spreadInvoker(0);
+            return spreader.invokeExact(typedBSM, argv);
+        }
+    }
+
     private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
             Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
 
     private static final MethodType LMF_ALT_MT = MethodType.methodType(CallSite.class,
             Lookup.class, String.class, MethodType.class, Object[].class);

@@ -372,20 +452,20 @@
             prefetchIntoCache(0, size());
         }
 
         @Override Object fillCache(int i) {
             Object[] buf = { null };
-            copyConstants(i, i+1, buf, 0);
+            copyArguments(i, i+1, buf, 0);
             Object res = wrapNull(buf[0]);
             cache[i] = res;
             int next = i + 1;
             if (next < cache.length && cache[next] == null)
                 maybePrefetchIntoCache(next, false);  // try to prefetch
             return res;
         }
 
-        @Override public int copyConstants(int start, int end,
+        @Override public int copyArguments(int start, int end,
                                            Object[] buf, int pos) {
             int i = start, bufi = pos;
             while (i < end) {
                 Object x = cache[i];
                 if (x == null)  break;

@@ -460,33 +540,35 @@
         }
     }
 
     /*non-public*/ static final
     class PushAdapter {
-        // skeleton for push-mode BSM which wraps a pull-mode BSM:
-        static Object pushToBootstrapMethod(MethodHandle pullModeBSM,
-                                            MethodHandles.Lookup lookup, String name, Object type,
-                                            Object... arguments) throws Throwable {
-            ConstantGroup cons = makeConstantGroup(Arrays.asList(arguments));
-            BootstrapCallInfo<?> bsci = makeBootstrapCallInfo(pullModeBSM, name, type, cons);
-            if (TRACE_METHOD_LINKAGE)
-                System.out.println("pull-mode BSM gets pushed arguments from fake BSCI");
-            return pullModeBSM.invoke(lookup, bsci);
-        }
-
         static final MethodHandle MH_pushToBootstrapMethod;
         static {
-            final Class<?> THIS_CLASS = PushAdapter.class;
             try {
                 MH_pushToBootstrapMethod = IMPL_LOOKUP
-                    .findStatic(THIS_CLASS, "pushToBootstrapMethod",
+                        .findStatic(BootstrapCallInfo.class, "invokeFromArgumentsToCallInfo",
                                 MethodType.methodType(Object.class, MethodHandle.class,
                                         Lookup.class, String.class, Object.class, Object[].class));
-            } catch (Throwable ex) {
+            }
+            catch (Throwable ex) {
                 throw new InternalError(ex);
             }
         }
+
+        /**
+         * Given a push-mode BSM (taking one BSCI argument) convert it to a
+         * pull-mode BSM (taking N pre-resolved arguments).  This method is used
+         * when the JVM is passing up pre-resolved arguments, but the BSM is
+         * expecting to BSCI to resolve arguments lazily.
+         */
+        static MethodHandle toPullBootstrapMethod(MethodHandle bsm) {
+            if (TRACE_METHOD_LINKAGE) {
+                System.out.println("converting a push-mode BSM of type " + bsm.type() + " to pull-mode");
+            }
+            return MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
+        }
     }
 
     /*non-public*/ static final
     class PullAdapter {
         // skeleton for pull-mode BSM which wraps a push-mode BSM:

@@ -535,40 +617,83 @@
                     bsci.copyConstants(0, argc, argv, 0);
                     return spreader.invokeExact(typedBSM, (Object) lookup, (Object) bsci.invocationName(), bsci.invocationType(), argv);
                 }
         }
 
+        static Object pullFromBootstrapMethodExclude(MethodHandle pushModeBSM,
+                                                     BootstrapCallInfo<?> bsci)
+                throws Throwable {
+            int argc = bsci.size();
+            switch (argc) {
+                case 0:
+                    return pushModeBSM.invoke();
+                case 1:
+                    return pushModeBSM.invoke(bsci.get(0));
+                case 2:
+                    return pushModeBSM.invoke(bsci.get(0), bsci.get(1));
+                case 3:
+                    return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2));
+                case 4:
+                    return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3));
+                case 5:
+                    return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4));
+                case 6:
+                    return pushModeBSM.invoke(bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5));
+                default:
+                    final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2;
+                    if (argc >= MAX_SAFE_SIZE) {
+                        // to be on the safe side, use invokeWithArguments which handles jumbo lists
+                        Object[] argv = new Object[argc];
+                        bsci.copyConstants(0, argc, argv, 0);
+                        return pushModeBSM.invokeWithArguments(argv);
+                    }
+                    MethodType invocationType = MethodType.genericMethodType(argc);
+                    MethodHandle typedBSM = pushModeBSM.asType(invocationType);
+                    MethodHandle spreader = invocationType.invokers().spreadInvoker(0);
+                    Object[] argv = new Object[argc];
+                    bsci.copyConstants(0, argc, argv, 0);
+                    return spreader.invokeExact(typedBSM, argv);
+            }
+        }
+
         static final MethodHandle MH_pullFromBootstrapMethod;
 
+        static final MethodHandle MH_pullFromBootstrapMethodExclude;
+
         static {
             final Class<?> THIS_CLASS = PullAdapter.class;
             try {
                 MH_pullFromBootstrapMethod = IMPL_LOOKUP
                     .findStatic(THIS_CLASS, "pullFromBootstrapMethod",
                                 MethodType.methodType(Object.class, MethodHandle.class,
                                         Lookup.class, BootstrapCallInfo.class));
-            } catch (Throwable ex) {
+
+                MH_pullFromBootstrapMethodExclude = IMPL_LOOKUP
+                        .findStatic(THIS_CLASS, "pullFromBootstrapMethodExclude",
+                                    MethodType.methodType(Object.class, MethodHandle.class,
+                                                          BootstrapCallInfo.class));
+            }
+            catch (Throwable ex) {
                 throw new InternalError(ex);
             }
         }
-    }
 
-    /** Given a push-mode BSM (taking one argument) convert it to a
-     *  pull-mode BSM (taking N pre-resolved arguments).
-     *  This method is used when, in fact, the JVM is passing up
-     *  pre-resolved arguments, but the BSM is expecting lazy stuff.
-     *  Or, when goToPushMode is true, do the reverse transform.
-     *  (The two transforms are exactly inverse.)
-     */
-    static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) {
-        if (TRACE_METHOD_LINKAGE) {
-            System.out.println("converting BSM of type " + bsm.type() + " to "
-                    + (goToPushMode ? "push mode" : "pull mode"));
-        }
-        assert(isPullModeBSM(bsm) == goToPushMode); // there must be a change
-        if (goToPushMode) {
-            return PushAdapter.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
-        } else {
-            return PullAdapter.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
+        /**
+         * Given a pull-mode BSM (taking N pre-resolved arguments) convert it to
+         * a push-mode BSM (taking one BSCI argument).  This method is used when
+         * the JVM is passing up indexes to constant pool entries (possibly
+         * unresolved), but the BSM is expecting pre-resolved arguments.
+         */
+        static MethodHandle toPushBootstrapMethod(MethodHandle bsm, boolean includeMetaData) {
+            if (TRACE_METHOD_LINKAGE) {
+                System.out.println("converting a pull-mode BSM of type " + bsm.type() + " to push-mode"
+                                   + (!includeMetaData ? ", exluding meta-data" : ""));
+            }
+            if (includeMetaData) {
+                return PullAdapter.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
+            }
+            else {
+                return PullAdapter.MH_pullFromBootstrapMethodExclude.bindTo(bsm).withVarargs(false);
+            }
         }
     }
 }
< prev index next >