8163371: Enable tracing which JLI classes can be pre-generated
authorredestad
Fri, 26 Aug 2016 16:16:09 +0200
changeset 40559 e727ba540ee6
parent 40558 ec1dec20d88f
child 40560 5641adb8d196
8163371: Enable tracing which JLI classes can be pre-generated Reviewed-by: vlivanov
jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties
--- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Fri Aug 26 08:16:42 2016 -0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Fri Aug 26 16:16:09 2016 +0200
@@ -497,6 +497,10 @@
                         String shortTypes = LambdaForm.shortenSignature(types);
                         String className = SPECIES_CLASS_PREFIX + shortTypes;
                         Class<?> c = BootLoader.loadClassOrNull(className);
+                        if (TRACE_RESOLVE) {
+                            System.out.println("[BMH_RESOLVE] " + shortTypes +
+                                    (c != null ? " (success)" : " (fail)") );
+                        }
                         if (c != null) {
                             return c.asSubclass(BoundMethodHandle.class);
                         } else {
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Fri Aug 26 08:16:42 2016 -0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Fri Aug 26 16:16:09 2016 +0200
@@ -607,7 +607,10 @@
     private static MemberName resolveFrom(String name, MethodType type, Class<?> holder) {
         MemberName member = new MemberName(holder, name, type, REF_invokeStatic);
         MemberName resolvedMember = MemberName.getFactory().resolveOrNull(REF_invokeStatic, member, holder);
-
+        if (TRACE_RESOLVE) {
+            System.out.println("[LF_RESOLVE] " + holder.getName() + " " + name + " " +
+                    shortenSignature(basicTypeSignature(type)) + (resolvedMember != null ? " (success)" : " (fail)") );
+        }
         return resolvedMember;
     }
 
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java	Fri Aug 26 08:16:42 2016 -0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java	Fri Aug 26 16:16:09 2016 +0200
@@ -46,6 +46,7 @@
     static final boolean DUMP_CLASS_FILES;
     static final boolean TRACE_INTERPRETER;
     static final boolean TRACE_METHOD_LINKAGE;
+    static final boolean TRACE_RESOLVE;
     static final int COMPILE_THRESHOLD;
     static final boolean LOG_LF_COMPILATION_FAILURE;
     static final int DONT_INLINE_THRESHOLD;
@@ -65,6 +66,8 @@
                 props.getProperty("java.lang.invoke.MethodHandle.TRACE_INTERPRETER"));
         TRACE_METHOD_LINKAGE = Boolean.parseBoolean(
                 props.getProperty("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE"));
+        TRACE_RESOLVE = Boolean.parseBoolean(
+                props.getProperty("java.lang.invoke.MethodHandle.TRACE_RESOLVE"));
         COMPILE_THRESHOLD = Integer.parseInt(
                 props.getProperty("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", "0"));
         LOG_LF_COMPILATION_FAILURE = Boolean.parseBoolean(
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Fri Aug 26 08:16:42 2016 -0400
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Fri Aug 26 16:16:09 2016 +0200
@@ -24,7 +24,11 @@
  */
 package jdk.tools.jlink.internal.plugins;
 
+import java.io.File;
+import java.io.IOException;
 import java.lang.invoke.MethodType;
+import java.nio.file.Files;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -32,6 +36,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.misc.JavaLangInvokeAccess;
 import jdk.tools.jlink.plugin.ResourcePoolEntry;
@@ -47,12 +52,8 @@
 
     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 DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
 
-    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";
@@ -61,8 +62,6 @@
     private static final String DMH_INVOKE_INTERFACE = "invokeInterface";
     private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit";
 
-    private static final String INVOKERS_PARAM = "invokers";
-
     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";
@@ -76,7 +75,6 @@
 
     Map<String, List<String>> dmhMethods;
 
-
     public GenerateJLIClassesPlugin() {
     }
 
@@ -112,7 +110,7 @@
      * A better long-term solution is to define and run a set of quick
      * generators and extracting this list as a step in the build process.
      */
-    public static List<String> defaultSpecies() {
+    private static List<String> defaultSpecies() {
         return List.of("LL", "L3", "L4", "L5", "L6", "L7", "L7I",
                 "L7II", "L7IIL", "L8", "L9", "L10", "L10I", "L10II", "L10IIL",
                 "L11", "L12", "L13", "LI", "D", "L3I", "LIL", "LLI", "LLIL",
@@ -122,26 +120,27 @@
     /**
      * @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");
+    private static List<String> defaultInvokers() {
+        return List.of("LL_L", "LL_I", "LILL_I", "L6_L");
     }
 
     /**
      * @return the list of default DirectMethodHandle methods to generate.
      */
-    public static Map<String, List<String>> defaultDMHMethods() {
+    private static Map<String, List<String>> defaultDMHMethods() {
         return Map.of(
-            DMH_INVOKE_VIRTUAL, List.of("_L", "L_L", "LI_I", "LL_V"),
-            DMH_INVOKE_SPECIAL, List.of("L_I", "L_L", "LF_L", "LD_L", "LL_L",
-                "L3_L", "L4_L", "L5_L", "L6_L", "L7_L", "LI_I", "LI_L", "LIL_I",
-                "LII_I", "LII_L", "LLI_L", "LLI_I", "LILI_I", "LIIL_L",
-                "LIILL_L", "LIILL_I", "LIIL_I", "LILIL_I", "LILILL_I",
-                "LILII_I", "LI3_I", "LI3L_I", "LI3LL_I", "LI3_L", "LI4_I"),
-            DMH_INVOKE_STATIC, List.of("II_I", "IL_I", "ILIL_I", "ILII_I",
-                "_I", "_L", "_V", "D_L", "F_L", "I_I", "II_L", "LI_L",
-                "L_V", "L_L", "LL_L", "L3_L", "L4_L", "L5_L", "L6_L",
-                "L7_L", "L8_L", "L9_L", "L9I_L", "L9II_L", "L9IIL_L",
-                "L10_L", "L11_L", "L12_L", "L13_L", "L13I_L", "L13II_L")
+            DMH_INVOKE_VIRTUAL, List.of("L_L", "LL_L", "LLI_I", "L3_V"),
+            DMH_INVOKE_SPECIAL, List.of("LL_I", "LL_L", "LLF_L", "LLD_L", "L3_L",
+                "L4_L", "L5_L", "L6_L", "L7_L", "L8_L", "LLI_I", "LLI_L",
+                "LLIL_I", "LLII_I", "LLII_L", "L3I_L", "L3I_I", "LLILI_I",
+                "LLIIL_L", "LLIILL_L", "LLIILL_I", "LLIIL_I", "LLILIL_I",
+                "LLILILL_I", "LLILII_I", "LLI3_I", "LLI3L_I", "LLI3LL_I",
+                "LLI3_L", "LLI4_I"),
+            DMH_INVOKE_STATIC, List.of("LII_I", "LIL_I", "LILIL_I", "LILII_I",
+                "L_I", "L_L", "L_V", "LD_L", "LF_L", "LI_I", "LII_L", "LLI_L",
+                "LL_V", "LL_L", "L3_L", "L4_L", "L5_L", "L6_L", "L7_L",
+                "L8_L", "L9_L", "L10_L", "L10I_L", "L10II_L", "L10IIL_L",
+                "L11_L", "L12_L", "L13_L", "L14_L", "L14I_L", "L14II_L")
         );
     }
 
@@ -160,94 +159,91 @@
     public void configure(Map<String, String> config) {
         String mainArgument = config.get(NAME);
 
-        // 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)) {
-                bmhEnabled = false;
-            }
-            if (!args.contains(DMH_PARAM)) {
-                dmhEnabled = false;
-            }
-            if (!args.contains(INVOKERS_PARAM)) {
-                dmhEnabled = false;
+        if (mainArgument != null && mainArgument.startsWith("@")) {
+            File file = new File(mainArgument.substring(1));
+            if (file.exists()) {
+                speciesTypes = new ArrayList<>();
+                invokerTypes = new ArrayList<>();
+                dmhMethods = new HashMap<>();
+                Stream<String> lines = fileLines(file);
+
+                lines.map(line -> line.split(" "))
+                    .forEach(parts -> {
+                        switch (parts[0]) {
+                            case "[BMH_RESOLVE]":
+                                speciesTypes.add(expandSignature(parts[1]));
+                                break;
+                            case "[LF_RESOLVE]":
+                                String methodType = parts[3];
+                                validateMethodType(methodType);
+                                if (parts[1].contains("Invokers")) {
+                                    invokerTypes.add(methodType);
+                                } else if (parts[1].contains("DirectMethodHandle")) {
+                                    String dmh = parts[2];
+                                    // ignore getObject etc for now (generated
+                                    // by default)
+                                    if (DMH_METHOD_TYPE_MAP.containsKey(dmh)) {
+                                        addDMHMethodType(dmh, methodType);
+                                    }
+                                }
+                                break;
+                            default: break; // ignore
+                        }
+                });
             }
-        }
-
-        if (!bmhEnabled) {
-            speciesTypes = List.of();
         } else {
-            String args = config.get(BMH_SPECIES_PARAM);
-            List<String> bmhSpecies;
-            if (args != null && !args.isEmpty()) {
-                bmhSpecies = Arrays.stream(args.split(","))
-                    .map(String::trim)
-                    .filter(s -> !s.isEmpty())
-                    .collect(Collectors.toList());
-            } else {
-                bmhSpecies = defaultSpecies();
-            }
-
+            List<String> bmhSpecies = defaultSpecies();
             // Expand BMH species signatures
             speciesTypes = bmhSpecies.stream()
                     .map(type -> expandSignature(type))
                     .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();
-            }
+            invokerTypes = defaultInvokers();
+            validateMethodTypes(invokerTypes);
 
-        }
-        // DirectMethodHandles
-        if (!dmhEnabled) {
-            dmhMethods = Map.of();
-        } else {
-            dmhMethods = new HashMap<>();
-            for (String dmhParam : DMH_METHOD_TYPE_MAP.keySet()) {
-                String args = config.get(dmhParam);
-                if (args != null && !args.isEmpty()) {
-                    List<String> dmhMethodTypes = Arrays.stream(args.split(","))
-                            .map(String::trim)
-                            .filter(s -> !s.isEmpty())
-                            .collect(Collectors.toList());
-                    dmhMethods.put(dmhParam, dmhMethodTypes);
-                    validateMethodTypes(dmhMethodTypes);
-                }
-            }
-            if (dmhMethods.isEmpty()) {
-                dmhMethods = defaultDMHMethods();
+            dmhMethods = defaultDMHMethods();
+            for (List<String> dmhMethodTypes : dmhMethods.values()) {
+                validateMethodTypes(dmhMethodTypes);
             }
         }
     }
 
-    void validateMethodTypes(List<String> dmhMethodTypes) {
+    private void addDMHMethodType(String dmh, String methodType) {
+        validateMethodType(methodType);
+        List<String> methodTypes = dmhMethods.get(dmh);
+        if (methodTypes == null) {
+            methodTypes = new ArrayList<>();
+            dmhMethods.put(dmh, methodTypes);
+        }
+        methodTypes.add(methodType);
+    }
+
+    private Stream<String> fileLines(File file) {
+        try {
+            return Files.lines(file.toPath());
+        } catch (IOException io) {
+            throw new PluginException("Couldn't read file");
+        }
+    }
+
+    private 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]);
+            validateMethodType(type);
         }
     }
 
+    private void validateMethodType(String type) {
+        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(
@@ -304,14 +300,33 @@
         for (Map.Entry<String, List<String>> entry : dmhMethods.entrySet()) {
             String dmhType = entry.getKey();
             for (String type : entry.getValue()) {
-                directMethodTypes[index] = asMethodType(type);
+                // The DMH type to actually ask for is retrieved by removing
+                // the first argument, which needs to be of Object.class
+                MethodType mt = asMethodType(type);
+                if (mt.parameterCount() < 1 ||
+                    mt.parameterType(0) != Object.class) {
+                    throw new PluginException(
+                            "DMH type parameter must start with L");
+                }
+                directMethodTypes[index] = mt.dropParameterTypes(0, 1);
                 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));
+            // 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(invokerTypes.get(i));
+            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");
+            }
+            mt = mt.dropParameterTypes(lastParam, lastParam + 1);
+            invokerMethodTypes[i] = mt.dropParameterTypes(0, 1);
         }
         try {
             byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Fri Aug 26 08:16:42 2016 -0400
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Fri Aug 26 16:16:09 2016 +0200
@@ -68,10 +68,12 @@
 exclude-resources.description=\
 Specify resources to exclude. e.g.: **.jcov,glob:**/META-INF/**
 
-generate-jli-classes.argument=<bmh[:bmh-species=LL,L3,...]>
+generate-jli-classes.argument=<@filename>
 
 generate-jli-classes.description=\
-Concrete java.lang.invoke classes to generate
+Takes a file hinting to jlink what java.lang.invoke classes to pre-generate. If\n\
+this flag is not specified a default set of classes will be generated, so to \n\
+disable pre-generation supply the name of an empty or non-existing file
 
 installed-modules.description=Fast loading of module descriptors (always enabled)