--- old/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java 2018-09-28 11:54:22.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java 2018-09-28 11:54:22.000000000 -0700 @@ -24,162 +24,68 @@ */ package java.lang.invoke; -import sun.invoke.util.Wrapper; +import jdk.internal.reflect.ConstantPool; +import jdk.internal.vm.annotation.Stable; -import java.lang.invoke.AbstractConstantGroup.BSCIWithCache; +import java.lang.constant.Constable; +import java.lang.constant.ConstantDesc; +import java.lang.constant.DynamicConstantDesc; +import java.lang.reflect.Array; import java.util.Arrays; +import java.util.List; +import java.util.Objects; -import static java.lang.invoke.BootstrapCallInfo.makeBootstrapCallInfo; -import static java.lang.invoke.ConstantGroup.makeConstantGroup; -import static java.lang.invoke.MethodHandleNatives.*; +import java.lang.invoke.AbstractBootstrapCallInfo.*; + +import static java.lang.invoke.AbstractBootstrapCallInfo.maybeShareArguments; +import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE; import static java.lang.invoke.MethodHandles.Lookup; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; +import static java.util.Objects.requireNonNull; final class BootstrapMethodInvoker { - /** * Factored code for invoking a bootstrap method for invokedynamic * or a dynamic constant. - * @param resultType the expected return type (either CallSite or a constant type) - * @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 the expected return type + * @param lookup the capability to access the caller class's constants + * @param bsci the BootstrapCallInfo provided by the JVM + * @param the kind of the type argument, {@code Class} or {@code MethodType} * @return the expected value, either a CallSite or a constant value */ - static T invoke(Class resultType, - MethodHandle bootstrapMethod, - // Callee information: - String name, Object type, - // Extra arguments for BSM, if any: - Object info, - // Caller information: - Class callerClass) { - MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass); + static > + Object invoke(Lookup lookup, + BootstrapCallInfo bsci) { + Objects.requireNonNull(lookup); + MethodHandle bsm = bsci.bootstrapMethod(); + MethodType bsmType = bsm.type(); + boolean bsmIsVarargs = bsm.isVarargsCollector(); 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 - pullModeBSM = null; - if (pullMode) { - bootstrapMethod = pushMePullYou(bootstrapMethod, true); - } - } else { - // VM wants us to pull args from it - pullModeBSM = pullMode ? bootstrapMethod : - pushMePullYou(bootstrapMethod, false); - bootstrapMethod = null; - } + try { - // As an optimization we special case various known BSMs, - // such as LambdaMetafactory::metafactory and - // StringConcatFactory::makeConcatWithConstants. - // - // 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); - } - 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())) { - result = (CallSite)bootstrapMethod - .invokeExact(caller, name, (MethodType)type, - (String)info, new Object[0]); - } else { - result = invoke(bootstrapMethod, caller, name, type, 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). - // The format of this index pair is private to the - // handshake between the VM and this class only. - // 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 bsci - = new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info); + // By using maybeShareArguments, we avoid extra copy steps + // on the argument list between a BSM created for the JVM + // (by MethodHandleNatives) and the invocation logic of + // invokeCommon. + if (isPullModeBSM(bsm)) { // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object - result = pullModeBSM.invoke(caller, bsci); + result = bsm.invoke(lookup, bsci); + } else if (isExpressionBSM(bsm)) { + if (bsci.invocationName().equals("invoke")) + // short circuit in common case + result = invokeCommon(bsm, + null, null, null, + maybeShareArguments(bsci), + bsci.argumentList()); + else + result = ConstantBootstraps.linkExpression(lookup, bsci); + } else { + // Push-mode BSM, needs to get (lookup, name, type, arg...). + result = invokeCommon(bsm, lookup, + bsci.invocationName(), bsci.invocationType(), + maybeShareArguments(bsci), bsci.argumentList()); } - 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) { - result = bootstrapMethod - .invokeExact(caller, name, (Class)type, (MethodType)argv[0], - (MethodHandle)argv[1], (MethodType)argv[2]); - } 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)) { - result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, argv); - } else { - switch (argv.length) { - case 0: - result = invoke(bootstrapMethod, caller, name, type); - break; - case 1: - result = invoke(bootstrapMethod, caller, name, type, - argv[0]); - break; - case 2: - result = invoke(bootstrapMethod, caller, name, type, - argv[0], argv[1]); - break; - case 3: - result = invoke(bootstrapMethod, caller, name, type, - argv[0], argv[1], argv[2]); - break; - case 4: - result = invoke(bootstrapMethod, caller, name, type, - argv[0], argv[1], argv[2], argv[3]); - break; - case 5: - result = invoke(bootstrapMethod, caller, name, type, - argv[0], argv[1], argv[2], argv[3], argv[4]); - break; - case 6: - result = invoke(bootstrapMethod, caller, name, type, - argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); - break; - default: - result = invokeWithManyArguments(bootstrapMethod, caller, name, type, 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. - MethodHandle funnel = MethodHandles.identity(resultType); - result = funnel.invoke(result); - // Now it is the wrapper type for resultType. - resultType = Wrapper.asWrapperType(resultType); - } - return resultType.cast(result); + return BootstrapCallInfo.convertResult(bsci.invocationType(), bsmType.returnType(), result); } catch (Error e) { // Pass through an Error, including BootstrapMethodError, any other @@ -195,151 +101,94 @@ } } - // If we don't provide static type information for type, we'll generate runtime - // checks. Let's try not to... - - 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 { - 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 { - 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 { - 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 { - 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 { - return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3); - } + /** A BSM runs in pull mode if and only if its sole arguments + * are {@code (Lookup, BootstrapCallInfo)}. + * Since the trailing argument is not an array type, it cannot be of variable arity. + * The return type of the BSM is unconstrained, but it will often be {@code Object}. + */ + public static boolean isPullModeBSM(MethodHandle bsm) { + MethodType mtype = bsm.type(); + return mtype.parameterCount() == 2 + && mtype.parameterType(0) == Lookup.class + && mtype.parameterType(1) == BootstrapCallInfo.class; + } + + /** Most BSM calls take a standard set of metadata arguments, + * one of {@code (Lookup, String, Class)}, {@code (Lookup, String, MethodType)}, + * or {@code (Lookup, BootstrapCallInfo)}. + * But if the lead argument is not {@code Lookup} and + * the invocation type being linked is a field type ({@code Class}), + * then only the static arguments will be passed. + * Such a BSM is called an expression mode BSM. + * The standard bootstrap method {@link ConstantBootstraps#linkExpression} + * is consulted to correctly invoke such an expression bootstrap method. + * Often, it is just applied directly to the static arguments. + */ + static boolean isExpressionBSM(MethodHandle bsm) { + MethodType mtype = bsm.type(); + return (mtype.parameterCount() == 0 + || mtype.parameterType(0) != Lookup.class); } - 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 { - return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4); + /** Given a poorly typed BSM, wrap it in a BSM whose + * type starts with Lookup. This will prevent it from + * being accidentally treated as an expression-mode BSM. + * We only do this, of course, if we know that the BSM + * is not supposed to have expression mode. This is the + * case when the BSM links an invokedynamic instruction. + */ + static MethodHandle forceLookupParameter(MethodHandle bsm) { + if (TRACE_METHOD_LINKAGE) + System.out.println("[TRACE_METHOD_LINKAGE] forceLookupParameter "+bsm); + MethodHandle mh = MH_forceLookupParameter; + if (mh == null) { + try { + mh = IMPL_LOOKUP.findStatic(BootstrapMethodInvoker.class, "forceLookupParameter", + MethodType.methodType(Object.class, MethodHandle.class, + Lookup.class, Object[].class)); + } catch (ReflectiveOperationException ex) { + throw new InternalError(ex); + } + MH_forceLookupParameter = mh; } + return mh.bindTo(bsm).withVarargs(true); } - - private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, - String name, Object type, Object arg0, Object arg1, - Object arg2, Object arg3, Object arg4, Object arg5) throws Throwable { - if (type instanceof Class) { - return bootstrapMethod.invoke(caller, name, (Class)type, arg0, arg1, arg2, arg3, arg4, arg5); - } else { - return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4, arg5); - } + private static @Stable MethodHandle MH_forceLookupParameter; + private static Object forceLookupParameter(MethodHandle bsm, + Lookup lookup, + Object... otherArgs) throws Throwable { + // There are often faster ways to get this done, but this formulation + // covers all the corner cases. Poorly typed BSMs are rare, anyway. + Object[] args = new Object[1 + otherArgs.length]; + args[0] = lookup; System.arraycopy(otherArgs, 0, args, 1, otherArgs.length); + return bsm.invokeWithArguments(args); } - private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Lookup caller, - String name, Object type, Object[] argv) throws Throwable { - 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); - return 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); - return spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv); - } - } + /// Signatures which have bespoke invocation paths: + // Lambda metafactory, standard. private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class, Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class); + // Lambda metafactory, alternate. private static final MethodType LMF_ALT_MT = MethodType.methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object[].class); + // Lambda metafactory, condy. private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class, Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class); + // String concat metafactory. 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( - * MethodHandles.Lookup,String,Class,MethodType,MethodHandle,MethodType)} - */ - private static boolean isLambdaMetafactoryCondyBSM(MethodType bsmType) { - return bsmType == LMF_CONDY_MT; - } - - /** - * @return true iff the BSM method type exactly matches - * {@see java.lang.invoke.LambdaMetafactory#metafactory( - * MethodHandles.Lookup,String,MethodType,MethodType,MethodHandle,MethodType)} - */ - private static boolean isLambdaMetafactoryIndyBSM(MethodType bsmType) { - return bsmType == LMF_INDY_MT; - } - - /** - * @return true iff the BSM method type exactly matches - * {@see java.lang.invoke.LambdaMetafactory#altMetafactory( - * MethodHandles.Lookup,String,MethodType,Object[])} - */ - private static boolean isLambdaMetafactoryAltMetafactoryBSM(MethodType bsmType) { - return bsmType == LMF_ALT_MT; - } - /** The JVM produces java.lang.Integer values to box * CONSTANT_Integer boxes but does not intern them. * Let's intern them. This is slightly wrong for * a {@code CONSTANT_Dynamic} which produces an * un-interned integer (e.g., {@code new Integer(0)}). */ - private static Object maybeReBox(Object x) { + static Object maybeReBox(Object x) { if (x instanceof Integer) { int xi = (int) x; if (xi == (byte) xi) @@ -348,72 +197,387 @@ return x; } - private static void maybeReBoxElements(Object[] xa) { + static void maybeReBoxElements(Object[] xa) { for (int i = 0; i < xa.length; i++) { xa[i] = maybeReBox(xa[i]); } } + static Object invokeCommon(MethodHandle handle, + Lookup lookup, + String name, + TypeDescriptor type, + Object[] argv, + List args) + throws Throwable { + // Efficiently invoke a method (probably a BSM) on a particular + // set of bootstrap arguments, perhaps including the usual + // metadata values of (lookup, name, type). + // To avoid copying, source the arguments from whatever + // the caller has on hand: A Java array, a List, or both. + // + // Support fast arity-specific invocation paths for both + // method types and field types (Class and MethodType), and + // no-metadata calls also. To get to these paths as often + // as possible, executet some varargs processing directly. + // + // For further optimization we special-case various known BSMs, + // including LambdaMetafactory::metafactory and + // StringConcatFactory::makeConcatWithConstants. + // (Actually, we special-case their *types*, which amounts + // to the same thing.) + // + // By providing static type information or even invoking + // exactly, we avoid emitting code to perform runtime + // checking. + // + // Because we usually don't examine the BSM return type, + // we can't use the MH.invokeExact mode as much as we would + // like, but at least the MH.invoke path has a reasonably + // small type mismatch. + + if (TRACE_METHOD_LINKAGE) { + System.out.println("invoking " + handle + " on " + (argv == null ? args : Arrays.asList(argv))); + } + int argc = argv != null ? argv.length : args.size(); + + boolean isFieldType = (type instanceof Class); + boolean isMethodType = (type instanceof MethodType); + + boolean passMetadata = (lookup != null); + final int MAX_ARITY = 7, FT = MAX_ARITY+1, MT = FT*2, NMD = 0; + final int METADATA_ARG_COUNT = 3; // (lookup, name, type) + final int metaArgc = passMetadata ? METADATA_ARG_COUNT : 0; + if (passMetadata) { + requireNonNull(name); + requireNonNull(type); + } + + MethodType handleType = handle.type(); + boolean handleVarargs = handle.isVarargsCollector(); + + if (!passMetadata) { + // Must not lead with a Lookup formal parameter type. + // Lookup leading types are reserved for metadata-using BSMs. + assert(handleType.parameterCount() == 0 || + handleType.parameterType(0) != Lookup.class); + assert(name == null); + assert(type == null); + } else if (isFieldType) { + // With condy, we require an explicit leading type of Lookup, + // if metadata is to be passed. + assert(handleType.parameterCount() == 0 || + handleType.parameterType(0) == Lookup.class); + // Also, the caller should not try to handle a BSCI call here. + assert(handleType.parameterCount() != 2 || + handleType.parameterType(1) != BootstrapCallInfo.class); + // Otherwise, we are doing an old-style indy bootstrap, + // which tolerates Object in the metadata positions. + } + + // Some varargs methods can be handled quickly here, + // rather than going through more dynamic invocation paths. + // Do the varargs spreading if: the BSM is varargs, the BSM + // directly accepts at least the metadata arguments (if applicable), + // the given static arguments fill in the BSM's inital set + // of arguments, and the BSM's trailing argument is a + // reference array. In that case, split the static argument + // list into head and tail arrays, and link the latter onto the + // end of the former. + if (handleVarargs) { + int headArgc = (handleType.parameterCount() - 1) - metaArgc; + Class tailType = handleType.lastParameterType(); + Class tailElementType = tailType.getComponentType(); + if (headArgc >= 0 && + headArgc <= argc && + Object[].class.isAssignableFrom(tailType) && + tailElementType != null) { + // Make a shortened argument list for a fixed-arity call. + Object[] headArgv = new Object[headArgc + 1]; + if (argv != null) { + System.arraycopy(argv, 0, headArgv, 0, headArgc); + } else { + List headArgs = args.subList(0, headArgc); + headArgv = headArgs.toArray(headArgv); + } + // Collect the trailing arguments into an array of the + // required type. Also, make sure they fit into the array. + int tailArgc = argc - headArgc; + Object[] tailArgv = (tailElementType == Object.class) + ? new Object[tailArgc] + : (Object[]) Array.newInstance(tailElementType, tailArgc); + try { + if (argv != null) { + System.arraycopy(argv, headArgc, tailArgv, 0, tailArgc); + } else { + List tailArgs = args.subList(headArgc, argc); + tailArgv = tailArgs.toArray(tailArgv); + } + } catch (ArrayStoreException ex) { + // Oops, one of the actuals doesn't fit in the varargs formal. + // For example, f(String...) is passed ("foo", 42, "bar"). + tailArgv = null; // bail out + } + if (tailArgv != null) { + // Link the head to the tail: + headArgv[headArgc] = tailArgv; + // Now swap in a new MH and arglist: + handle = handle.asFixedArity(); + assert(handleType == handle.type()); // still true + handleVarargs = false; + argv = headArgv; + args = null; + argc = headArgc + 1; + } + } + } + + // Now try to make an inline call: + if (argc <= MAX_ARITY && argv == null) + argv = args.toArray(); + + assert(argv == null || argc == argv.length); + assert(args == null || argc == args.size()); + + // If we don't provide static type information for the type, + // we'll generate runtime checks. The cases marked FT and MT + // avoid that problem. + + switch (argc > MAX_ARITY ? -1 : + argc + (isFieldType ? FT : isMethodType ? MT : NMD)) { + // Fixed-arity cases for a Class (field) type: + case 0+FT: + return handle.invoke(lookup, name, (Class)type); + case 1+FT: + return handle.invoke(lookup, name, (Class)type, + argv[0]); + case 2+FT: + return handle.invoke(lookup, name, (Class)type, + argv[0], argv[1]); + case 3+FT: + if (handleType == LMF_CONDY_MT && !handleVarargs) { + return handle.invokeExact(lookup, name, (Class)type, + (MethodType)argv[0], + (MethodHandle)argv[1], + (MethodType)argv[2]); + } + return handle.invoke(lookup, name, (Class)type, + argv[0], argv[1], argv[2]); + case 4+FT: + return handle.invoke(lookup, name, (Class)type, + argv[0], argv[1], argv[2], argv[3]); + case 5+FT: + return handle.invoke(lookup, name, (Class)type, + argv[0], argv[1], argv[2], argv[3], + argv[4]); + case 6+FT: + return handle.invoke(lookup, name, (Class)type, + argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5]); + case MAX_ARITY+FT: + return handle.invoke(lookup, name, (Class)type, + argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6]); + + // Same cases, but for a MethodType argument: + case 0+MT: + return handle.invoke(lookup, name, (MethodType)type); + case 1+MT: + if (handleType == LMF_ALT_MT && !handleVarargs) { + return (CallSite) + handle.invokeExact(lookup, name, (MethodType)type, + (Object[])argv[0]); + } + return handle.invoke(lookup, name, (MethodType)type, + argv[0]); + case 2+MT: + if (handleType == SCF_MT && !handleVarargs) { + return (CallSite) + handle.invokeExact(lookup, name, (MethodType)type, + (String)argv[0], + (Object[])argv[1]); + } + return handle.invoke(lookup, name, (MethodType)type, + argv[0], argv[1]); + case 3+MT: + if (handleType == LMF_INDY_MT && !handleVarargs) { + return (CallSite) + handle.invokeExact(lookup, name, (MethodType)type, + (MethodType)argv[0], + (MethodHandle)argv[1], + (MethodType)argv[2]); + } + return handle.invoke(lookup, name, (MethodType)type, + argv[0], argv[1], argv[2]); + case 4+MT: + return handle.invoke(lookup, name, (MethodType)type, + argv[0], argv[1], argv[2], argv[3]); + case 5+MT: + return handle.invoke(lookup, name, (MethodType)type, + argv[0], argv[1], argv[2], argv[3], + argv[4]); + case 6+MT: + return handle.invoke(lookup, name, (MethodType)type, + argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5]); + case MAX_ARITY+MT: + return handle.invoke(lookup, name, (MethodType)type, + argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6]); + + // Same cases again, but without metadata: + case 0+NMD: + return handle.invoke(); + case 1+NMD: + return handle.invoke(argv[0]); + case 2+NMD: + return handle.invoke(argv[0], argv[1]); + case 3+NMD: + return handle.invoke(argv[0], argv[1], argv[2]); + case 4+NMD: + return handle.invoke(argv[0], argv[1], argv[2], argv[3]); + case 5+NMD: + return handle.invoke(argv[0], argv[1], argv[2], argv[3], + argv[4]); + case 6+NMD: + return handle.invoke(argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5]); + case MAX_ARITY+NMD: + return handle.invoke(argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6]); + } + + // We have to stop at some point and use invokeWithArguments instead. + // Note that invokeWithArguments handles jumbo argument lists. + + if (passMetadata) { + Object[] allArgv = new Object[metaArgc + argc]; + int pos = 0; + allArgv[pos++] = lookup; + allArgv[pos++] = name; + allArgv[pos++] = type; + assert(pos == metaArgc); + if (argv != null) { + System.arraycopy(argv, 0, allArgv, pos, argc); + pos += argc; + } else { + for (Object arg : args) { + allArgv[pos++] = arg; + } + } + assert(pos == allArgv.length); + return handle.invokeWithArguments(allArgv); + } else if (argv != null) { + // It's simple if there are no metadata arguments to prepend. + return handle.invokeWithArguments(argv); + } else { + return handle.invokeWithArguments(args); + } + } + /** Canonical VM-aware implementation of BootstrapCallInfo. * Knows how to dig into the JVM for lazily resolved (pull-mode) constants. */ - private static final class VM_BSCI extends BSCIWithCache { - private final int[] indexInfo; - private final Class caller; // for index resolution only - - VM_BSCI(MethodHandle bsm, String name, T type, - Lookup lookup, int[] indexInfo) { - super(bsm, name, type, indexInfo[0]); - if (!lookup.hasPrivateAccess()) //D.I.D. - throw new AssertionError("bad Lookup object"); - this.caller = lookup.lookupClass(); - this.indexInfo = indexInfo; - // scoop up all the easy stuff right away: - prefetchIntoCache(0, size()); + static final class VM_BSCI> extends WithCache { + private final int bssIndex; + private final int indyIndex; + private final ConstantPool pool; + private int[] argIndexes; // lazy + + public VM_BSCI(ConstantPool pool, int bssIndex, int indyIndex, + MethodHandle bsm, String name, TypeView type, Object[] arguments) { + super(bsm, name, type, arguments); + this.pool = pool; + this.bssIndex = bssIndex; + this.indyIndex = indyIndex; } - @Override Object fillCache(int i) { + /** Special VM-specific hook to resolve static arguments. */ + @Override Object resolveConstant(int i) { Object[] buf = { null }; - copyConstants(i, i+1, buf, 0); - Object res = wrapNull(buf[0]); - cache[i] = res; + copyVMArguments(i, i+1, buf, 0, BAR_FORCE, null); + Object res = buf[0]; int next = i + 1; if (next < cache.length && cache[next] == null) - maybePrefetchIntoCache(next, false); // try to prefetch + maybePrefetchIntoCache(cache, next, false); // try to prefetch return res; } - @Override public int copyConstants(int start, int end, - Object[] buf, int pos) { + /** Special VM-specific hook to find symbolic references. */ + @Override ConstantDesc findSymbol(int i) { + Object[] buf = { null }; + copyVMArguments(i, i+1, buf, 0, BAR_SYMREF, null); + ConstantDesc res = (ConstantDesc) buf[0]; + //int next = i + 1; + //if (next < symCache.length && symCache[next] == null) + // maybePrefetchIntoCache(symCache, next, false); // try to prefetch + return res; + } + + private Class caller() { return pool.getHolder(); } + + /*non-public contract with ArgList*/ + void copyVMArguments(int start, int end, + Object[] buf, int pos, + byte resolving, Object ifNotPresent) { + if (buf.getClass() != Object[].class) throw new InternalError(); int i = start, bufi = pos; - while (i < end) { + if (resolving == BAR_SYMREF) { + while (i < end) { + Object x = symCache[i]; + if (x == null) break; + buf[bufi++] = x; + i++; + } + if (i >= end) return; + // pull in all of the symbols into symCache + int[] temp = new int[end - i]; + int tempi = 0; + pool.copyOutBootstrapArgumentsAt(bssIndex, + start, end, temp, 0, + BAR_SYMREF, null, null, false); + for (; i < end; i++) { + ConstantDesc x = symCache[i]; + int symIndex = temp[tempi++]; + if (x == null) { + x = makeConstantFromPool(symIndex); + symCache[i] = x; + } + buf[bufi++] = x; + } + return; + } + assert(resolving == BAR_IFPRESENT || resolving == BAR_FORCE); + for (; i < end; i++) { Object x = cache[i]; if (x == null) break; buf[bufi++] = unwrapNull(x); - i++; } // give up at first null and grab the rest in one big block - if (i >= end) return i; - Object[] temp = new Object[end - i]; + if (i >= end) return; if (TRACE_METHOD_LINKAGE) { System.out.println("resolving more BSM arguments: " + - Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end)); + Arrays.asList(caller().getSimpleName(), bssIndex, indyIndex, i, end)); } - copyOutBootstrapArguments(caller, indexInfo, - i, end, temp, 0, - true, null); - for (Object x : temp) { - x = maybeReBox(x); - buf[bufi++] = x; - cache[i++] = wrapNull(x); + pool.copyOutBootstrapArgumentsAt(bssIndex, + i, end, cache, i, + resolving, null, wrapNull(null), true); + for (; i < end; i++) { + Object x = cache[i]; + Object x2 = maybeReBox(x); + if (x2 != x) cache[i] = x = x2; + buf[bufi++] = (x == null) ? ifNotPresent : unwrapNull(x); } if (end < cache.length && cache[end] == null) - maybePrefetchIntoCache(end, true); // try to prefetch - return i; + maybePrefetchIntoCache(cache, end, true); // try to prefetch + } + + private ConstantDesc makeConstantFromPool(int symIndex) { + return pool.getConstantDescAt(symIndex); } private static final int MIN_PF = 4; - private void maybePrefetchIntoCache(int i, boolean bulk) { + private void maybePrefetchIntoCache(Object[] cache, int i, boolean bulk) { int len = cache.length; assert(0 <= i && i <= len); int pfLimit = i; @@ -438,137 +602,88 @@ } if (bulk && empty < MIN_PF && pfLimit < len) return; // not worth the effort - prefetchIntoCache(i, pfLimit); + prefetchIntoCache(cache, i, pfLimit); } - private void prefetchIntoCache(int i, int pfLimit) { + private void prefetchIntoCache(Object[] cache, int i, int pfLimit) { if (pfLimit <= i) return; // corner case - Object[] temp = new Object[pfLimit - i]; if (TRACE_METHOD_LINKAGE) { System.out.println("prefetching BSM arguments: " + - Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit)); - } - copyOutBootstrapArguments(caller, indexInfo, - i, pfLimit, temp, 0, - false, NOT_PRESENT); - for (Object x : temp) { - if (x != NOT_PRESENT && cache[i] == null) { - cache[i] = wrapNull(maybeReBox(x)); - } - i++; + Arrays.asList(caller().getSimpleName(), bssIndex, indyIndex, i, pfLimit)); } + pool.copyOutBootstrapArgumentsAt(bssIndex, + i, pfLimit, cache, i, + BAR_IFPRESENT, null, wrapNull(null), true); + } + + void setArgIndexes(int[] argIndexes) { + assert(this.argIndexes == null); + this.argIndexes = argIndexes; } } - /*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); + /** Canonical DynamicConstantDesc implementation of BootstrapCallInfo. + */ + static final class DCD_BSCI extends WithCache> { + private Lookup lookup; + private DynamicConstantDesc desc; + private final List> args; + + public DCD_BSCI(Lookup lookup, DynamicConstantDesc desc) throws ReflectiveOperationException { + super(desc.bootstrapMethod().resolveConstantDesc(lookup), + desc.constantName(), + new TypeView>(desc.constantType(), lookup), + desc.bootstrapArgs()); + this.lookup = lookup; + this.desc = desc; + this.args = desc.bootstrapArgsList(); } - static final MethodHandle MH_pushToBootstrapMethod; - static { - final Class THIS_CLASS = PushAdapter.class; + @Override Object resolveConstant(int i) throws BootstrapMethodError { try { - MH_pushToBootstrapMethod = IMPL_LOOKUP - .findStatic(THIS_CLASS, "pushToBootstrapMethod", - MethodType.methodType(Object.class, MethodHandle.class, - Lookup.class, String.class, Object.class, Object[].class)); - } catch (Throwable ex) { - throw new InternalError(ex); + return argumentDesc(i).resolveConstantDesc(lookup); + } catch (ReflectiveOperationException ex) { + throw new BootstrapMethodError(ex); } } - } - /*non-public*/ static final - class PullAdapter { - // skeleton for pull-mode BSM which wraps a push-mode BSM: - static Object pullFromBootstrapMethod(MethodHandle pushModeBSM, - MethodHandles.Lookup lookup, - BootstrapCallInfo bsci) - throws Throwable { - int argc = bsci.size(); - switch (argc) { - case 0: - return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType()); - case 1: - return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), - bsci.get(0)); - case 2: - return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), - bsci.get(0), bsci.get(1)); - case 3: - return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), - bsci.get(0), bsci.get(1), bsci.get(2)); - case 4: - return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), - bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3)); - case 5: - return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), - bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4)); - case 6: - return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), - bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5)); - default: - final int NON_SPREAD_ARG_COUNT = 3; // (lookup, name, type) - final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT; - if (argc >= MAX_SAFE_SIZE) { - // to be on the safe side, use invokeWithArguments which handles jumbo lists - Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argc]; - newargv[0] = lookup; - newargv[1] = bsci.invocationName(); - newargv[2] = bsci.invocationType(); - bsci.copyConstants(0, argc, newargv, NON_SPREAD_ARG_COUNT); - return pushModeBSM.invokeWithArguments(newargv); - } - MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argc); - MethodHandle typedBSM = pushModeBSM.asType(invocationType); - MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); - Object[] argv = new Object[argc]; - bsci.copyConstants(0, argc, argv, 0); - return spreader.invokeExact(typedBSM, (Object) lookup, (Object) bsci.invocationName(), bsci.invocationType(), argv); - } + @Override + ConstantDesc findSymbol(int i) { + return args.get(i); } + } - static final MethodHandle MH_pullFromBootstrapMethod; - - 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) { - throw new InternalError(ex); - } - } + /** + * Create a BootstrapCallInfo whose symbolic content is derived from the given + * descriptor. When it resolves constants, it will use the given Lookup object. + * @param desc the dynamic constant descriptor + * @param lookup a lookup object which is capable of resolving the dynamic constant + * @return a BootstrapCallInfo which carries the given + * @throws ReflectiveOperationException if the bootstrap method fails to resolve + */ + static BootstrapCallInfo> ofConstantDesc(DynamicConstantDesc desc, Lookup lookup) + throws ReflectiveOperationException { + return new BootstrapMethodInvoker.DCD_BSCI(lookup, desc); } - /** 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.) + /** + * Resolve a dynamic constant, by creating a temporary BootstrapCallInfo for it + * and immediately invoking its resolution behavior. Any cached resolution state + * will be lost after the temporary BootstrapCallInfo is discarded. + * @param desc the dynamic constant descriptor + * @param lookup a lookup object which is capable of resolving the dynamic constant + * @return the result of resolving the descriptor in the lookup class + * @throws ReflectiveOperationException */ - 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); + static Object resolveDynamicConstant(DynamicConstantDesc desc, Lookup lookup) + throws ReflectiveOperationException { + try { + return invoke(lookup, ofConstantDesc(desc, lookup)); + } catch (LinkageError ex) { + var roe = ex.getCause(); + if (roe instanceof ReflectiveOperationException) + throw (ReflectiveOperationException) roe; + throw ex; } } }