8232864: Classes generated at link time by GenerateJLIClassesPlugin are not reproducible
Reviewed-by: redestad, mchung
--- 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));
}
}