8199471: Enable generation of callSiteForms at link time
authorredestad
Wed, 14 Mar 2018 17:14:02 +0100
changeset 49240 5290e816c64c
parent 49239 c35ec365e329
child 49241 de4b3a04feae
8199471: Enable generation of callSiteForms at link time Reviewed-by: psandoz, mchung
src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java
src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
src/java.base/share/classes/java/lang/invoke/Invokers.java
src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java
src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
--- a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Wed Mar 14 11:23:16 2018 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Wed Mar 14 17:14:02 2018 +0100
@@ -133,7 +133,7 @@
     }
 
     static byte[] generateInvokersHolderClassBytes(String className,
-            MethodType[] methodTypes) {
+            MethodType[] invokerMethodTypes, MethodType[] callSiteMethodTypes) {
 
         HashSet<MethodType> dedupSet = new HashSet<>();
         ArrayList<LambdaForm> forms = new ArrayList<>();
@@ -144,17 +144,33 @@
             MethodTypeForm.LF_GEN_LINKER,
             MethodTypeForm.LF_GEN_INVOKER
         };
-        for (int i = 0; i < methodTypes.length; i++) {
+
+        for (int i = 0; i < invokerMethodTypes.length; i++) {
             // generate methods representing invokers of the specified type
-            if (dedupSet.add(methodTypes[i])) {
+            if (dedupSet.add(invokerMethodTypes[i])) {
                 for (int type : types) {
-                    LambdaForm invokerForm = Invokers.invokeHandleForm(methodTypes[i],
+                    LambdaForm invokerForm = Invokers.invokeHandleForm(invokerMethodTypes[i],
                             /*customized*/false, type);
                     forms.add(invokerForm);
                     names.add(invokerForm.kind.defaultLambdaName);
                 }
             }
         }
+
+        dedupSet = new HashSet<>();
+        for (int i = 0; i < callSiteMethodTypes.length; i++) {
+            // generate methods representing invokers of the specified type
+            if (dedupSet.add(callSiteMethodTypes[i])) {
+                LambdaForm callSiteForm = Invokers.callSiteForm(callSiteMethodTypes[i], true);
+                forms.add(callSiteForm);
+                names.add(callSiteForm.kind.defaultLambdaName);
+
+                LambdaForm methodHandleForm = Invokers.callSiteForm(callSiteMethodTypes[i], false);
+                forms.add(methodHandleForm);
+                names.add(methodHandleForm.kind.defaultLambdaName);
+            }
+        }
+
         return generateCodeBytesForLFs(className,
                 names.toArray(new String[0]),
                 forms.toArray(new LambdaForm[0]));
--- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Mar 14 11:23:16 2018 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Mar 14 17:14:02 2018 +0100
@@ -649,6 +649,8 @@
             }
             case EXACT_INVOKER:             // fall-through
             case EXACT_LINKER:              // fall-through
+            case LINK_TO_CALL_SITE:         // fall-through
+            case LINK_TO_TARGET_METHOD:     // fall-through
             case GENERIC_INVOKER:           // fall-through
             case GENERIC_LINKER:            return resolveFrom(name, invokerType.basicType(), Invokers.Holder.class);
             case GET_OBJECT:                // fall-through
--- a/src/java.base/share/classes/java/lang/invoke/Invokers.java	Wed Mar 14 11:23:16 2018 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/Invokers.java	Wed Mar 14 17:14:02 2018 +0100
@@ -523,7 +523,7 @@
     }
 
     // skipCallSite is true if we are optimizing a ConstantCallSite
-    private static LambdaForm callSiteForm(MethodType mtype, boolean skipCallSite) {
+    static LambdaForm callSiteForm(MethodType mtype, boolean skipCallSite) {
         mtype = mtype.basicType();  // normalize Z to I, String to Object, etc.
         final int which = (skipCallSite ? MethodTypeForm.LF_MH_LINKER : MethodTypeForm.LF_CS_LINKER);
         LambdaForm lform = mtype.form().cachedLambdaForm(which);
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Mar 14 11:23:16 2018 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Mar 14 17:14:02 2018 +0100
@@ -1841,10 +1841,13 @@
 
             @Override
             public byte[] generateInvokersHolderClassBytes(final String className,
-                    MethodType[] methodTypes) {
+                    MethodType[] invokerMethodTypes,
+                    MethodType[] callSiteMethodTypes) {
                 return GenerateJLIClassesHelper
-                        .generateInvokersHolderClassBytes(className, methodTypes);
+                        .generateInvokersHolderClassBytes(className,
+                                invokerMethodTypes, callSiteMethodTypes);
             }
+
         });
     }
 
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java	Wed Mar 14 11:23:16 2018 +0100
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java	Wed Mar 14 17:14:02 2018 +0100
@@ -84,7 +84,7 @@
     /**
      * Returns a {@code byte[]} representation of {@code BoundMethodHandle}
      * species class implementing the signature defined by {@code types}. Used
-     * by GenerateBMHClassesPlugin to enable generation of such classes during
+     * by GenerateJLIClassesPlugin to enable generation of such classes during
      * the jlink phase. Should do some added validation since this string may be
      * user provided.
      */
@@ -99,8 +99,11 @@
 
     /**
      * Returns a {@code byte[]} representation of a class implementing
-     * the invoker forms for the set of supplied {@code methodTypes}.
+     * the invoker forms for the set of supplied {@code invokerMethodTypes}
+     * and {@code callSiteMethodTypes}.
      */
     byte[] generateInvokersHolderClassBytes(String className,
-            MethodType[] methodTypes);
+            MethodType[] invokerMethodTypes,
+            MethodType[] callSiteMethodTypes);
+
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Wed Mar 14 11:23:16 2018 +0100
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Wed Mar 14 17:14:02 2018 +0100
@@ -69,7 +69,9 @@
 
     private static final String DELEGATING_HOLDER = "java/lang/invoke/DelegatingMethodHandle$Holder";
     private static final String BASIC_FORMS_HOLDER = "java/lang/invoke/LambdaForm$Holder";
-    private static final String INVOKERS_HOLDER = "java/lang/invoke/Invokers$Holder";
+
+    private static final String INVOKERS_HOLDER_NAME = "java.lang.invoke.Invokers$Holder";
+    private static final String INVOKERS_HOLDER_INTERNAL_NAME = INVOKERS_HOLDER_NAME.replace('.', '/');
 
     private static final JavaLangInvokeAccess JLIA
             = SharedSecrets.getJavaLangInvokeAccess();
@@ -78,6 +80,8 @@
 
     Set<String> invokerTypes = Set.of();
 
+    Set<String> callSiteTypes = Set.of();
+
     Map<String, Set<String>> dmhMethods = Map.of();
 
     String mainArgument;
@@ -128,7 +132,7 @@
      * @return the default invoker forms to generate.
      */
     private static Set<String> defaultInvokers() {
-        return Set.of("LL_L", "LL_I", "LILL_I", "L6_L");
+        return Set.of("LL_L", "LL_I", "LLLL_L", "LLLL_I", "LLIL_L", "LLIL_I", "L6_L");
     }
 
     /**
@@ -209,6 +213,8 @@
         // ease finding methods in the generated code
         speciesTypes = new TreeSet<>(speciesTypes);
         invokerTypes = new TreeSet<>(invokerTypes);
+        callSiteTypes = new TreeSet<>(callSiteTypes);
+
         TreeMap<String, Set<String>> newDMHMethods = new TreeMap<>();
         for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
             newDMHMethods.put(entry.getKey(), new TreeSet<>(entry.getValue()));
@@ -229,8 +235,13 @@
                     case "[LF_RESOLVE]":
                         String methodType = parts[3];
                         validateMethodType(methodType);
-                        if (parts[1].contains("Invokers")) {
-                            invokerTypes.add(methodType);
+                        if (parts[1].equals(INVOKERS_HOLDER_NAME)) {
+                            if ("linkToTargetMethod".equals(parts[2]) ||
+                                    "linkToCallSite".equals(parts[2])) {
+                                callSiteTypes.add(methodType);
+                            } else {
+                                invokerTypes.add(methodType);
+                            }
                         } else if (parts[1].contains("DirectMethodHandle")) {
                             String dmh = parts[2];
                             // ignore getObject etc for now (generated
@@ -294,10 +305,11 @@
         // Copy all but DMH_ENTRY to out
         in.transformAndCopy(entry -> {
                 // filter out placeholder entries
-                if (entry.path().equals(DIRECT_METHOD_HOLDER_ENTRY) ||
-                    entry.path().equals(DELEGATING_METHOD_HOLDER_ENTRY) ||
-                    entry.path().equals(INVOKERS_HOLDER_ENTRY) ||
-                    entry.path().equals(BASIC_FORMS_HOLDER_ENTRY)) {
+                String path = entry.path();
+                if (path.equals(DIRECT_METHOD_HOLDER_ENTRY) ||
+                    path.equals(DELEGATING_METHOD_HOLDER_ENTRY) ||
+                    path.equals(INVOKERS_HOLDER_ENTRY) ||
+                    path.equals(BASIC_FORMS_HOLDER_ENTRY)) {
                     return null;
                 } else {
                     return entry;
@@ -361,23 +373,40 @@
                 index++;
             }
         }
+
+        // The invoker type to ask for is retrieved by removing the first
+        // and the last argument, which needs to be of Object.class
         MethodType[] invokerMethodTypes = new MethodType[this.invokerTypes.size()];
         int i = 0;
         for (String invokerType : invokerTypes) {
-            // The invoker type to ask for is retrieved by removing the first
-            // and the last argument, which needs to be of Object.class
             MethodType mt = asMethodType(invokerType);
             final int lastParam = mt.parameterCount() - 1;
             if (mt.parameterCount() < 2 ||
                     mt.parameterType(0) != Object.class ||
                     mt.parameterType(lastParam) != Object.class) {
                 throw new PluginException(
-                        "Invoker type parameter must start and end with L");
+                        "Invoker type parameter must start and end with Object: " + invokerType);
             }
             mt = mt.dropParameterTypes(lastParam, lastParam + 1);
             invokerMethodTypes[i] = mt.dropParameterTypes(0, 1);
             i++;
         }
+
+        // The callSite type to ask for is retrieved by removing the last
+        // argument, which needs to be of Object.class
+        MethodType[] callSiteMethodTypes = new MethodType[this.callSiteTypes.size()];
+        i = 0;
+        for (String callSiteType : callSiteTypes) {
+            MethodType mt = asMethodType(callSiteType);
+            final int lastParam = mt.parameterCount() - 1;
+            if (mt.parameterCount() < 1 ||
+                    mt.parameterType(lastParam) != Object.class) {
+                throw new PluginException(
+                        "CallSite type parameter must end with Object: " + callSiteType);
+            }
+            callSiteMethodTypes[i] = mt.dropParameterTypes(lastParam, lastParam + 1);
+            i++;
+        }
         try {
             byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(
                     DIRECT_HOLDER, directMethodTypes, dmhTypes);
@@ -390,8 +419,8 @@
             ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HOLDER_ENTRY, bytes);
             out.add(ndata);
 
-            bytes = JLIA.generateInvokersHolderClassBytes(INVOKERS_HOLDER,
-                    invokerMethodTypes);
+            bytes = JLIA.generateInvokersHolderClassBytes(INVOKERS_HOLDER_INTERNAL_NAME,
+                    invokerMethodTypes, callSiteMethodTypes);
             ndata = ResourcePoolEntry.create(INVOKERS_HOLDER_ENTRY, bytes);
             out.add(ndata);
 
@@ -409,7 +438,7 @@
     private static final String BASIC_FORMS_HOLDER_ENTRY =
             "/java.base/" + BASIC_FORMS_HOLDER + ".class";
     private static final String INVOKERS_HOLDER_ENTRY =
-            "/java.base/" + INVOKERS_HOLDER + ".class";
+            "/java.base/" + INVOKERS_HOLDER_INTERNAL_NAME + ".class";
 
     // Convert LL -> LL, L3 -> LLL
     public static String expandSignature(String signature) {