8214230: Classes generated by SystemModulesPlugin.java are not reproducable
Reviewed-by: alanb, redestad, mchung
This commit is contained in:
parent
3e2804d0d8
commit
45628a358a
@ -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
|
||||
*/
|
||||
|
114
test/jdk/tools/jlink/JLinkReproducibleTest.java
Normal file
114
test/jdk/tools/jlink/JLinkReproducibleTest.java
Normal 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));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user