8232864: Classes generated at link time by GenerateJLIClassesPlugin are not reproducible

Reviewed-by: redestad, mchung
This commit is contained in:
Jie Fu 2019-10-29 10:13:27 -07:00
parent d83df45396
commit 66a4fd2b79
2 changed files with 52 additions and 44 deletions

View File

@ -36,7 +36,6 @@ 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.access.SharedSecrets;
import jdk.internal.access.JavaLangInvokeAccess;
@ -75,13 +74,13 @@ public final class GenerateJLIClassesPlugin implements Plugin {
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 @@ public final class GenerateJLIClassesPlugin implements Plugin {
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 @@ public final class GenerateJLIClassesPlugin implements Plugin {
}
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 @@ public final class GenerateJLIClassesPlugin implements Plugin {
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 @@ public final class GenerateJLIClassesPlugin implements Plugin {
}
}
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 @@ public final class GenerateJLIClassesPlugin implements Plugin {
generateHolderClasses(out);
// Let it go
speciesTypes = null;
invokerTypes = null;
dmhMethods = null;
speciesTypes.clear();
invokerTypes.clear();
callSiteTypes.clear();
dmhMethods.clear();
return out.build();
}

View File

@ -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 @@ public class JLinkReproducibleTest {
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 @@ public class JLinkReproducibleTest {
"--compress=2",
"--output", image.toString()
));
if (!with_default_trace_file) {
cmd.add("--generate-jli-classes=@file-not-exists");
}
run(cmd);
}
@ -98,17 +101,31 @@ public class JLinkReproducibleTest {
// 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));
}
}