8232864: Classes generated at link time by GenerateJLIClassesPlugin are not reproducible
authorjiefu
Tue, 29 Oct 2019 10:13:27 -0700
changeset 58843 63994dedec49
parent 58842 6c255334120d
child 58844 5a0e0d0b3a27
8232864: Classes generated at link time by GenerateJLIClassesPlugin are not reproducible Reviewed-by: redestad, mchung
src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
test/jdk/tools/jlink/JLinkReproducibleTest.java
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Tue Oct 29 08:26:55 2019 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Tue Oct 29 10:13:27 2019 -0700
@@ -36,7 +36,6 @@
 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.access.SharedSecrets;
 import jdk.internal.access.JavaLangInvokeAccess;
@@ -75,13 +74,13 @@
     private static final JavaLangInvokeAccess JLIA
             = SharedSecrets.getJavaLangInvokeAccess();
 
-    Set<String> speciesTypes = Set.of();
+    private final TreeSet<String> speciesTypes = new TreeSet<>();
 
-    Set<String> invokerTypes = Set.of();
+    private final TreeSet<String> invokerTypes = new TreeSet<>();
 
-    Set<String> callSiteTypes = Set.of();
+    private final TreeSet<String> callSiteTypes = new TreeSet<>();
 
-    Map<String, Set<String>> dmhMethods = Map.of();
+    private final Map<String, Set<String>> dmhMethods = new TreeMap<>();
 
     String mainArgument;
 
@@ -187,21 +186,31 @@
         mainArgument = config.get(NAME);
     }
 
+    private void addSpeciesType(String type) {
+        speciesTypes.add(expandSignature(type));
+    }
+
+    private void addInvokerType(String methodType) {
+        validateMethodType(methodType);
+        invokerTypes.add(methodType);
+    }
+
+    private void addCallSiteType(String csType) {
+        validateMethodType(csType);
+        callSiteTypes.add(csType);
+    }
+
     public void initialize(ResourcePool in) {
         // Start with the default configuration
-        speciesTypes = defaultSpecies().stream()
-                .map(type -> expandSignature(type))
-                .collect(Collectors.toSet());
+        defaultSpecies().stream().forEach(this::addSpeciesType);
 
-        invokerTypes = defaultInvokers();
-        validateMethodTypes(invokerTypes);
+        defaultInvokers().stream().forEach(this::validateMethodType);
 
-        callSiteTypes = defaultCallSiteTypes();
+        defaultCallSiteTypes().stream().forEach(this::addCallSiteType);
 
-        dmhMethods = defaultDMHMethods();
-        for (Set<String> dmhMethodTypes : dmhMethods.values()) {
-            validateMethodTypes(dmhMethodTypes);
-        }
+        defaultDMHMethods().entrySet().stream().forEach(e -> {
+            e.getValue().stream().forEach(type -> addDMHMethodType(e.getKey(), type));
+        });
 
         // Extend the default configuration with the contents in the supplied
         // input file - if none was supplied we look for the default file
@@ -225,18 +234,6 @@
     }
 
     private void readTraceConfig(Stream<String> lines) {
-        // 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<>(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()));
-        }
-        dmhMethods = newDMHMethods;
         lines.map(line -> line.split(" "))
              .forEach(parts -> {
                 switch (parts[0]) {
@@ -245,19 +242,18 @@
                         if (parts.length == 3 && parts[1].startsWith("java.lang.invoke.BoundMethodHandle$Species_")) {
                             String species = parts[1].substring("java.lang.invoke.BoundMethodHandle$Species_".length());
                             if (!"L".equals(species)) {
-                                speciesTypes.add(expandSignature(species));
+                                addSpeciesType(species);
                             }
                         }
                         break;
                     case "[LF_RESOLVE]":
                         String methodType = parts[3];
-                        validateMethodType(methodType);
                         if (parts[1].equals(INVOKERS_HOLDER_NAME)) {
                             if ("linkToTargetMethod".equals(parts[2]) ||
                                     "linkToCallSite".equals(parts[2])) {
-                                callSiteTypes.add(methodType);
+                                addCallSiteType(methodType);
                             } else {
-                                invokerTypes.add(methodType);
+                                addInvokerType(methodType);
                             }
                         } else if (parts[1].contains("DirectMethodHandle")) {
                             String dmh = parts[2];
@@ -291,12 +287,6 @@
         }
     }
 
-    private void validateMethodTypes(Set<String> dmhMethodTypes) {
-        for (String type : dmhMethodTypes) {
-            validateMethodType(type);
-        }
-    }
-
     private void validateMethodType(String type) {
         String[] typeParts = type.split("_");
         // check return type (second part)
@@ -340,9 +330,10 @@
         generateHolderClasses(out);
 
         // Let it go
-        speciesTypes = null;
-        invokerTypes = null;
-        dmhMethods = null;
+        speciesTypes.clear();
+        invokerTypes.clear();
+        callSiteTypes.clear();
+        dmhMethods.clear();
 
         return out.build();
     }
--- a/test/jdk/tools/jlink/JLinkReproducibleTest.java	Tue Oct 29 08:26:55 2019 -0700
+++ b/test/jdk/tools/jlink/JLinkReproducibleTest.java	Tue Oct 29 10:13:27 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -43,7 +43,7 @@
         res.shouldHaveExitValue(0);
     }
 
-    private static void jlink(Path image) throws Exception {
+    private static void jlink(Path image, boolean with_default_trace_file) throws Exception {
         var cmd = new ArrayList<String>();
         cmd.add(JDKToolFinder.getJDKTool("jlink"));
         cmd.addAll(List.of(
@@ -52,6 +52,9 @@
             "--compress=2",
             "--output", image.toString()
         ));
+        if (!with_default_trace_file) {
+            cmd.add("--generate-jli-classes=@file-not-exists");
+        }
         run(cmd);
     }
 
@@ -98,17 +101,31 @@
 
         // Link the first image
         var firstImage = Path.of("image-first");
-        jlink(firstImage);
+        jlink(firstImage, true);
         var firstModulesFile = firstImage.resolve("lib")
                                          .resolve("modules");
 
         // Link the second image
         var secondImage = Path.of("image-second");
-        jlink(secondImage);
+        jlink(secondImage, true);
         var secondModulesFile = secondImage.resolve("lib")
                                            .resolve("modules");
 
         // Ensure module files are identical
         assertEquals(-1L, Files.mismatch(firstModulesFile, secondModulesFile));
+
+        // Link the third image
+        var thirdImage = Path.of("image-third");
+        jlink(thirdImage, false);
+        var thirdModulesFile = thirdImage.resolve("lib")
+                                         .resolve("modules");
+        // Link the fourth image
+        var fourthImage = Path.of("image-fourth");
+        jlink(fourthImage, false);
+        var fourthModulesFile = fourthImage.resolve("lib")
+                                           .resolve("modules");
+
+        // Ensure module files are identical
+        assertEquals(-1L, Files.mismatch(thirdModulesFile, fourthModulesFile));
     }
 }