8157068: ExceptionInInitializerError if images build patched to use exploded version of jdk.internal.module.SystemModules

Reviewed-by: alanb
This commit is contained in:
Mandy Chung 2016-05-19 11:17:35 -07:00
parent 2100b2e6c2
commit ab6efe7a7d
22 changed files with 528 additions and 49 deletions

View File

@ -45,6 +45,7 @@ import jdk.internal.jimage.ImageLocation;
import jdk.internal.jimage.ImageReader; import jdk.internal.jimage.ImageReader;
import jdk.internal.jimage.ImageReaderFactory; import jdk.internal.jimage.ImageReaderFactory;
import jdk.internal.module.ModuleHashes; import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleHashes.HashSupplier;
import jdk.internal.module.SystemModules; import jdk.internal.module.SystemModules;
import jdk.internal.module.ModulePatcher; import jdk.internal.module.ModulePatcher;
import jdk.internal.perf.PerfCounter; import jdk.internal.perf.PerfCounter;
@ -84,57 +85,23 @@ class SystemModuleFinder implements ModuleFinder {
long t0 = System.nanoTime(); long t0 = System.nanoTime();
imageReader = ImageReaderFactory.getImageReader(); imageReader = ImageReaderFactory.getImageReader();
String[] moduleNames = SystemModules.MODULE_NAMES; String[] names = moduleNames();
ModuleDescriptor[] descriptors = null; ModuleDescriptor[] descriptors = descriptors(names);
boolean fastLoad = System.getProperty("jdk.installed.modules.disable") == null; int n = names.length;
if (fastLoad) {
// fast loading of ModuleDescriptor of installed modules
descriptors = SystemModules.modules();
}
int n = moduleNames.length;
moduleCount.add(n); moduleCount.add(n);
Set<ModuleReference> mods = new HashSet<>(n); Set<ModuleReference> mods = new HashSet<>(n);
Map<String, ModuleReference> map = new HashMap<>(n); Map<String, ModuleReference> map = new HashMap<>(n);
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
String mn = moduleNames[i]; ModuleDescriptor md = descriptors[i];
ModuleDescriptor md;
String hash;
if (fastLoad) {
md = descriptors[i];
hash = SystemModules.MODULES_TO_HASH[i];
} else {
// fallback to read module-info.class
// if fast loading of ModuleDescriptors is disabled
ImageLocation location = imageReader.findLocation(mn, "module-info.class");
md = ModuleDescriptor.read(imageReader.getResourceBuffer(location));
hash = null;
}
if (!md.name().equals(mn))
throw new InternalError();
// create the ModuleReference // create the ModuleReference
ModuleReference mref = toModuleReference(md, hashSupplier(i, names[i]));
URI uri = URI.create("jrt:/" + mn);
Supplier<ModuleReader> readerSupplier = new Supplier<>() {
@Override
public ModuleReader get() {
return new ImageModuleReader(mn, uri);
}
};
ModuleReference mref =
new ModuleReference(md, uri, readerSupplier, hashSupplier(hash));
// may need a reference to a patched module if -Xpatch specified
mref = ModulePatcher.interposeIfNeeded(mref);
mods.add(mref); mods.add(mref);
map.put(mn, mref); map.put(names[i], mref);
// counters // counters
packageCount.add(md.packages().size()); packageCount.add(md.packages().size());
@ -147,16 +114,114 @@ class SystemModuleFinder implements ModuleFinder {
initTime.addElapsedTimeFrom(t0); initTime.addElapsedTimeFrom(t0);
} }
private static ModuleHashes.HashSupplier hashSupplier(String hash) { /*
if (hash == null) * Returns an array of ModuleDescriptor of the given module names.
return null; *
* This obtains ModuleDescriptors from SystemModules class that is generated
* from the jlink system-modules plugin. ModuleDescriptors have already
* been validated at link time.
*
* If java.base is patched, or fastpath is disabled for troubleshooting
* purpose, it will fall back to find system modules via jrt file system.
*/
private static ModuleDescriptor[] descriptors(String[] names) {
// fastpath is enabled by default.
// It can be disabled for troubleshooting purpose.
boolean disabled =
System.getProperty("jdk.system.module.finder.disabledFastPath") != null;
return new ModuleHashes.HashSupplier() { // fast loading of ModuleDescriptor of system modules
if (isFastPathSupported() && !disabled)
return SystemModules.modules();
// if fast loading of ModuleDescriptors is disabled
// fallback to read module-info.class
ModuleDescriptor[] descriptors = new ModuleDescriptor[names.length];
for (int i = 0; i < names.length; i++) {
String mn = names[i];
ImageLocation loc = imageReader.findLocation(mn, "module-info.class");
descriptors[i] = ModuleDescriptor.read(imageReader.getResourceBuffer(loc));
// add the recorded hashes of tied modules
Hashes.add(descriptors[i]);
}
return descriptors;
}
private static boolean isFastPathSupported() {
return SystemModules.MODULE_NAMES.length > 0;
}
private static String[] moduleNames() {
if (isFastPathSupported())
// module names recorded at link time
return SystemModules.MODULE_NAMES;
// this happens when java.base is patched with java.base
// from an exploded image
return imageReader.getModuleNames();
}
private static ModuleReference toModuleReference(ModuleDescriptor md,
HashSupplier hash)
{
String mn = md.name();
URI uri = URI.create("jrt:/" + mn);
Supplier<ModuleReader> readerSupplier = new Supplier<>() {
@Override @Override
public String generate(String algorithm) { public ModuleReader get() {
return hash; return new ImageModuleReader(mn, uri);
} }
}; };
ModuleReference mref =
new ModuleReference(md, uri, readerSupplier, hash);
// may need a reference to a patched module if -Xpatch specified
mref = ModulePatcher.interposeIfNeeded(mref);
return mref;
}
private static HashSupplier hashSupplier(int index, String name) {
if (isFastPathSupported()) {
return new HashSupplier() {
@Override
public String generate(String algorithm) {
return SystemModules.MODULES_TO_HASH[index];
}
};
} else {
return Hashes.hashFor(name);
}
}
/*
* This helper class is only used when SystemModules is patched.
* It will get the recorded hashes from module-info.class.
*/
private static class Hashes {
static Map<String, String> hashes = new HashMap<>();
static void add(ModuleDescriptor descriptor) {
Optional<ModuleHashes> ohashes = descriptor.hashes();
if (ohashes.isPresent()) {
hashes.putAll(ohashes.get().hashes());
}
}
static HashSupplier hashFor(String name) {
if (!hashes.containsKey(name))
return null;
return new HashSupplier() {
@Override
public String generate(String algorithm) {
return hashes.get(name);
}
};
}
} }
SystemModuleFinder() { } SystemModuleFinder() { }

View File

@ -149,6 +149,17 @@ public final class ImageReader implements AutoCloseable {
return reader.getEntryNames(); return reader.getEntryNames();
} }
public String[] getModuleNames() {
Objects.requireNonNull(reader, "image file closed");
int off = "/modules/".length();
return reader.findNode("/modules")
.getChildren()
.stream()
.map(Node::getNameString)
.map(s -> s.substring(off, s.length()))
.toArray(String[]::new);
}
public long[] getAttributes(int offset) { public long[] getAttributes(int offset) {
Objects.requireNonNull(reader, "image file closed"); Objects.requireNonNull(reader, "image file closed");
return reader.getAttributes(offset); return reader.getAttributes(offset);

View File

@ -46,12 +46,12 @@ public final class SystemModules {
* and read module-info.class from the run-time image instead of * and read module-info.class from the run-time image instead of
* the fastpath. * the fastpath.
*/ */
public static final String[] MODULE_NAMES = new String[1]; public static final String[] MODULE_NAMES = new String[0];
/** /**
* Hash of system modules. * Hash of system modules.
*/ */
public static String[] MODULES_TO_HASH = new String[1]; public static String[] MODULES_TO_HASH = new String[0];
/** /**
* Number of packages in the boot layer from the installed modules. * Number of packages in the boot layer from the installed modules.
@ -67,7 +67,7 @@ public final class SystemModules {
* When running an exploded image it returns an empty array. * When running an exploded image it returns an empty array.
*/ */
public static ModuleDescriptor[] modules() { public static ModuleDescriptor[] modules() {
return new ModuleDescriptor[0]; throw new InternalError("should not reach here");
} }
} }

View File

@ -0,0 +1,225 @@
/**
* Copyright (c) 2016, 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.
*/
/*
* @test
* @bug 8157068
* @summary Patch java.base and user module with Hashes attribute tied with
* other module.
* @library /lib/testlibrary
* @modules jdk.compiler
* @build CompilerUtils
* @run testng PatchSystemModules
*/
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import jdk.testlibrary.FileUtils;
import jdk.testlibrary.JDKToolFinder;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import static jdk.testlibrary.ProcessTools.executeCommand;
import static org.testng.Assert.*;
public class PatchSystemModules {
private static final String JAVA_HOME = System.getProperty("java.home");
private static final Path TEST_SRC = Paths.get(System.getProperty("test.src"));
private static final Path PATCH_SRC_DIR = TEST_SRC.resolve("src1");
private static final Path JMODS = Paths.get(JAVA_HOME, "jmods");
private static final Path MODS_DIR = Paths.get("mods");
private static final Path JARS_DIR = Paths.get("jars");
private static final Path PATCH_DIR = Paths.get("patches");
private static final Path IMAGE = Paths.get("image");
private static final String JAVA_BASE = "java.base";
private final String[] modules = new String[] { "m1", "m2" };
@BeforeTest
private void setup() throws Throwable {
Path src = TEST_SRC.resolve("src");
for (String name : modules) {
assertTrue(CompilerUtils.compile(src.resolve(name),
MODS_DIR,
"-modulesourcepath", src.toString()));
}
// compile patched source
assertTrue(CompilerUtils.compile(PATCH_SRC_DIR.resolve(JAVA_BASE),
PATCH_DIR.resolve(JAVA_BASE),
"-Xmodule:java.base"));
assertTrue(CompilerUtils.compile(PATCH_SRC_DIR.resolve("m2"),
PATCH_DIR.resolve("m2")));
// create an image with only m1 and m2
if (Files.exists(JMODS)) {
// create an image with m1,m2
createImage();
}
}
@Test
public void test() throws Throwable {
Path patchedJavaBase = PATCH_DIR.resolve(JAVA_BASE);
Path patchedM2 = PATCH_DIR.resolve("m2");
Path home = Paths.get(JAVA_HOME);
runTest(home,
"-mp", MODS_DIR.toString(),
"-m", "m1/p1.Main", "1");
runTest(home,
"-Xpatch:java.base=" + patchedJavaBase.toString(),
"-mp", MODS_DIR.toString(),
"-m", "m1/p1.Main", "1");
runTest(home,
"-Xpatch:m2=" + patchedM2.toString(),
"-mp", MODS_DIR.toString(),
"-m", "m1/p1.Main", "2");
}
@Test
public void testImage() throws Throwable {
if (Files.notExists(JMODS))
return;
Path patchedJavaBase = PATCH_DIR.resolve(JAVA_BASE);
Path patchedM2 = PATCH_DIR.resolve("m2");
runTest(IMAGE,
"-m", "m1/p1.Main", "1");
runTest(IMAGE,
"-Xpatch:java.base=" + patchedJavaBase.toString(),
"-m", "m1/p1.Main", "1");
runTest(IMAGE,
"-Xpatch:m2=" + patchedM2.toString(),
"-m", "m1/p1.Main", "2");
}
@Test
public void upgradeTiedModule() throws Throwable {
if (Files.notExists(JMODS))
return;
Path m1 = MODS_DIR.resolve("m1.jar");
// create another m1.jar
jar("--create",
"--file=" + m1.toString(),
"-C", MODS_DIR.resolve("m1").toString(), ".");
// Fail to upgrade m1.jar with mismatched hash
runTestWithExitCode(getJava(IMAGE),
"-upgrademodulepath", m1.toString(),
"-m", "m1/p1.Main");
runTestWithExitCode(getJava(IMAGE),
"-Xpatch:java.base=" + PATCH_DIR.resolve(JAVA_BASE).toString(),
"-upgrademodulepath", m1.toString(),
"-m", "m1/p1.Main", "1");
}
private void runTestWithExitCode(String... options) throws Throwable {
assertTrue(executeCommand(options)
.outputTo(System.out)
.errorTo(System.out)
.shouldContain("differs to expected hash")
.getExitValue() != 0);
}
private void runTest(Path image, String... opts) throws Throwable {
String[] options =
Stream.concat(Stream.of(getJava(image)),
Stream.of(opts))
.toArray(String[]::new);
ProcessBuilder pb = new ProcessBuilder(options);
int exitValue = executeCommand(pb)
.outputTo(System.out)
.errorTo(System.out)
.getExitValue();
assertTrue(exitValue == 0);
}
static void createImage() throws Throwable {
FileUtils.deleteFileTreeUnchecked(JARS_DIR);
FileUtils.deleteFileTreeUnchecked(IMAGE);
Files.createDirectories(JARS_DIR);
Path m1 = JARS_DIR.resolve("m1.jar");
Path m2 = JARS_DIR.resolve("m2.jar");
// hash m1 in m2's Hashes attribute
jar("--create",
"--file=" + m1.toString(),
"-C", MODS_DIR.resolve("m1").toString(), ".");
jar("--create",
"--file=" + m2.toString(),
"--modulepath", JARS_DIR.toString(),
"--hash-modules", "m1",
"-C", MODS_DIR.resolve("m2").toString(), ".");
String mpath = JARS_DIR.toString() + File.pathSeparator + JMODS.toString();
execTool("jlink", "--modulepath", mpath,
"--addmods", "m1",
"--output", IMAGE.toString());
}
static void jar(String... args) throws Throwable {
execTool("jar", args);
}
static void execTool(String tool, String... args) throws Throwable {
String path = JDKToolFinder.getJDKTool(tool);
List<String> commands = new ArrayList<>();
commands.add(path);
Stream.of(args).forEach(commands::add);
ProcessBuilder pb = new ProcessBuilder(commands);
int exitValue = executeCommand(pb)
.outputTo(System.out)
.errorTo(System.out)
.shouldNotContain("no module is recorded in hash")
.getExitValue();
assertTrue(exitValue == 0);
}
static String getJava(Path image) {
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("win");
Path java = image.resolve("bin").resolve(isWindows ? "java.exe" : "java");
if (Files.notExists(java))
throw new RuntimeException(java + " not found");
return java.toAbsolutePath().toString();
}
}

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2016, 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.
*/
module m1 {
requires m2;
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2016, 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.
*/
package p1;
public class Main {
public static void main(String[] args) throws Exception {
int version = p2.Lib.version();
int expected = args.length == 0 ? 1 : Integer.parseInt(args[0]);
if (version != expected)
throw new RuntimeException(version + " != " + expected + " (expected)");
}
}

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2016, 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.
*/
module m2 {
exports p2;
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2016, 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.
*/
package p2;
public class Lib {
public static int version() {
return 1;
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2016, 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.
*/
package jdk.internal.module;
/*
* Test -Xpatch:java.base=jdk/modules/java.base to override
* java.base with an exploded image
*/
public final class SystemModules {
public static final String[] MODULE_NAMES = new String[0];
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2016, 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.
*/
package p2;
public class Lib {
public static int version() {
return 2;
}
}