< 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,40 ****
--- 24,47 ----
*/
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,79 ****
/** 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) {
--- 71,80 ----
*** 134,143 ****
--- 135,149 ----
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,221 ****
}
}
private static native int getNamedCon(int which, Object[] name);
static boolean verifyConstants() {
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 (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);
--- 203,230 ----
}
}
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);
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,374 ****
}
}
return true;
}
static {
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,
Object[] appendixResult) {
! CallSite callSite = CallSite.makeSite(bootstrapMethod,
! name,
! type,
! staticArguments,
! caller);
! if (callSite instanceof ConstantCallSite) {
! appendixResult[0] = callSite.dynamicInvoker();
! return Invokers.linkToTargetMethod(type);
} else {
! appendixResult[0] = callSite;
! return Invokers.linkToCallSiteMethod(type);
! }
}
! // 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]);
return res;
- } catch (Throwable ex) {
- ex.printStackTrace(); // print now in case exception is swallowed
- System.out.println("linkCallSite => throw "+ex);
- throw ex;
}
}
! // 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);
try {
! Object res = linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments);
! System.out.println("linkDynamicConstantImpl => "+res);
! return res;
} catch (Throwable ex) {
ex.printStackTrace(); // print now in case exception is swallowed
! System.out.println("linkDynamicConstant => 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();
}
/**
* The JVM wants a pointer to a MethodType. Oblige it by finding or creating one.
*/
--- 232,375 ----
}
}
return true;
}
static {
+ if (TRACE_RESOLVE) {
+ boolean ok = verifyConstants();
+ assert(ok);
+ } else {
assert(verifyConstants());
}
+ }
// Up-calls from the JVM.
// These must NOT be public.
/**
! * 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) {
! 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 {
! // 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);
}
! // 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;
}
+ return encodeBindingViaAppendix(bsci, res, appendixResult);
}
! // 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 = 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(linkWhat+" => throw "+ex);
throw ex;
}
}
! // 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,480 ****
--- 472,482 ----
// 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 >