8164569: Generate non-customized invoker forms at link time
authorredestad
Wed, 24 Aug 2016 16:11:21 +0200
changeset 40543 629f1f599595
parent 40542 e7be26f852fa
child 40544 807dd9a425db
8164569: Generate non-customized invoker forms at link time Reviewed-by: vlivanov
jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java
jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java
jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
jdk/test/java/lang/StackWalker/VerifyStackTrace.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Wed Aug 24 16:11:21 2016 +0200
@@ -133,6 +133,34 @@
                 forms.toArray(new LambdaForm[0]));
     }
 
+    static byte[] generateInvokersHolderClassBytes(String className,
+            MethodType[] methodTypes) {
+
+        HashSet<MethodType> dedupSet = new HashSet<>();
+        ArrayList<LambdaForm> forms = new ArrayList<>();
+        ArrayList<String> names = new ArrayList<>();
+        int[] types = {
+            MethodTypeForm.LF_EX_LINKER,
+            MethodTypeForm.LF_EX_INVOKER,
+            MethodTypeForm.LF_GEN_LINKER,
+            MethodTypeForm.LF_GEN_INVOKER
+        };
+        for (int i = 0; i < methodTypes.length; i++) {
+            // generate methods representing invokers of the specified type
+            if (dedupSet.add(methodTypes[i])) {
+                for (int type : types) {
+                    LambdaForm invokerForm = Invokers.invokeHandleForm(methodTypes[i],
+                            /*customized*/false, type);
+                    forms.add(invokerForm);
+                    names.add(invokerForm.kind.defaultLambdaName);
+                }
+            }
+        }
+        return generateCodeBytesForLFs(className,
+                names.toArray(new String[0]),
+                forms.toArray(new LambdaForm[0]));
+    }
+
     /*
      * Generate customized code for a set of LambdaForms of specified types into
      * a class with a specified name.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Aug 24 16:11:21 2016 +0200
@@ -629,6 +629,10 @@
                 name = name + "_" + form.returnType().basicTypeChar();
                 return resolveFrom(name, invokerType, LambdaForm.Holder.class);
             }
+            case EXACT_INVOKER:             // fall-through
+            case EXACT_LINKER:              // fall-through
+            case GENERIC_INVOKER:           // fall-through
+            case GENERIC_LINKER:            return resolveFrom(name, invokerType.basicType(), Invokers.Holder.class);
             case GET_OBJECT:                // fall-through
             case GET_BOOLEAN:               // fall-through
             case GET_BYTE:                  // fall-through
--- a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java	Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java	Wed Aug 24 16:11:21 2016 +0200
@@ -36,6 +36,7 @@
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
 import static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.LambdaForm.Kind.*;
 
 /**
  * Construction and caching of often-used invokers.
@@ -254,7 +255,7 @@
      * @param which bit-encoded 0x01 whether it is a CP adapter ("linker") or MHs.invoker value ("invoker");
      *                          0x02 whether it is for invokeExact or generic invoke
      */
-    private static LambdaForm invokeHandleForm(MethodType mtype, boolean customized, int which) {
+    static LambdaForm invokeHandleForm(MethodType mtype, boolean customized, int which) {
         boolean isCached;
         if (!customized) {
             mtype = mtype.basicType();  // normalize Z to I, String to Object, etc.
@@ -263,12 +264,12 @@
             isCached = false;  // maybe cache if mtype == mtype.basicType()
         }
         boolean isLinker, isGeneric;
-        String debugName;
+        Kind kind;
         switch (which) {
-        case MethodTypeForm.LF_EX_LINKER:   isLinker = true;  isGeneric = false; debugName = "invokeExact_MT"; break;
-        case MethodTypeForm.LF_EX_INVOKER:  isLinker = false; isGeneric = false; debugName = "exactInvoker"; break;
-        case MethodTypeForm.LF_GEN_LINKER:  isLinker = true;  isGeneric = true;  debugName = "invoke_MT"; break;
-        case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true;  debugName = "invoker"; break;
+        case MethodTypeForm.LF_EX_LINKER:   isLinker = true;  isGeneric = false; kind = EXACT_LINKER; break;
+        case MethodTypeForm.LF_EX_INVOKER:  isLinker = false; isGeneric = false; kind = EXACT_INVOKER; break;
+        case MethodTypeForm.LF_GEN_LINKER:  isLinker = true;  isGeneric = true;  kind = GENERIC_LINKER; break;
+        case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true;  kind = GENERIC_INVOKER; break;
         default: throw new InternalError();
         }
         LambdaForm lform;
@@ -323,7 +324,11 @@
             names[CHECK_CUSTOM] = new Name(NF_checkCustomized, outArgs[0]);
         }
         names[LINKER_CALL] = new Name(outCallType, outArgs);
-        lform = new LambdaForm(debugName, INARG_LIMIT, names);
+        if (customized) {
+            lform = new LambdaForm(kind.defaultLambdaName, INARG_LIMIT, names);
+        } else {
+            lform = new LambdaForm(kind.defaultLambdaName, INARG_LIMIT, names, kind);
+        }
         if (isLinker)
             lform.compileToBytecode();  // JVM needs a real methodOop
         if (isCached)
@@ -614,4 +619,15 @@
             }
         }
     }
+
+    static {
+        // The Holder class will contain pre-generated Invokers resolved
+        // speculatively using MemberName.getFactory().resolveOrNull. However, that
+        // doesn't initialize the class, which subtly breaks inlining etc. By forcing
+        // initialization of the Holder class we avoid these issues.
+        UNSAFE.ensureClassInitialized(Holder.class);
+    }
+
+    /* Placeholder class for Invokers generated ahead of time */
+    final class Holder {}
 }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Wed Aug 24 16:11:21 2016 +0200
@@ -275,6 +275,10 @@
         BOUND_REINVOKER("BMH.reinvoke"),
         REINVOKER("MH.reinvoke"),
         DELEGATE("MH.delegate"),
+        EXACT_LINKER("MH.invokeExact_MT"),
+        EXACT_INVOKER("MH.exactInvoker"),
+        GENERIC_LINKER("MH.invoke_MT"),
+        GENERIC_INVOKER("MH.invoker"),
         DIRECT_INVOKE_VIRTUAL("DMH.invokeVirtual"),
         DIRECT_INVOKE_SPECIAL("DMH.invokeSpecial"),
         DIRECT_INVOKE_STATIC("DMH.invokeStatic"),
@@ -366,6 +370,10 @@
         this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true, /*customized=*/null, Kind.GENERIC);
     }
     LambdaForm(String debugName,
+               int arity, Name[] names, Kind kind) {
+        this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true, /*customized=*/null, kind);
+    }
+    LambdaForm(String debugName,
                int arity, Name[] names, boolean forceInline) {
         this(debugName, arity, names, LAST_RESULT, forceInline, /*customized=*/null, Kind.GENERIC);
     }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Aug 24 16:11:21 2016 +0200
@@ -1745,6 +1745,13 @@
                 return GenerateJLIClassesHelper
                         .generateBasicFormsClassBytes(className);
             }
+
+            @Override
+            public byte[] generateInvokersHolderClassBytes(final String className,
+                    MethodType[] methodTypes) {
+                return GenerateJLIClassesHelper
+                        .generateInvokersHolderClassBytes(className, methodTypes);
+            }
         });
     }
 
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java	Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java	Wed Aug 24 16:11:21 2016 +0200
@@ -46,7 +46,7 @@
     boolean isNative(Object mname);
 
     /**
-     * Returns a {@code byte[]} containing the bytecode for a class implementing
+     * Returns a {@code byte[]} representation of a class implementing
      * DirectMethodHandle of each pairwise combination of {@code MethodType} and
      * an {@code int} representing method type.  Used by
      * GenerateJLIClassesPlugin to generate such a class during the jlink phase.
@@ -55,7 +55,7 @@
             MethodType[] methodTypes, int[] types);
 
     /**
-     * Returns a {@code byte[]} containing the bytecode for a class implementing
+     * Returns a {@code byte[]} representation of a class implementing
      * DelegatingMethodHandles of each {@code MethodType} kind in the
      * {@code methodTypes} argument.  Used by GenerateJLIClassesPlugin to
      * generate such a class during the jlink phase.
@@ -64,7 +64,7 @@
             MethodType[] methodTypes);
 
     /**
-     * Returns a {@code byte[]} containing the bytecode for a BoundMethodHandle
+     * 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
      * the jlink phase. Should do some added validation since this string may be
@@ -74,8 +74,15 @@
             final String types);
 
     /**
-     * Returns a {@code byte[]} containing the bytecode for a class implementing
+     * Returns a {@code byte[]} representation of a class implementing
      * the zero and identity forms of all {@code LambdaForm.BasicType}s.
      */
     byte[] generateBasicFormsClassBytes(final String className);
+
+    /**
+     * Returns a {@code byte[]} representation of a class implementing
+     * the invoker forms for the set of supplied {@code methodTypes}.
+     */
+    byte[] generateInvokersHolderClassBytes(String className,
+            MethodType[] methodTypes);
 }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Wed Aug 24 16:11:21 2016 +0200
@@ -48,14 +48,12 @@
     private static final String NAME = "generate-jli-classes";
 
     private static final String BMH_PARAM = "bmh";
-
     private static final String BMH_SPECIES_PARAM = "bmh-species";
 
-    private static final String DMH_PARAM = "dmh";
-
     private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
 
-    private static final String DIRECT_METHOD_HANDLE = "java/lang/invoke/DirectMethodHandle$Holder";
+    private static final String DMH_PARAM = "dmh";
+    private static final String DIRECT_HOLDER = "java/lang/invoke/DirectMethodHandle$Holder";
     private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual";
     private static final String DMH_INVOKE_STATIC = "invokeStatic";
     private static final String DMH_INVOKE_SPECIAL = "invokeSpecial";
@@ -63,17 +61,22 @@
     private static final String DMH_INVOKE_INTERFACE = "invokeInterface";
     private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit";
 
-    private static final String DELEGATING_METHOD_HANDLE = "java/lang/invoke/DelegatingMethodHandle$Holder";
+    private static final String INVOKERS_PARAM = "invokers";
 
-    private static final String BASIC_FORMS_HANDLE = "java/lang/invoke/LambdaForm$Holder";
+    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 JavaLangInvokeAccess JLIA
             = SharedSecrets.getJavaLangInvokeAccess();
 
     List<String> speciesTypes;
 
+    List<String> invokerTypes;
+
     Map<String, List<String>> dmhMethods;
 
+
     public GenerateJLIClassesPlugin() {
     }
 
@@ -117,6 +120,13 @@
     }
 
     /**
+     * @return the default invoker forms to generate.
+     */
+    public static List<String> defaultInvokers() {
+        return List.of("_L", "_I", "I_I", "LI_I", "ILL_I", "LIL_I", "L_L", "LL_V", "LLLL_L");
+    }
+
+    /**
      * @return the list of default DirectMethodHandle methods to generate.
      */
     public static Map<String, List<String>> defaultDMHMethods() {
@@ -153,6 +163,7 @@
         // Enable by default
         boolean bmhEnabled = true;
         boolean dmhEnabled = true;
+        boolean invokersEnabled = true;
         if (mainArgument != null) {
             List<String> args = Arrays.asList(mainArgument.split(","));
             if (!args.contains(BMH_PARAM)) {
@@ -161,6 +172,9 @@
             if (!args.contains(DMH_PARAM)) {
                 dmhEnabled = false;
             }
+            if (!args.contains(INVOKERS_PARAM)) {
+                dmhEnabled = false;
+            }
         }
 
         if (!bmhEnabled) {
@@ -183,6 +197,21 @@
                     .collect(Collectors.toList());
         }
 
+        if (!invokersEnabled) {
+            invokerTypes = List.of();
+        } else {
+            String args = config.get(INVOKERS_PARAM);
+            if (args != null && !args.isEmpty()) {
+                invokerTypes = Arrays.stream(args.split(","))
+                            .map(String::trim)
+                            .filter(s -> !s.isEmpty())
+                            .collect(Collectors.toList());
+                validateMethodTypes(invokerTypes);
+            } else {
+                invokerTypes = defaultInvokers();
+            }
+
+        }
         // DirectMethodHandles
         if (!dmhEnabled) {
             dmhMethods = Map.of();
@@ -196,18 +225,7 @@
                             .filter(s -> !s.isEmpty())
                             .collect(Collectors.toList());
                     dmhMethods.put(dmhParam, dmhMethodTypes);
-                    // Validation check
-                    for (String type : dmhMethodTypes) {
-                        String[] typeParts = type.split("_");
-                        // check return type (second part)
-                        if (typeParts.length != 2 || typeParts[1].length() != 1
-                                || "LJIFDV".indexOf(typeParts[1].charAt(0)) == -1) {
-                            throw new PluginException(
-                                    "Method type signature must be of form [LJIFD]*_[LJIFDV]");
-                        }
-                        // expand and check arguments (first part)
-                        expandSignature(typeParts[0]);
-                    }
+                    validateMethodTypes(dmhMethodTypes);
                 }
             }
             if (dmhMethods.isEmpty()) {
@@ -216,6 +234,20 @@
         }
     }
 
+    void validateMethodTypes(List<String> dmhMethodTypes) {
+        for (String type : dmhMethodTypes) {
+            String[] typeParts = type.split("_");
+            // check return type (second part)
+            if (typeParts.length != 2 || typeParts[1].length() != 1
+                    || "LJIFDV".indexOf(typeParts[1].charAt(0)) == -1) {
+                throw new PluginException(
+                        "Method type signature must be of form [LJIFD]*_[LJIFDV]");
+            }
+            // expand and check arguments (first part)
+            expandSignature(typeParts[0]);
+        }
+    }
+
     private static void requireBasicType(char c) {
         if ("LIJFD".indexOf(c) < 0) {
             throw new PluginException(
@@ -228,9 +260,10 @@
         // Copy all but DMH_ENTRY to out
         in.transformAndCopy(entry -> {
                 // filter out placeholder entries
-                if (entry.path().equals(DIRECT_METHOD_HANDLE_ENTRY) ||
-                    entry.path().equals(DELEGATING_METHOD_HANDLE_ENTRY) ||
-                    entry.path().equals(BASIC_FORMS_HANDLE_ENTRY)) {
+                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)) {
                     return null;
                 } else {
                     return entry;
@@ -265,42 +298,53 @@
         for (List<String> entry : dmhMethods.values()) {
             count += entry.size();
         }
-        MethodType[] methodTypes = new MethodType[count];
+        MethodType[] directMethodTypes = new MethodType[count];
         int[] dmhTypes = new int[count];
         int index = 0;
         for (Map.Entry<String, List<String>> entry : dmhMethods.entrySet()) {
             String dmhType = entry.getKey();
             for (String type : entry.getValue()) {
-                methodTypes[index] = asMethodType(type);
+                directMethodTypes[index] = asMethodType(type);
                 dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
                 index++;
             }
         }
+        MethodType[] invokerMethodTypes = new MethodType[this.invokerTypes.size()];
+        for (int i = 0; i < invokerTypes.size(); i++) {
+            invokerMethodTypes[i] = asMethodType(invokerTypes.get(i));
+        }
         try {
             byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(
-                    DIRECT_METHOD_HANDLE, methodTypes, dmhTypes);
+                    DIRECT_HOLDER, directMethodTypes, dmhTypes);
             ResourcePoolEntry ndata = ResourcePoolEntry
-                    .create(DIRECT_METHOD_HANDLE_ENTRY, bytes);
+                    .create(DIRECT_METHOD_HOLDER_ENTRY, bytes);
             out.add(ndata);
 
             bytes = JLIA.generateDelegatingMethodHandleHolderClassBytes(
-                    DELEGATING_METHOD_HANDLE, methodTypes);
-            ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HANDLE_ENTRY, bytes);
+                    DELEGATING_HOLDER, directMethodTypes);
+            ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HOLDER_ENTRY, bytes);
             out.add(ndata);
 
-            bytes = JLIA.generateBasicFormsClassBytes(BASIC_FORMS_HANDLE);
-            ndata = ResourcePoolEntry.create(BASIC_FORMS_HANDLE_ENTRY, bytes);
+            bytes = JLIA.generateInvokersHolderClassBytes(INVOKERS_HOLDER,
+                    invokerMethodTypes);
+            ndata = ResourcePoolEntry.create(INVOKERS_HOLDER_ENTRY, bytes);
+            out.add(ndata);
+
+            bytes = JLIA.generateBasicFormsClassBytes(BASIC_FORMS_HOLDER);
+            ndata = ResourcePoolEntry.create(BASIC_FORMS_HOLDER_ENTRY, bytes);
             out.add(ndata);
         } catch (Exception ex) {
             throw new PluginException(ex);
         }
     }
-    private static final String DIRECT_METHOD_HANDLE_ENTRY =
-            "/java.base/" + DIRECT_METHOD_HANDLE + ".class";
-    private static final String DELEGATING_METHOD_HANDLE_ENTRY =
-            "/java.base/" + DELEGATING_METHOD_HANDLE + ".class";
-    private static final String BASIC_FORMS_HANDLE_ENTRY =
-            "/java.base/" + BASIC_FORMS_HANDLE + ".class";
+    private static final String DIRECT_METHOD_HOLDER_ENTRY =
+            "/java.base/" + DIRECT_HOLDER + ".class";
+    private static final String DELEGATING_METHOD_HOLDER_ENTRY =
+            "/java.base/" + DELEGATING_HOLDER + ".class";
+    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";
 
     // Convert LL -> LL, L3 -> LLL
     private static String expandSignature(String signature) {
--- a/jdk/test/java/lang/StackWalker/VerifyStackTrace.java	Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/test/java/lang/StackWalker/VerifyStackTrace.java	Wed Aug 24 16:11:21 2016 +0200
@@ -205,12 +205,13 @@
                     .replaceAll("java.base@(\\d+\\.){0,3}(\\d+)/", "java.base/")
                     .replaceAll("/[0-9]+\\.run", "/xxxxxxxx.run")
                     .replaceAll("/[0-9]+\\.invoke", "/xxxxxxxx.invoke")
-                    // DMHs may or may not be pre-generated, making frames differ
+                    // LFs may or may not be pre-generated, making frames differ
                     .replaceAll("DirectMethodHandle\\$Holder", "LambdaForm\\$DMH")
-                    .replaceAll("DMH\\.invoke", "DMH/xxxxxxxx.invoke")
+                    .replaceAll("Invokers\\$Holder", "LambdaForm\\$MH")
+                    .replaceAll("MH\\.invoke", "MH/xxxxxxxx.invoke")
                     // invoke frames may or may not have basic method type
                     // information encoded for diagnostic purposes
-                    .replaceAll("xx\\.invoke([A-Za-z]*)_[A-Z]+_[A-Z]", "xx.invoke$1")
+                    .replaceAll("xx\\.invoke([A-Za-z]*)_[A-Z_]+", "xx.invoke$1")
                     .replaceAll("\\$[0-9]+", "\\$??");
         } else {
             return produced;