< prev index next >
src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.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
@@ -24,17 +24,24 @@
*/
package java.lang.invoke;
import jdk.internal.ref.CleanerFactory;
+import jdk.internal.reflect.ConstantPool;
import sun.invoke.util.Wrapper;
+import java.lang.constant.ClassDesc;
+import java.lang.constant.MethodTypeDesc;
+import java.lang.invoke.AbstractBootstrapCallInfo.TypeView;
+import java.lang.invoke.BootstrapMethodInvoker.VM_BSCI;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
+import java.util.Arrays;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
+import static java.lang.invoke.MethodHandleStatics.TRACE_RESOLVE;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
* The JVM interface for the method handles package is all here.
* This is an interface internal and private to an implementation of JSR 292.
@@ -64,16 +71,10 @@
/** Tell the JVM that we need to change the target of a CallSite. */
static native void setCallSiteTargetNormal(CallSite site, MethodHandle target);
static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target);
- static native void copyOutBootstrapArguments(Class<?> caller, int[] indexInfo,
- int start, int end,
- Object[] buf, int pos,
- boolean resolve,
- Object ifNotAvailable);
-
/** Represents a context to track nmethod dependencies on CallSite instance target. */
static class CallSiteContext implements Runnable {
//@Injected JVM_nmethodBucket* vmdependencies;
static CallSiteContext make(CallSite cs) {
@@ -134,10 +135,15 @@
REF_invokeStatic = 6,
REF_invokeSpecial = 7,
REF_newInvokeSpecial = 8,
REF_invokeInterface = 9,
REF_LIMIT = 10;
+
+ /**
+ * Flag values which affect the copying of bootstrap static arguments.
+ */
+ static final byte BAR_IFPRESENT = 0, BAR_FORCE = 1, BAR_SYMREF = 2;
}
static boolean refKindIsValid(int refKind) {
return (refKind > REF_NONE && refKind < REF_LIMIT);
}
@@ -197,25 +203,28 @@
}
}
private static native int getNamedCon(int which, Object[] name);
static boolean verifyConstants() {
+ // This code requires ADVERTISE_CON_VALUES to be turned on
+ // in methodHandles.cpp, which is usually only true in debug builds.
+ // If that is not the case, then getNamedCon returns nothing and
+ // the loop body never runs.
Object[] box = { null };
for (int i = 0; ; i++) {
box[0] = null;
int vmval = getNamedCon(i, box);
if (box[0] == null) break;
String name = (String) box[0];
try {
Field con = Constants.class.getDeclaredField(name);
int jval = con.getInt(null);
+ if (TRACE_RESOLVE) {
+ System.out.println("[CON_RESOLVE] " + name + " = " + vmval);
+ }
if (jval == vmval) continue;
String err = (name+": JVM has "+vmval+" while Java has "+jval);
- if (name.equals("CONV_OP_LIMIT")) {
- System.err.println("warning: "+err);
- continue;
- }
throw new InternalError(err);
} catch (NoSuchFieldException | IllegalAccessException ex) {
String err = (name+": JVM has "+vmval+" which Java does not define");
// ignore exotic ops the JVM cares about; we just wont issue them
//System.err.println("warning: "+err);
@@ -223,152 +232,144 @@
}
}
return true;
}
static {
+ if (TRACE_RESOLVE) {
+ boolean ok = verifyConstants();
+ assert(ok);
+ } else {
assert(verifyConstants());
}
+ }
// Up-calls from the JVM.
// These must NOT be public.
/**
- * The JVM is linking an invokedynamic instruction. Create a reified call site for it.
- */
- static MemberName linkCallSite(Object callerObj,
- int indexInCP,
- Object bootstrapMethodObj,
- Object nameObj, Object typeObj,
- Object staticArguments,
- Object[] appendixResult) {
- MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
- Class<?> caller = (Class<?>)callerObj;
- String name = nameObj.toString().intern();
- MethodType type = (MethodType)typeObj;
- if (!TRACE_METHOD_LINKAGE)
- return linkCallSiteImpl(caller, bootstrapMethod, name, type,
- staticArguments, appendixResult);
- return linkCallSiteTracing(caller, bootstrapMethod, name, type,
- staticArguments, appendixResult);
- }
- static MemberName linkCallSiteImpl(Class<?> caller,
- MethodHandle bootstrapMethod,
- String name, MethodType type,
- Object staticArguments,
+ * All bootstrap method invocations funnel through this single method.
+ * A large subset of the values of the JVM's BootstrapInfo structure is
+ * made available here, basically everything that can be easily computed
+ * without a detectable side effect or exception. The callee is
+ * invited to call back the JVM for more information.
+ */
+ static Object bootstrapMethodHelper(ConstantPool pool,
+ int bssIndex,
+ int indyIndex,
+ int argc,
+ MethodHandle bsm,
+ String name,
+ Object type, // Class or MethodType or String
+ int[] argIndexes,
+ Object[] argValues,
+ Object request,
Object[] appendixResult) {
- CallSite callSite = CallSite.makeSite(bootstrapMethod,
- name,
- type,
- staticArguments,
- caller);
- if (callSite instanceof ConstantCallSite) {
- appendixResult[0] = callSite.dynamicInvoker();
- return Invokers.linkToTargetMethod(type);
+ assert((appendixResult != null) == (indyIndex != 0));
+ assert(request == (indyIndex == 0 ? null : CallSite.class));
+ // Special case: If the bsm does not begin with Lookup, *and*
+ // it is a CallSite request (i.e., legacy from Java 7), then
+ // forcibly convert the leading type to Lookup.
+ if (request == CallSite.class && BootstrapMethodInvoker.isExpressionBSM(bsm)) {
+ bsm = BootstrapMethodInvoker.forceLookupParameter(bsm);
+ assert(!BootstrapMethodInvoker.isExpressionBSM(bsm)); // properly wrapped
+ }
+ // Make a full-power lookup into the CP (a privileged operation).
+ Lookup lookup = IMPL_LOOKUP.in(pool.getHolder());
+ // Process the type: { field, method } x { resolved, string }
+ TypeView<Class<?>> ftype = null;
+ TypeView<MethodType> mtype = null;
+ if (type instanceof MethodType) {
+ mtype = new TypeView<>((MethodType)type);
+ } else if (type instanceof Class<?>) {
+ ftype = new TypeView<>((Class<?>)type);
} else {
- appendixResult[0] = callSite;
- return Invokers.linkToCallSiteMethod(type);
- }
+ // JVM was not able to resolve the type. Keep the ball rolling.
+ String desc = (String) type;
+ if (desc.startsWith("("))
+ mtype = new TypeView<>(MethodTypeDesc.ofDescriptor(desc), lookup);
+ else
+ ftype = new TypeView<>(ClassDesc.ofDescriptor(desc), lookup);
}
- // Tracing logic:
- static MemberName linkCallSiteTracing(Class<?> caller,
- MethodHandle bootstrapMethod,
- String name, MethodType type,
- Object staticArguments,
- Object[] appendixResult) {
- Object bsmReference = bootstrapMethod.internalMemberName();
- if (bsmReference == null) bsmReference = bootstrapMethod;
- String staticArglist = staticArglistForTrace(staticArguments);
- System.out.println("linkCallSite "+caller.getName()+" "+
- bsmReference+" "+
- name+type+"/"+staticArglist);
- try {
- MemberName res = linkCallSiteImpl(caller, bootstrapMethod, name, type,
- staticArguments, appendixResult);
- System.out.println("linkCallSite => "+res+" + "+appendixResult[0]);
+ // If VM is pushing up argument values, use them. Otherwise make a fresh buffer.
+ if (argValues == null) {
+ argValues = new Object[argc];
+ } else {
+ assert(argValues.length == argc);
+ // The JVM resolves CONSTANT_Integer CP items without calling Integer.valueOf.
+ BootstrapMethodInvoker.maybeReBoxElements(argValues);
+ }
+ // Make a BSCI wrapper for all of this state.
+ VM_BSCI<?> bsci;
+ // (The following if/then/else is required to make the generic types match up.)
+ if (ftype != null)
+ bsci = new VM_BSCI<>(pool, bssIndex, indyIndex, bsm, name, ftype, argValues);
+ else
+ bsci = new VM_BSCI<>(pool, bssIndex, indyIndex, bsm, name, mtype, argValues);
+ // If VM is passing up argument indexes, accept that also.
+ if (argIndexes != null) bsci.setArgIndexes(argIndexes);
+ // Finish the job by invoking the fresh BSCI under the Lookup:
+ if (!TRACE_METHOD_LINKAGE) {
+ Object res = BootstrapMethodInvoker.invoke(lookup, bsci);
+ if (appendixResult == null) {
return res;
- } catch (Throwable ex) {
- ex.printStackTrace(); // print now in case exception is swallowed
- System.out.println("linkCallSite => throw "+ex);
- throw ex;
}
+ return encodeBindingViaAppendix(bsci, res, appendixResult);
}
- // this implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
- static Object linkDynamicConstant(Object callerObj,
- int indexInCP,
- Object bootstrapMethodObj,
- Object nameObj, Object typeObj,
- Object staticArguments) {
- MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
- Class<?> caller = (Class<?>)callerObj;
- String name = nameObj.toString().intern();
- Class<?> type = (Class<?>)typeObj;
- if (!TRACE_METHOD_LINKAGE)
- return linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments);
- return linkDynamicConstantTracing(caller, bootstrapMethod, name, type, staticArguments);
- }
-
- static Object linkDynamicConstantImpl(Class<?> caller,
- MethodHandle bootstrapMethod,
- String name, Class<?> type,
- Object staticArguments) {
- return ConstantBootstraps.makeConstant(bootstrapMethod, name, type, staticArguments, caller);
- }
-
- private static String staticArglistForTrace(Object staticArguments) {
- if (staticArguments instanceof Object[])
- return "BSA="+java.util.Arrays.asList((Object[]) staticArguments);
- if (staticArguments instanceof int[])
- return "BSA@"+java.util.Arrays.toString((int[]) staticArguments);
- if (staticArguments == null)
- return "BSA0=null";
- return "BSA1="+staticArguments;
- }
-
- // Tracing logic:
- static Object linkDynamicConstantTracing(Class<?> caller,
- MethodHandle bootstrapMethod,
- String name, Class<?> type,
- Object staticArguments) {
- Object bsmReference = bootstrapMethod.internalMemberName();
- if (bsmReference == null) bsmReference = bootstrapMethod;
- String staticArglist = staticArglistForTrace(staticArguments);
- System.out.println("linkDynamicConstant "+caller.getName()+" "+
- bsmReference+" "+
- name+type+"/"+staticArglist);
+ // Tracing logic follows:
+ String linkWhat = "[TRACE_METHOD_LINKAGE] link"
+ + ((request == null) ? "DynamicConstant"
+ : request instanceof Class ? ((Class<?>)request).getSimpleName()
+ : request);
+ System.out.println(linkWhat+" "+bsci.toString());
try {
- Object res = linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments);
- System.out.println("linkDynamicConstantImpl => "+res);
- return res;
+ Object res = BootstrapMethodInvoker.invoke(lookup, bsci);
+ if (appendixResult == null) {
+ System.out.println(linkWhat+" => "+res); return res;
+ }
+ MemberName mem = encodeBindingViaAppendix(bsci, res, appendixResult);
+ System.out.println(linkWhat+" => "+res+" => "+mem+" + "+appendixResult[0]);
+ return mem;
} catch (Throwable ex) {
ex.printStackTrace(); // print now in case exception is swallowed
- System.out.println("linkDynamicConstant => throw "+ex);
+ System.out.println(linkWhat+" => throw "+ex);
throw ex;
}
}
- /** The JVM is requesting pull-mode bootstrap when it provides
- * a tuple of the form int[]{ argc, vmindex }.
- * The BSM is expected to call back to the JVM using the caller
- * class and vmindex to resolve the static arguments.
- */
- static boolean staticArgumentsPulled(Object staticArguments) {
- return staticArguments instanceof int[];
- }
-
- /** A BSM runs in pull-mode if and only if its sole arguments
- * are (Lookup, BootstrapCallInfo), or can be converted pairwise
- * to those types, and it is not of variable arity.
- * Excluding error cases, we can just test that the arity is a constant 2.
- *
- * NOTE: This method currently returns false, since pulling is not currently
- * exposed to a BSM. When pull mode is supported the method block will be
- * replaced with currently commented out code.
- */
- static boolean isPullModeBSM(MethodHandle bsm) {
- return false;
-// return bsm.type().parameterCount() == 2 && !bsm.isVarargsCollector();
+ // Follow-up step to bootstrapMethodHelper, when an appendix was provided.
+ // The basic result was a specific behavior; factor it (for the VM's sake)
+ // into a generic chunk of argument shuffling plus a datum.
+ static MemberName encodeBindingViaAppendix(BootstrapCallInfo<?> bsci, Object binding, Object[] appendixResult) {
+ MethodType type = (MethodType) bsci.invocationType();
+ MemberName result;
+ MethodType resultType;
+ Object appendix;
+ if (binding instanceof ConstantCallSite) {
+ ConstantCallSite ccs = (ConstantCallSite) binding;
+ result = Invokers.linkToTargetMethod(type);
+ appendix = ccs.getTarget();
+ resultType = ((MethodHandle) appendix).type();
+ } else if (binding instanceof CallSite) {
+ result = Invokers.linkToCallSiteMethod(type);
+ appendix = binding;
+ resultType = ((CallSite) appendix).type();
+ } else if (binding instanceof MethodHandle) {
+ result = Invokers.linkToTargetMethod(type);
+ appendix = binding;
+ resultType = ((MethodHandle) appendix).type();
+ } else {
+ throw new InternalError("should not get here: "+binding+":"+binding.getClass().getName());
+ }
+ // Double-check the function type also.
+ if (!resultType.equals(type)) {
+ throw new InternalError("should not get here: "+binding+type);
+ }
+ // After type checking, give the JVM the two bits it needs
+ // to implement the call site: A behavior method, and a cookie.
+ appendixResult[0] = appendix;
+ return result;
}
/**
* The JVM wants a pointer to a MethodType. Oblige it by finding or creating one.
*/
@@ -471,10 +472,11 @@
// Wrap anything else in LinkageError
throw new LinkageError(ex.getMessage(), ex);
}
throw new LinkageError("no such method "+defc.getName()+"."+name+type);
}
+
private static MethodType fixMethodType(Class<?> callerClass, Object type) {
if (type instanceof MethodType)
return (MethodType) type;
else
return MethodType.fromDescriptor((String)type, callerClass.getClassLoader());
< prev index next >