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