8164858: Enable build-time use of java.lang.invoke resolve tracing
authorredestad
Wed, 31 Aug 2016 14:20:02 +0200
changeset 40681 87d3f0e6a51f
parent 40680 01c426b7cb59
child 40682 b08455408de7
8164858: Enable build-time use of java.lang.invoke resolve tracing Reviewed-by: erikj, vlivanov
jdk/make/GenerateClasslist.gmk
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
jdk/test/tools/jlink/plugins/GenerateJLIClassesPluginTest.java
--- a/jdk/make/GenerateClasslist.gmk	Tue Aug 30 17:48:07 2016 -0700
+++ b/jdk/make/GenerateClasslist.gmk	Wed Aug 31 14:20:02 2016 +0200
@@ -50,6 +50,8 @@
 
 CLASSLIST_FILE := $(SUPPORT_OUTPUTDIR)/classlist/classlist
 
+JLI_TRACE_FILE := $(SUPPORT_OUTPUTDIR)/classlist/jli_trace.out
+
 # If an external buildjdk has been supplied, we don't build a separate interim
 # image, so just use the external build jdk instead.
 ifeq ($(EXTERNAL_BUILDJDK), true)
@@ -59,13 +61,11 @@
 $(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXE_SUFFIX) $(CLASSLIST_JAR)
 	$(call MakeDir, $(@D))
 	$(call LogInfo, Generating lib/classlist)
-	$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@.tmp \
+	$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@ \
+	    -Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true \
 	    -cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
-	    build.tools.classlist.HelloClasslist $(LOG_DEBUG) 2>&1
-        # Filter out generated classes, remove after JDK-8149977
-	$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@ \
-	    -Xshare:dump -XX:SharedClassListFile=$@.tmp $(LOG_DEBUG) 2>&1
-	$(RM) $@.tmp
+	    build.tools.classlist.HelloClasslist \
+	    $(LOG_DEBUG) 2>&1 > $(JLI_TRACE_FILE)
 
 TARGETS += $(CLASSLIST_FILE)
 
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Tue Aug 30 17:48:07 2016 -0700
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Wed Aug 31 14:20:02 2016 +0200
@@ -29,12 +29,12 @@
 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;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import jdk.internal.misc.SharedSecrets;
@@ -69,11 +69,11 @@
     private static final JavaLangInvokeAccess JLIA
             = SharedSecrets.getJavaLangInvokeAccess();
 
-    List<String> speciesTypes;
+    Set<String> speciesTypes;
 
-    List<String> invokerTypes;
+    Set<String> invokerTypes;
 
-    Map<String, List<String>> dmhMethods;
+    Map<String, Set<String>> dmhMethods;
 
     public GenerateJLIClassesPlugin() {
     }
@@ -110,8 +110,8 @@
      * 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() {
-        return List.of("LL", "L3", "L4", "L5", "L6", "L7", "L7I",
+    public static Set<String> defaultSpecies() {
+        return Set.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",
                 "LILL", "I", "LLILL");
@@ -120,23 +120,23 @@
     /**
      * @return the default invoker forms to generate.
      */
-    private static List<String> defaultInvokers() {
-        return List.of("LL_L", "LL_I", "LILL_I", "L6_L");
+    private static Set<String> defaultInvokers() {
+        return Set.of("LL_L", "LL_I", "LILL_I", "L6_L");
     }
 
     /**
      * @return the list of default DirectMethodHandle methods to generate.
      */
-    private static Map<String, List<String>> defaultDMHMethods() {
+    private static Map<String, Set<String>> defaultDMHMethods() {
         return Map.of(
-            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",
+            DMH_INVOKE_VIRTUAL, Set.of("L_L", "LL_L", "LLI_I", "L3_V"),
+            DMH_INVOKE_SPECIAL, Set.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",
+            DMH_INVOKE_STATIC, Set.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",
@@ -159,15 +159,48 @@
     public void configure(Map<String, String> config) {
         String mainArgument = config.get(NAME);
 
-        if (mainArgument != null && mainArgument.startsWith("@")) {
+        if ("none".equals(mainArgument)) {
+            speciesTypes = Set.of();
+            invokerTypes = Set.of();
+            dmhMethods = Map.of();
+            return;
+        }
+
+        // Start with the default configuration
+        Set<String> defaultBMHSpecies = defaultSpecies();
+        // Expand BMH species signatures
+        defaultBMHSpecies = defaultBMHSpecies.stream()
+                .map(type -> expandSignature(type))
+                .collect(Collectors.toSet());
+
+        Set<String> defaultInvokerTypes = defaultInvokers();
+        validateMethodTypes(defaultInvokerTypes);
+
+        Map<String, Set<String>> defaultDmhMethods = defaultDMHMethods();
+        for (Set<String> dmhMethodTypes : defaultDmhMethods.values()) {
+            validateMethodTypes(dmhMethodTypes);
+        }
+
+        // Extend the default configuration with the contents in the supplied
+        // input file
+        if (mainArgument == null || !mainArgument.startsWith("@")) {
+            speciesTypes = defaultBMHSpecies;
+            invokerTypes = defaultInvokerTypes;
+            dmhMethods = defaultDmhMethods;
+        } else {
             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(" "))
+                // Use TreeSet/TreeMap to keep things sorted in a deterministic
+                // order to avoid scrambling the layout on small changes and to
+                // ease finding methods in the generated code
+                speciesTypes = new TreeSet<>(defaultBMHSpecies);
+                invokerTypes = new TreeSet<>(defaultInvokerTypes);
+                dmhMethods = new TreeMap<>();
+                for (Map.Entry<String, Set<String>> entry : defaultDmhMethods.entrySet()) {
+                    dmhMethods.put(entry.getKey(), new TreeSet<>(entry.getValue()));
+                }
+                fileLines(file)
+                    .map(line -> line.split(" "))
                     .forEach(parts -> {
                         switch (parts[0]) {
                             case "[BMH_RESOLVE]":
@@ -191,28 +224,14 @@
                         }
                 });
             }
-        } else {
-            List<String> bmhSpecies = defaultSpecies();
-            // Expand BMH species signatures
-            speciesTypes = bmhSpecies.stream()
-                    .map(type -> expandSignature(type))
-                    .collect(Collectors.toList());
-
-            invokerTypes = defaultInvokers();
-            validateMethodTypes(invokerTypes);
-
-            dmhMethods = defaultDMHMethods();
-            for (List<String> dmhMethodTypes : dmhMethods.values()) {
-                validateMethodTypes(dmhMethodTypes);
-            }
         }
     }
 
     private void addDMHMethodType(String dmh, String methodType) {
         validateMethodType(methodType);
-        List<String> methodTypes = dmhMethods.get(dmh);
+        Set<String> methodTypes = dmhMethods.get(dmh);
         if (methodTypes == null) {
-            methodTypes = new ArrayList<>();
+            methodTypes = new TreeSet<>();
             dmhMethods.put(dmh, methodTypes);
         }
         methodTypes.add(methodType);
@@ -226,7 +245,7 @@
         }
     }
 
-    private void validateMethodTypes(List<String> dmhMethodTypes) {
+    private void validateMethodTypes(Set<String> dmhMethodTypes) {
         for (String type : dmhMethodTypes) {
             validateMethodType(type);
         }
@@ -291,13 +310,13 @@
 
     private void generateHolderClasses(ResourcePoolBuilder out) {
         int count = 0;
-        for (List<String> entry : dmhMethods.values()) {
+        for (Set<String> entry : dmhMethods.values()) {
             count += entry.size();
         }
         MethodType[] directMethodTypes = new MethodType[count];
         int[] dmhTypes = new int[count];
         int index = 0;
-        for (Map.Entry<String, List<String>> entry : dmhMethods.entrySet()) {
+        for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
             String dmhType = entry.getKey();
             for (String type : entry.getValue()) {
                 // The DMH type to actually ask for is retrieved by removing
@@ -314,10 +333,11 @@
             }
         }
         MethodType[] invokerMethodTypes = new MethodType[this.invokerTypes.size()];
-        for (int i = 0; i < invokerTypes.size(); i++) {
+        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(invokerTypes.get(i));
+            MethodType mt = asMethodType(invokerType);
             final int lastParam = mt.parameterCount() - 1;
             if (mt.parameterCount() < 2 ||
                     mt.parameterType(0) != Object.class ||
@@ -327,6 +347,7 @@
             }
             mt = mt.dropParameterTypes(lastParam, lastParam + 1);
             invokerMethodTypes[i] = mt.dropParameterTypes(0, 1);
+            i++;
         }
         try {
             byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Tue Aug 30 17:48:07 2016 -0700
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Wed Aug 31 14:20:02 2016 +0200
@@ -68,12 +68,12 @@
 exclude-resources.description=\
 Specify resources to exclude. e.g.: **.jcov,glob:**/META-INF/**
 
-generate-jli-classes.argument=<@filename>
+generate-jli-classes.argument=<none|@filename>
 
 generate-jli-classes.description=\
 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
+this flag is not specified a default set of classes will be generated. To \n\
+disable pre-generation specify none as the argument
 
 installed-modules.description=Fast loading of module descriptors (always enabled)
 
--- a/jdk/test/tools/jlink/plugins/GenerateJLIClassesPluginTest.java	Tue Aug 30 17:48:07 2016 -0700
+++ b/jdk/test/tools/jlink/plugins/GenerateJLIClassesPluginTest.java	Wed Aug 31 14:20:02 2016 +0200
@@ -22,6 +22,7 @@
  */
 
 import java.nio.file.Path;
+import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -75,7 +76,7 @@
 
     }
 
-    private static List<String> classFilesForSpecies(List<String> species) {
+    private static List<String> classFilesForSpecies(Collection<String> species) {
         return species.stream()
                 .map(s -> "/java.base/java/lang/invoke/BoundMethodHandle$Species_" + s + ".class")
                 .collect(Collectors.toList());