src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java
changeset 48930 b1a5b4ad7427
parent 48921 576e024f10b6
child 50305 02934b0d661b
equal deleted inserted replaced
48929:28d8fc8cd3cd 48930:b1a5b4ad7427
    35 import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
    35 import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
    36 import static java.lang.invoke.MethodHandles.Lookup;
    36 import static java.lang.invoke.MethodHandles.Lookup;
    37 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
    37 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
    38 
    38 
    39 final class BootstrapMethodInvoker {
    39 final class BootstrapMethodInvoker {
       
    40 
    40     /**
    41     /**
    41      * Factored code for invoking a bootstrap method for invokedynamic
    42      * Factored code for invoking a bootstrap method for invokedynamic
    42      * or a dynamic constant.
    43      * or a dynamic constant.
    43      * @param resultType the expected return type (either CallSite or a constant type)
    44      * @param resultType the expected return type (either CallSite or a constant type)
    44      * @param bootstrapMethod the BSM to call
    45      * @param bootstrapMethod the BSM to call
    74             pullModeBSM = pullMode ? bootstrapMethod :
    75             pullModeBSM = pullMode ? bootstrapMethod :
    75                     pushMePullYou(bootstrapMethod, false);
    76                     pushMePullYou(bootstrapMethod, false);
    76             bootstrapMethod = null;
    77             bootstrapMethod = null;
    77         }
    78         }
    78         try {
    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.
    79             info = maybeReBox(info);
    87             info = maybeReBox(info);
    80             if (info == null) {
    88             if (info == null) {
    81                 // VM is allowed to pass up a null meaning no BSM args
    89                 // VM is allowed to pass up a null meaning no BSM args
    82                 result = bootstrapMethod.invoke(caller, name, type);
    90                 result = invoke(bootstrapMethod, caller, name, type);
    83             }
    91             }
    84             else if (!info.getClass().isArray()) {
    92             else if (!info.getClass().isArray()) {
    85                 // VM is allowed to pass up a single BSM arg directly
    93                 // VM is allowed to pass up a single BSM arg directly
    86                 result = bootstrapMethod.invoke(caller, name, type, info);
    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                 }
    87             }
   104             }
    88             else if (info.getClass() == int[].class) {
   105             else if (info.getClass() == int[].class) {
    89                 // VM is allowed to pass up a pair {argc, index}
   106                 // VM is allowed to pass up a pair {argc, index}
    90                 // referring to 'argc' BSM args at some place 'index'
   107                 // referring to 'argc' BSM args at some place 'index'
    91                 // in the guts of the VM (associated with callerClass).
   108                 // in the guts of the VM (associated with callerClass).
   101             }
   118             }
   102             else {
   119             else {
   103                 // VM is allowed to pass up a full array of resolved BSM args
   120                 // VM is allowed to pass up a full array of resolved BSM args
   104                 Object[] argv = (Object[]) info;
   121                 Object[] argv = (Object[]) info;
   105                 maybeReBoxElements(argv);
   122                 maybeReBoxElements(argv);
   106                 switch (argv.length) {
   123 
   107                     case 0:
   124                 MethodType bsmType = bootstrapMethod.type();
   108                         result = bootstrapMethod.invoke(caller, name, type);
   125                 if (isLambdaMetafactoryIndyBSM(bsmType) && argv.length == 3) {
   109                         break;
   126                     result = (CallSite)bootstrapMethod
   110                     case 1:
   127                             .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
   111                         result = bootstrapMethod.invoke(caller, name, type,
   128                                     (MethodHandle)argv[1], (MethodType)argv[2]);
   112                                                         argv[0]);
   129                 } else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) {
   113                         break;
   130                     result = bootstrapMethod
   114                     case 2:
   131                             .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
   115                         result = bootstrapMethod.invoke(caller, name, type,
   132                                     (MethodHandle)argv[1], (MethodType)argv[2]);
   116                                                         argv[0], argv[1]);
   133                 } else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) {
   117                         break;
   134                     String recipe = (String)argv[0];
   118                     case 3:
   135                     Object[] shiftedArgs = Arrays.copyOfRange(argv, 1, argv.length);
   119                         // Special case the LambdaMetafactory::metafactory BSM
   136                     result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, recipe, shiftedArgs);
   120                         //
   137                 } else {
   121                         // By invoking exactly, we can avoid generating a number of
   138                     switch (argv.length) {
   122                         // classes on first (and subsequent) lambda initialization,
   139                         case 0:
   123                         // most of which won't be shared with other invoke uses.
   140                             result = invoke(bootstrapMethod, caller, name, type);
   124                         MethodType bsmType = bootstrapMethod.type();
   141                             break;
   125                         if (isLambdaMetafactoryIndyBSM(bsmType)) {
   142                         case 1:
   126                             result = (CallSite)bootstrapMethod
   143                             result = invoke(bootstrapMethod, caller, name, type,
   127                                     .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
   144                                             argv[0]);
   128                                                  (MethodHandle)argv[1], (MethodType)argv[2]);
   145                             break;
   129                         } else if (isLambdaMetafactoryCondyBSM(bsmType)) {
   146                         case 2:
   130                             result = bootstrapMethod
   147                             result = invoke(bootstrapMethod, caller, name, type,
   131                                     .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
   148                                             argv[0], argv[1]);
   132                                                  (MethodHandle)argv[1], (MethodType)argv[2]);
   149                             break;
   133                         } else {
   150                         case 3:
   134                             result = bootstrapMethod.invoke(caller, name, type,
   151                             result = invoke(bootstrapMethod, caller, name, type,
   135                                                             argv[0], argv[1], argv[2]);
   152                                             argv[0], argv[1], argv[2]);
   136                         }
   153                             break;
   137                         break;
   154                         case 4:
   138                     case 4:
   155                             result = invoke(bootstrapMethod, caller, name, type,
   139                         result = bootstrapMethod.invoke(caller, name, type,
   156                                             argv[0], argv[1], argv[2], argv[3]);
   140                                                         argv[0], argv[1], argv[2], argv[3]);
   157                             break;
   141                         break;
   158                         case 5:
   142                     case 5:
   159                             result = invoke(bootstrapMethod, caller, name, type,
   143                         result = bootstrapMethod.invoke(caller, name, type,
   160                                             argv[0], argv[1], argv[2], argv[3], argv[4]);
   144                                                         argv[0], argv[1], argv[2], argv[3], argv[4]);
   161                             break;
   145                         break;
   162                         case 6:
   146                     case 6:
   163                             result = invoke(bootstrapMethod, caller, name, type,
   147                         result = bootstrapMethod.invoke(caller, name, type,
   164                                             argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
   148                                                         argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
   165                             break;
   149                         break;
   166                         default:
   150                     default:
   167                             result = invokeWithManyArguments(bootstrapMethod, caller, name, type, argv);
   151                         final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
   168                     }
   152                         final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
       
   153                         if (argv.length >= MAX_SAFE_SIZE) {
       
   154                             // to be on the safe side, use invokeWithArguments which handles jumbo lists
       
   155                             Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
       
   156                             newargv[0] = caller;
       
   157                             newargv[1] = name;
       
   158                             newargv[2] = type;
       
   159                             System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
       
   160                             result = bootstrapMethod.invokeWithArguments(newargv);
       
   161                             break;
       
   162                         }
       
   163                         MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
       
   164                         MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
       
   165                         MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
       
   166                         result = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
       
   167                 }
   169                 }
   168             }
   170             }
   169             if (resultType.isPrimitive()) {
   171             if (resultType.isPrimitive()) {
   170                 // Non-reference conversions are more than just plain casts.
   172                 // Non-reference conversions are more than just plain casts.
   171                 // By pushing the value through a funnel of the form (T x)->x,
   173                 // By pushing the value through a funnel of the form (T x)->x,
   189             // Wrap anything else in BootstrapMethodError
   191             // Wrap anything else in BootstrapMethodError
   190             throw new BootstrapMethodError("bootstrap method initialization exception", ex);
   192             throw new BootstrapMethodError("bootstrap method initialization exception", ex);
   191         }
   193         }
   192     }
   194     }
   193 
   195 
       
   196     // If we don't provide static type information for type, we'll generate runtime
       
   197     // checks. Let's try not to...
       
   198 
       
   199     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
       
   200                                  String name, Object type) throws Throwable {
       
   201         if (type instanceof Class) {
       
   202             return bootstrapMethod.invoke(caller, name, (Class<?>)type);
       
   203         } else {
       
   204             return bootstrapMethod.invoke(caller, name, (MethodType)type);
       
   205         }
       
   206     }
       
   207 
       
   208     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
       
   209                                  String name, Object type, Object arg0) throws Throwable {
       
   210         if (type instanceof Class) {
       
   211             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0);
       
   212         } else {
       
   213             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0);
       
   214         }
       
   215     }
       
   216 
       
   217     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
       
   218                                  Object type, Object arg0, Object arg1) throws Throwable {
       
   219         if (type instanceof Class) {
       
   220             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1);
       
   221         } else {
       
   222             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1);
       
   223         }
       
   224     }
       
   225 
       
   226     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
       
   227                                  Object type, Object arg0, Object arg1,
       
   228                                  Object arg2) throws Throwable {
       
   229         if (type instanceof Class) {
       
   230             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2);
       
   231         } else {
       
   232             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2);
       
   233         }
       
   234     }
       
   235 
       
   236     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
       
   237                                  Object type, Object arg0, Object arg1,
       
   238                                  Object arg2, Object arg3) throws Throwable {
       
   239         if (type instanceof Class) {
       
   240             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3);
       
   241         } else {
       
   242             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3);
       
   243         }
       
   244     }
       
   245 
       
   246     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
       
   247                                  String name, Object type, Object arg0, Object arg1,
       
   248                                  Object arg2, Object arg3, Object arg4) throws Throwable {
       
   249         if (type instanceof Class) {
       
   250             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4);
       
   251         } else {
       
   252             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4);
       
   253         }
       
   254     }
       
   255 
       
   256     private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
       
   257                                  String name, Object type, Object arg0, Object arg1,
       
   258                                  Object arg2, Object arg3, Object arg4, Object arg5) throws Throwable {
       
   259         if (type instanceof Class) {
       
   260             return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4, arg5);
       
   261         } else {
       
   262             return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4, arg5);
       
   263         }
       
   264     }
       
   265 
       
   266     private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Lookup caller,
       
   267                                                   String name, Object type, Object[] argv) throws Throwable {
       
   268         final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
       
   269         final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
       
   270         if (argv.length >= MAX_SAFE_SIZE) {
       
   271             // to be on the safe side, use invokeWithArguments which handles jumbo lists
       
   272             Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
       
   273             newargv[0] = caller;
       
   274             newargv[1] = name;
       
   275             newargv[2] = type;
       
   276             System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
       
   277             return bootstrapMethod.invokeWithArguments(newargv);
       
   278         } else {
       
   279             MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
       
   280             MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
       
   281             MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
       
   282             return spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
       
   283         }
       
   284     }
       
   285 
   194     private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
   286     private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
   195             Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
   287             Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
   196 
   288 
   197     private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class,
   289     private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class,
   198             Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class);
   290             Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class);
       
   291 
       
   292     private static final MethodType SCF_MT = MethodType.methodType(CallSite.class,
       
   293             Lookup.class, String.class, MethodType.class, String.class, Object[].class);
       
   294 
       
   295     /**
       
   296      * @return true iff the BSM method type exactly matches
       
   297      *         {@see java.lang.invoke.StringConcatFactory#makeConcatWithConstants(MethodHandles.Lookup,
       
   298      *                 String,MethodType,String,Object...))}
       
   299      */
       
   300     private static boolean isStringConcatFactoryBSM(MethodType bsmType) {
       
   301         return bsmType == SCF_MT;
       
   302     }
   199 
   303 
   200     /**
   304     /**
   201      * @return true iff the BSM method type exactly matches
   305      * @return true iff the BSM method type exactly matches
   202      *         {@see java.lang.invoke.LambdaMetafactory#metafactory(
   306      *         {@see java.lang.invoke.LambdaMetafactory#metafactory(
   203      *          MethodHandles.Lookup,String,Class,MethodType,MethodHandle,MethodType)}
   307      *          MethodHandles.Lookup,String,Class,MethodType,MethodHandle,MethodType)}