src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java
changeset 48826 c4d9d1b08e2e
child 48895 7a1916641c0c
equal deleted inserted replaced
48825:ef8a98bc71f8 48826:c4d9d1b08e2e
       
     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      * Factored code for invoking a bootstrap method for invokedynamic
       
    42      * or a dynamic constant.
       
    43      * @param resultType the expected return type (either CallSite or a constant type)
       
    44      * @param bootstrapMethod the BSM to call
       
    45      * @param name the method name or constant name
       
    46      * @param type the method type or constant type
       
    47      * @param info information passed up from the JVM, to derive static arguments
       
    48      * @param callerClass the class containing the resolved method call or constant load
       
    49      * @param <T> the expected return type
       
    50      * @return the expected value, either a CallSite or a constant value
       
    51      */
       
    52     static <T> T invoke(Class<T> resultType,
       
    53                         MethodHandle bootstrapMethod,
       
    54                         // Callee information:
       
    55                         String name, Object type,
       
    56                         // Extra arguments for BSM, if any:
       
    57                         Object info,
       
    58                         // Caller information:
       
    59                         Class<?> callerClass) {
       
    60         MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
       
    61         Object result;
       
    62         boolean pullMode = isPullModeBSM(bootstrapMethod);  // default value is false
       
    63         boolean vmIsPushing = !staticArgumentsPulled(info); // default value is true
       
    64         MethodHandle pullModeBSM;
       
    65         // match the VM with the BSM
       
    66         if (vmIsPushing) {
       
    67             // VM is pushing arguments at us
       
    68             pullModeBSM = null;
       
    69             if (pullMode) {
       
    70                 bootstrapMethod = Adapters.pushMePullYou(bootstrapMethod, true);
       
    71             }
       
    72         } else {
       
    73             // VM wants us to pull args from it
       
    74             pullModeBSM = pullMode ? bootstrapMethod :
       
    75                     Adapters.pushMePullYou(bootstrapMethod, false);
       
    76             bootstrapMethod = null;
       
    77         }
       
    78         try {
       
    79             info = maybeReBox(info);
       
    80             if (info == null) {
       
    81                 // VM is allowed to pass up a null meaning no BSM args
       
    82                 result = bootstrapMethod.invoke(caller, name, type);
       
    83             }
       
    84             else if (!info.getClass().isArray()) {
       
    85                 // VM is allowed to pass up a single BSM arg directly
       
    86                 result = bootstrapMethod.invoke(caller, name, type, info);
       
    87             }
       
    88             else if (info.getClass() == int[].class) {
       
    89                 // VM is allowed to pass up a pair {argc, index}
       
    90                 // referring to 'argc' BSM args at some place 'index'
       
    91                 // in the guts of the VM (associated with callerClass).
       
    92                 // The format of this index pair is private to the
       
    93                 // handshake between the VM and this class only.
       
    94                 // This supports "pulling" of arguments.
       
    95                 // The VM is allowed to do this for any reason.
       
    96                 // The code in this method makes up for any mismatches.
       
    97                 BootstrapCallInfo<Object> bsci
       
    98                     = new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info);
       
    99                 // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
       
   100                 result = pullModeBSM.invoke(caller, bsci);
       
   101             }
       
   102             else {
       
   103                 // VM is allowed to pass up a full array of resolved BSM args
       
   104                 Object[] argv = (Object[]) info;
       
   105                 maybeReBoxElements(argv);
       
   106                 switch (argv.length) {
       
   107                     case 0:
       
   108                         result = bootstrapMethod.invoke(caller, name, type);
       
   109                         break;
       
   110                     case 1:
       
   111                         result = bootstrapMethod.invoke(caller, name, type,
       
   112                                                         argv[0]);
       
   113                         break;
       
   114                     case 2:
       
   115                         result = bootstrapMethod.invoke(caller, name, type,
       
   116                                                         argv[0], argv[1]);
       
   117                         break;
       
   118                     case 3:
       
   119                         result = bootstrapMethod.invoke(caller, name, type,
       
   120                                                         argv[0], argv[1], argv[2]);
       
   121                         break;
       
   122                     case 4:
       
   123                         result = bootstrapMethod.invoke(caller, name, type,
       
   124                                                         argv[0], argv[1], argv[2], argv[3]);
       
   125                         break;
       
   126                     case 5:
       
   127                         result = bootstrapMethod.invoke(caller, name, type,
       
   128                                                         argv[0], argv[1], argv[2], argv[3], argv[4]);
       
   129                         break;
       
   130                     case 6:
       
   131                         result = bootstrapMethod.invoke(caller, name, type,
       
   132                                                         argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
       
   133                         break;
       
   134                     default:
       
   135                         final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
       
   136                         final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
       
   137                         if (argv.length >= MAX_SAFE_SIZE) {
       
   138                             // to be on the safe side, use invokeWithArguments which handles jumbo lists
       
   139                             Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
       
   140                             newargv[0] = caller;
       
   141                             newargv[1] = name;
       
   142                             newargv[2] = type;
       
   143                             System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
       
   144                             result = bootstrapMethod.invokeWithArguments(newargv);
       
   145                             break;
       
   146                         }
       
   147                         MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
       
   148                         MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
       
   149                         MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
       
   150                         result = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
       
   151                 }
       
   152             }
       
   153             if (resultType.isPrimitive()) {
       
   154                 // Non-reference conversions are more than just plain casts.
       
   155                 // By pushing the value through a funnel of the form (T x)->x,
       
   156                 // the boxed result can be widened as needed.  See MH::asType.
       
   157                 MethodHandle funnel = MethodHandles.identity(resultType);
       
   158                 result = funnel.invoke(result);
       
   159                 // Now it is the wrapper type for resultType.
       
   160                 resultType = Wrapper.asWrapperType(resultType);
       
   161             }
       
   162             return resultType.cast(result);
       
   163         }
       
   164         catch (Error e) {
       
   165             // Pass through an Error, including BootstrapMethodError, any other
       
   166             // form of linkage error, such as IllegalAccessError if the bootstrap
       
   167             // method is inaccessible, or say ThreadDeath/OutOfMemoryError
       
   168             // See the "Linking Exceptions" section for the invokedynamic
       
   169             // instruction in JVMS 6.5.
       
   170             throw e;
       
   171         }
       
   172         catch (Throwable ex) {
       
   173             // Wrap anything else in BootstrapMethodError
       
   174             throw new BootstrapMethodError("bootstrap method initialization exception", ex);
       
   175         }
       
   176     }
       
   177 
       
   178     /** The JVM produces java.lang.Integer values to box
       
   179      *  CONSTANT_Integer boxes but does not intern them.
       
   180      *  Let's intern them.  This is slightly wrong for
       
   181      *  a {@code CONSTANT_Dynamic} which produces an
       
   182      *  un-interned integer (e.g., {@code new Integer(0)}).
       
   183      */
       
   184     private static Object maybeReBox(Object x) {
       
   185         if (x instanceof Integer) {
       
   186             int xi = (int) x;
       
   187             if (xi == (byte) xi)
       
   188                 x = xi;  // must rebox; see JLS 5.1.7
       
   189         }
       
   190         return x;
       
   191     }
       
   192 
       
   193     private static void maybeReBoxElements(Object[] xa) {
       
   194         for (int i = 0; i < xa.length; i++) {
       
   195             xa[i] = maybeReBox(xa[i]);
       
   196         }
       
   197     }
       
   198 
       
   199     /** Canonical VM-aware implementation of BootstrapCallInfo.
       
   200      * Knows how to dig into the JVM for lazily resolved (pull-mode) constants.
       
   201      */
       
   202     private static final class VM_BSCI<T> extends BSCIWithCache<T> {
       
   203         private final int[] indexInfo;
       
   204         private final Class<?> caller;  // for index resolution only
       
   205 
       
   206         VM_BSCI(MethodHandle bsm, String name, T type,
       
   207                 Lookup lookup, int[] indexInfo) {
       
   208             super(bsm, name, type, indexInfo[0]);
       
   209             if (!lookup.hasPrivateAccess())  //D.I.D.
       
   210                 throw new AssertionError("bad Lookup object");
       
   211             this.caller = lookup.lookupClass();
       
   212             this.indexInfo = indexInfo;
       
   213             // scoop up all the easy stuff right away:
       
   214             prefetchIntoCache(0, size());
       
   215         }
       
   216 
       
   217         @Override Object fillCache(int i) {
       
   218             Object[] buf = { null };
       
   219             copyConstants(i, i+1, buf, 0);
       
   220             Object res = wrapNull(buf[0]);
       
   221             cache[i] = res;
       
   222             int next = i + 1;
       
   223             if (next < cache.length && cache[next] == null)
       
   224                 maybePrefetchIntoCache(next, false);  // try to prefetch
       
   225             return res;
       
   226         }
       
   227 
       
   228         @Override public int copyConstants(int start, int end,
       
   229                                            Object[] buf, int pos) {
       
   230             int i = start, bufi = pos;
       
   231             while (i < end) {
       
   232                 Object x = cache[i];
       
   233                 if (x == null)  break;
       
   234                 buf[bufi++] = unwrapNull(x);
       
   235                 i++;
       
   236             }
       
   237             // give up at first null and grab the rest in one big block
       
   238             if (i >= end)  return i;
       
   239             Object[] temp = new Object[end - i];
       
   240             if (TRACE_METHOD_LINKAGE)
       
   241                 System.out.println("resolving more BSM arguments: "+
       
   242                         Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end));
       
   243             copyOutBootstrapArguments(caller, indexInfo,
       
   244                                       i, end, temp, 0,
       
   245                                       true, null);
       
   246             for (Object x : temp) {
       
   247                 x = maybeReBox(x);
       
   248                 buf[bufi++] = x;
       
   249                 cache[i++] = wrapNull(x);
       
   250             }
       
   251             if (end < cache.length && cache[end] == null)
       
   252                 maybePrefetchIntoCache(end, true);  // try to prefetch
       
   253             return i;
       
   254         }
       
   255 
       
   256         private static final int MIN_PF = 4;
       
   257         private void maybePrefetchIntoCache(int i, boolean bulk) {
       
   258             int len = cache.length;
       
   259             assert(0 <= i && i <= len);
       
   260             int pfLimit = i;
       
   261             if (bulk)  pfLimit += i;  // exponential prefetch expansion
       
   262             // try to prefetch at least MIN_PF elements
       
   263             if (pfLimit < i + MIN_PF)  pfLimit = i + MIN_PF;
       
   264             if (pfLimit > len || pfLimit < 0)  pfLimit = len;
       
   265             // stop prefetching where cache is more full than empty
       
   266             int empty = 0, nonEmpty = 0, lastEmpty = i;
       
   267             for (int j = i; j < pfLimit; j++) {
       
   268                 if (cache[j] == null) {
       
   269                     empty++;
       
   270                     lastEmpty = j;
       
   271                 } else {
       
   272                     nonEmpty++;
       
   273                     if (nonEmpty > empty) {
       
   274                         pfLimit = lastEmpty + 1;
       
   275                         break;
       
   276                     }
       
   277                     if (pfLimit < len)  pfLimit++;
       
   278                 }
       
   279             }
       
   280             if (bulk && empty < MIN_PF && pfLimit < len)
       
   281                 return;  // not worth the effort
       
   282             prefetchIntoCache(i, pfLimit);
       
   283         }
       
   284 
       
   285         private void prefetchIntoCache(int i, int pfLimit) {
       
   286             if (pfLimit <= i)  return;  // corner case
       
   287             Object[] temp = new Object[pfLimit - i];
       
   288             if (TRACE_METHOD_LINKAGE)
       
   289                 System.out.println("prefetching BSM arguments: "+
       
   290                         Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit));
       
   291             copyOutBootstrapArguments(caller, indexInfo,
       
   292                                       i, pfLimit, temp, 0,
       
   293                                       false, NOT_PRESENT);
       
   294             for (Object x : temp) {
       
   295                 if (x != NOT_PRESENT && cache[i] == null) {
       
   296                     cache[i] = wrapNull(maybeReBox(x));
       
   297                 }
       
   298                 i++;
       
   299             }
       
   300         }
       
   301     }
       
   302 
       
   303     /*non-public*/ static final
       
   304     class Adapters {
       
   305         // skeleton for push-mode BSM which wraps a pull-mode BSM:
       
   306         static Object pushToBootstrapMethod(MethodHandle pullModeBSM,
       
   307                                             MethodHandles.Lookup lookup, String name, Object type,
       
   308                                             Object... arguments) throws Throwable {
       
   309             ConstantGroup cons = makeConstantGroup(Arrays.asList(arguments));
       
   310             BootstrapCallInfo<?> bsci = makeBootstrapCallInfo(pullModeBSM, name, type, cons);
       
   311             if (TRACE_METHOD_LINKAGE)
       
   312                 System.out.println("pull-mode BSM gets pushed arguments from fake BSCI");
       
   313             return pullModeBSM.invoke(lookup, bsci);
       
   314         }
       
   315 
       
   316         // skeleton for pull-mode BSM which wraps a push-mode BSM:
       
   317         static Object pullFromBootstrapMethod(MethodHandle pushModeBSM,
       
   318                                               MethodHandles.Lookup lookup, BootstrapCallInfo<?> bsci)
       
   319                 throws Throwable {
       
   320             int argc = bsci.size();
       
   321             Object arguments[] = new Object[3 + argc];
       
   322             arguments[0] = lookup;
       
   323             arguments[1] = bsci.invocationName();
       
   324             arguments[2] = bsci.invocationType();
       
   325             bsci.copyConstants(0, argc, arguments, 3);
       
   326             if (TRACE_METHOD_LINKAGE)
       
   327                 System.out.println("pulled arguments from VM for push-mode BSM");
       
   328             return pushModeBSM.invokeWithArguments(arguments);
       
   329         }
       
   330         static final MethodHandle MH_pushToBootstrapMethod;
       
   331         static final MethodHandle MH_pullFromBootstrapMethod;
       
   332         static {
       
   333             final Class<?> THIS_CLASS = Adapters.class;
       
   334             try {
       
   335                 MH_pushToBootstrapMethod = IMPL_LOOKUP
       
   336                     .findStatic(THIS_CLASS, "pushToBootstrapMethod",
       
   337                                 MethodType.methodType(Object.class, MethodHandle.class,
       
   338                                         Lookup.class, String.class, Object.class, Object[].class));
       
   339                 MH_pullFromBootstrapMethod = IMPL_LOOKUP
       
   340                     .findStatic(THIS_CLASS, "pullFromBootstrapMethod",
       
   341                                 MethodType.methodType(Object.class, MethodHandle.class,
       
   342                                         Lookup.class, BootstrapCallInfo.class));
       
   343             } catch (Throwable ex) {
       
   344                 throw new InternalError(ex);
       
   345             }
       
   346         }
       
   347 
       
   348         /** Given a push-mode BSM (taking one argument) convert it to a
       
   349          *  pull-mode BSM (taking N pre-resolved arguments).
       
   350          *  This method is used when, in fact, the JVM is passing up
       
   351          *  pre-resolved arguments, but the BSM is expecting lazy stuff.
       
   352          *  Or, when goToPushMode is true, do the reverse transform.
       
   353          *  (The two transforms are exactly inverse.)
       
   354          */
       
   355         static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) {
       
   356             if (TRACE_METHOD_LINKAGE)
       
   357                 System.out.println("converting BSM to "+(goToPushMode ? "push mode" : "pull mode"));
       
   358             assert(isPullModeBSM(bsm) == goToPushMode);  //there must be a change
       
   359             if (goToPushMode) {
       
   360                 return Adapters.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
       
   361             } else {
       
   362                 return Adapters.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
       
   363             }
       
   364         }
       
   365     }
       
   366 }