diff -r 02404e27d356 src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java --- a/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java Wed Feb 21 00:29:04 2018 -0500 +++ b/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java Wed Feb 21 19:24:25 2018 +0100 @@ -37,6 +37,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; final class BootstrapMethodInvoker { + + private static final Object[] EMPTY_ARGS = new Object[0]; + /** * Factored code for invoking a bootstrap method for invokedynamic * or a dynamic constant. @@ -79,11 +82,15 @@ info = maybeReBox(info); if (info == null) { // VM is allowed to pass up a null meaning no BSM args - result = bootstrapMethod.invoke(caller, name, type); + result = basicInvoke(bootstrapMethod, caller, name, type); } else if (!info.getClass().isArray()) { // VM is allowed to pass up a single BSM arg directly - result = bootstrapMethod.invoke(caller, name, type, info); + if (isStringConcatFactoryBSM(bootstrapMethod.type())) { + result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, (String)info, EMPTY_ARGS); + } else { + result = bootstrapMethod.invoke(caller, name, type, info); + } } else if (info.getClass() == int[].class) { // VM is allowed to pass up a pair {argc, index} @@ -103,67 +110,59 @@ // VM is allowed to pass up a full array of resolved BSM args Object[] argv = (Object[]) info; maybeReBoxElements(argv); - switch (argv.length) { - case 0: - result = bootstrapMethod.invoke(caller, name, type); - break; - case 1: - result = bootstrapMethod.invoke(caller, name, type, - argv[0]); - break; - case 2: - result = bootstrapMethod.invoke(caller, name, type, - argv[0], argv[1]); - break; - case 3: - // Special case the LambdaMetafactory::metafactory BSM - // - // By invoking exactly, we can avoid generating a number of - // classes on first (and subsequent) lambda initialization, - // most of which won't be shared with other invoke uses. - MethodType bsmType = bootstrapMethod.type(); - if (isLambdaMetafactoryIndyBSM(bsmType)) { - result = (CallSite)bootstrapMethod - .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0], - (MethodHandle)argv[1], (MethodType)argv[2]); - } else if (isLambdaMetafactoryCondyBSM(bsmType)) { - result = bootstrapMethod - .invokeExact(caller, name, (Class)type, (MethodType)argv[0], - (MethodHandle)argv[1], (MethodType)argv[2]); - } else { + + // Special case various known BSM, such as LambdaMetafactory::metafactory, + // StringConcatFactory::makeConcatWithConstants + // + // By invoking exactly, we can avoid generating a number of + // classes on first (and subsequent) lambda initialization, + // most of which won't be shared with other invoke uses. + MethodType bsmType = bootstrapMethod.type(); + if (isLambdaMetafactoryIndyBSM(bsmType)) { + result = (CallSite)bootstrapMethod + .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0], + (MethodHandle)argv[1], (MethodType)argv[2]); + } else if (isLambdaMetafactoryCondyBSM(bsmType)) { + result = bootstrapMethod + .invokeExact(caller, name, (Class)type, (MethodType)argv[0], + (MethodHandle)argv[1], (MethodType)argv[2]); + } else if (isStringConcatFactoryBSM(bsmType)) { + String recipe = (String)argv[0]; + Object[] shiftedArgs = new Object[argv.length - 1]; + System.arraycopy(argv, 1, shiftedArgs, 0, argv.length - 1); + result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, recipe, shiftedArgs); + } else { + switch (argv.length) { + case 0: + result = basicInvoke(bootstrapMethod, caller, name, type); + break; + case 1: result = bootstrapMethod.invoke(caller, name, type, - argv[0], argv[1], argv[2]); - } - break; - case 4: - result = bootstrapMethod.invoke(caller, name, type, - argv[0], argv[1], argv[2], argv[3]); - break; - case 5: - result = bootstrapMethod.invoke(caller, name, type, - argv[0], argv[1], argv[2], argv[3], argv[4]); - break; - case 6: - result = bootstrapMethod.invoke(caller, name, type, - argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); - break; - default: - final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type) - final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT; - if (argv.length >= MAX_SAFE_SIZE) { - // to be on the safe side, use invokeWithArguments which handles jumbo lists - Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length]; - newargv[0] = caller; - newargv[1] = name; - newargv[2] = type; - System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length); - result = bootstrapMethod.invokeWithArguments(newargv); + argv[0]); break; - } - MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length); - MethodHandle typedBSM = bootstrapMethod.asType(invocationType); - MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); - result = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv); + case 2: + result = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1]); + break; + case 3: + result = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2]); + break; + case 4: + result = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2], argv[3]); + break; + case 5: + result = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2], argv[3], argv[4]); + break; + case 6: + result = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); + break; + default: + result = invokeWithManyArguments(bootstrapMethod, name, type, caller, argv); + } } } if (resultType.isPrimitive()) { @@ -191,12 +190,58 @@ } } + private static Object basicInvoke(MethodHandle bootstrapMethod, + Lookup caller, + String name, + Object type) throws Throwable { + // Basic cases will match for example StringConcatFactory::makeConcat, but if we don't + // case type to the correct type we'll generate runtime checked cast. + if (type instanceof Class) { + return bootstrapMethod.invokeExact(caller, name, (Class)type); + } else { + return bootstrapMethod.invokeExact(caller, name, (MethodType)type); + } + } + + private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, String name, Object type, Lookup caller, Object[] argv) throws Throwable { + Object result; + final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type) + final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT; + if (argv.length >= MAX_SAFE_SIZE) { + // to be on the safe side, use invokeWithArguments which handles jumbo lists + Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length]; + newargv[0] = caller; + newargv[1] = name; + newargv[2] = type; + System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length); + result = bootstrapMethod.invokeWithArguments(newargv); + } 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); + result = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv); + } + return result; + } + 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_CONDY_MT = MethodType.methodType(Object.class, Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class); + private static final MethodType SCF_MT = MethodType.methodType(CallSite.class, + Lookup.class, String.class, MethodType.class, String.class, Object[].class); + + /** + * @return true iff the BSM method type exactly matches + * {@see java.lang.invoke.StringConcatFactory#makeConcatWithConstants(MethodHandles.Lookup, + * String,MethodType,String,Object...))} + */ + private static boolean isStringConcatFactoryBSM(MethodType bsmType) { + return bsmType == SCF_MT; + } + /** * @return true iff the BSM method type exactly matches * {@see java.lang.invoke.LambdaMetafactory#metafactory(