< 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 >