8214230: Classes generated by SystemModulesPlugin.java are not reproducable

Reviewed-by: alanb, redestad, mchung
This commit is contained in:
Erik Helin 2018-12-04 09:30:10 +01:00
parent 3e2804d0d8
commit 45628a358a
2 changed files with 143 additions and 13 deletions

View File

@ -52,6 +52,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
@ -925,7 +926,7 @@ public final class SystemModulesPlugin implements Plugin {
mv.visitTypeInsn(ANEWARRAY, "java/util/Map$Entry");
int index = 0;
for (Map.Entry<String, Set<String>> e : map.entrySet()) {
for (var e : new TreeMap<>(map).entrySet()) {
String name = e.getKey();
Set<String> s = e.getValue();
@ -971,7 +972,7 @@ public final class SystemModulesPlugin implements Plugin {
pushInt(mv, size);
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
int i = 0;
for (String element : set) {
for (String element : sorted(set)) {
mv.visitInsn(DUP);
pushInt(mv, i);
mv.visitLdcInsn(element);
@ -985,7 +986,7 @@ public final class SystemModulesPlugin implements Plugin {
true);
} else {
StringBuilder sb = new StringBuilder("(");
for (String element : set) {
for (String element : sorted(set)) {
mv.visitLdcInsn(element);
sb.append("Ljava/lang/Object;");
}
@ -1146,7 +1147,7 @@ public final class SystemModulesPlugin implements Plugin {
pushInt(mv, requires.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Requires");
int arrayIndex = 0;
for (Requires require : requires) {
for (Requires require : sorted(requires)) {
String compiledVersion = null;
if (require.compiledVersion().isPresent()) {
compiledVersion = require.compiledVersion().get().toString();
@ -1192,7 +1193,7 @@ public final class SystemModulesPlugin implements Plugin {
pushInt(mv, exports.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Exports");
int arrayIndex = 0;
for (Exports export : exports) {
for (Exports export : sorted(exports)) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex++);
newExports(export.modifiers(), export.source(), export.targets());
@ -1245,7 +1246,7 @@ public final class SystemModulesPlugin implements Plugin {
pushInt(mv, opens.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Opens");
int arrayIndex = 0;
for (Opens open : opens) {
for (Opens open : sorted(opens)) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex++);
newOpens(open.modifiers(), open.source(), open.targets());
@ -1310,7 +1311,7 @@ public final class SystemModulesPlugin implements Plugin {
pushInt(mv, provides.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Provides");
int arrayIndex = 0;
for (Provides provide : provides) {
for (Provides provide : sorted(provides)) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex++);
newProvides(provide.service(), provide.providers());
@ -1420,6 +1421,8 @@ public final class SystemModulesPlugin implements Plugin {
// Invoke ModuleHashes.Builder::hashForModule
recordedHashes
.names()
.stream()
.sorted()
.forEach(mn -> hashForModule(mn, recordedHashes.hashFor(mn)));
// Put ModuleHashes into the hashes array
@ -1600,7 +1603,7 @@ public final class SystemModulesPlugin implements Plugin {
* it will reuse defaultVarIndex. For a Set with multiple references,
* it will use a new local variable retrieved from the nextLocalVar
*/
class SetBuilder<T> {
class SetBuilder<T extends Comparable<T>> {
private final Set<T> elements;
private final int defaultVarIndex;
private final IntSupplier nextLocalVar;
@ -1660,7 +1663,7 @@ public final class SystemModulesPlugin implements Plugin {
if (elements.size() <= 10) {
// call Set.of(e1, e2, ...)
StringBuilder sb = new StringBuilder("(");
for (T t : elements) {
for (T t : sorted(elements)) {
sb.append("Ljava/lang/Object;");
visitElement(t, mv);
}
@ -1672,7 +1675,7 @@ public final class SystemModulesPlugin implements Plugin {
pushInt(mv, elements.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
int arrayIndex = 0;
for (T t : elements) {
for (T t : sorted(elements)) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, arrayIndex);
visitElement(t, mv); // value
@ -1690,7 +1693,7 @@ public final class SystemModulesPlugin implements Plugin {
* Generates bytecode to create one single instance of EnumSet
* for a given set of modifiers and assign to a local variable slot.
*/
class EnumSetBuilder<T> extends SetBuilder<T> {
class EnumSetBuilder<T extends Comparable<T>> extends SetBuilder<T> {
private final String className;
@ -1788,7 +1791,7 @@ public final class SystemModulesPlugin implements Plugin {
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
int index = 0;
for (String moduleName : map.keySet()) {
for (String moduleName : sorted(map.keySet())) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, index);
mv.visitLdcInsn(moduleName);
@ -1811,7 +1814,7 @@ public final class SystemModulesPlugin implements Plugin {
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
index = 0;
for (String className : map.values()) {
for (String className : sorted(map.values())) {
mv.visitInsn(DUP); // arrayref
pushInt(mv, index);
mv.visitLdcInsn(className.replace('/', '.'));
@ -1831,6 +1834,19 @@ public final class SystemModulesPlugin implements Plugin {
return rn;
}
/**
* Returns a sorted copy of a collection.
*
* This is useful to ensure a deterministic iteration order.
*
* @return a sorted copy of the given collection.
*/
private static <T extends Comparable<T>> List<T> sorted(Collection<T> c) {
var l = new ArrayList<T>(c);
Collections.sort(l);
return l;
}
/**
* Pushes an int constant
*/

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2018, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.nio.file.*;
import java.util.*;
import static jdk.test.lib.Asserts.*;
import jdk.test.lib.JDKToolFinder;
import jdk.test.lib.process.ProcessTools;
/*
* @test
* @bug 8214230
* @summary Test that jlinks generates reproducible modules files
* @library /test/lib
* @run driver JLinkReproducibleTest
*/
public class JLinkReproducibleTest {
private static void run(List<String> cmd) throws Exception {
var pb = new ProcessBuilder(cmd.toArray(new String[0]));
var res = ProcessTools.executeProcess(pb);
res.shouldHaveExitValue(0);
}
private static void jlink(Path image) throws Exception {
var cmd = new ArrayList<String>();
cmd.add(JDKToolFinder.getJDKTool("jlink"));
cmd.addAll(List.of(
"--module-path", JMODS_DIR.toString() + File.pathSeparator + CLASS_DIR.toString(),
"--add-modules", "main",
"--compress=2",
"--output", image.toString()
));
run(cmd);
}
private static void javac(String... args) throws Exception {
var cmd = new ArrayList<String>();
cmd.add(JDKToolFinder.getJDKTool("javac"));
cmd.addAll(Arrays.asList(args));
run(cmd);
}
private static final List<String> MODULE_INFO = List.of(
"module main {",
" exports org.test.main;",
"}"
);
private static final List<String> MAIN_CLASS = List.of(
"package org.test.main;",
"public class Main {",
" public static void main(String[] args) {",
" System.out.println(\"Hello, world\");",
" }",
"}"
);
private static final Path CLASS_DIR = Path.of("classes");
private static final Path JMODS_DIR = Path.of(System.getProperty("java.home"), "jmods");
public static void main(String[] args) throws Exception {
// Write the source code
var srcDir = Path.of("main", "org", "test", "main");
Files.createDirectories(srcDir.toAbsolutePath());
var srcFile = srcDir.resolve("Main.java");
Files.write(srcFile, MAIN_CLASS);
var moduleFile = Path.of("main").resolve("module-info.java");
Files.write(moduleFile, MODULE_INFO);
// Compile the source code to class files
javac("--module-source-path", ".",
"--module", "main",
"-d", CLASS_DIR.toString());
// Link the first image
var firstImage = Path.of("image-first");
jlink(firstImage);
var firstModulesFile = firstImage.resolve("lib")
.resolve("modules");
// Link the second image
var secondImage = Path.of("image-second");
jlink(secondImage);
var secondModulesFile = secondImage.resolve("lib")
.resolve("modules");
// Ensure module files are identical
assertEquals(-1L, Files.mismatch(firstModulesFile, secondModulesFile));
}
}