< prev index next >
src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java
Print this page
rev 52749 : Bootstrap method consolidation
* clean up and simplify JDK support code for BSM invocation
* simplify JVM bootstrap handshake: use BootstrapCallInfo only
* remove unused JVM paths and data fields
* move bootstrap argument processing from MethodHandleNatives to ConstantPool
* remove ConstantGroup; merge argument access into BootstrapCallInfo
* adjust BSM argument access: remove copyArguments, add argumentRef API
* add metadata-free BSM modes, including symbolic arguments from CP
@@ -22,166 +22,72 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
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 <T> the expected return type
+ * @param lookup the capability to access the caller class's constants
+ * @param bsci the BootstrapCallInfo provided by the JVM
+ * @param <T> the kind of the type argument, {@code Class} or {@code MethodType}
* @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) {
- MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
+ static <T extends TypeDescriptor & Constable<T>>
+ Object invoke(Lookup lookup,
+ BootstrapCallInfo<T> 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<Object> 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
// form of linkage error, such as IllegalAccessError if the bootstrap
// method is inaccessible, or say ThreadDeath/OutOfMemoryError
@@ -193,229 +99,487 @@
// Wrap anything else in BootstrapMethodError
throw new BootstrapMethodError("bootstrap method initialization exception", ex);
}
}
- // 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);
- }
+ /** 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 <em>not</em> {@code Lookup} <em>and</em>
+ * 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 <em>expression mode BSM</em>.
+ * 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) 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);
+ /** 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;
}
-
- 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);
+ return mh.bindTo(bsm).withVarargs(true);
}
+ 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 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 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)
x = xi; // must rebox; see JLS 5.1.7
}
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<Object> 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<Object> 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<Object> 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<T> extends BSCIWithCache<T> {
- 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<T extends TypeDescriptor & Constable<T>> extends WithCache<T> {
+ 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<T> 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;
+ 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;
if (bulk) pfLimit += i; // exponential prefetch expansion
// try to prefetch at least MIN_PF elements
@@ -436,139 +600,90 @@
if (pfLimit < len) pfLimit++;
}
}
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));
+ Arrays.asList(caller().getSimpleName(), bssIndex, indyIndex, i, pfLimit));
}
- i++;
+ 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<Class<?>> {
+ private Lookup lookup;
+ private DynamicConstantDesc<?> desc;
+ private final List<ConstantDesc<?>> args;
+
+ public DCD_BSCI(Lookup lookup, DynamicConstantDesc<?> desc) throws ReflectiveOperationException {
+ super(desc.bootstrapMethod().resolveConstantDesc(lookup),
+ desc.constantName(),
+ new TypeView<Class<?>>(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<Class<?>> 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;
}
}
}
< prev index next >