mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-15 13:49:42 +02:00
8360037: Refactor ImageReader in preparation for Valhalla support
Reviewed-by: alanb, rriggs, jpai
This commit is contained in:
parent
5a442197d2
commit
b81f4faed7
10 changed files with 880 additions and 626 deletions
File diff suppressed because it is too large
Load diff
|
@ -119,9 +119,9 @@ class ExplodedImage extends SystemImage {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getChildren() {
|
||||
public Stream<String> getChildNames() {
|
||||
if (!isDirectory())
|
||||
throw new IllegalArgumentException("not a directory: " + getNameString());
|
||||
throw new IllegalArgumentException("not a directory: " + getName());
|
||||
if (children == null) {
|
||||
List<Node> list = new ArrayList<>();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
|
||||
|
@ -138,7 +138,7 @@ class ExplodedImage extends SystemImage {
|
|||
}
|
||||
children = list;
|
||||
}
|
||||
return children;
|
||||
return children.stream().map(Node::getName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, 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
|
||||
|
@ -49,7 +49,7 @@ final class JrtFileAttributes implements BasicFileAttributes {
|
|||
//-------- basic attributes --------
|
||||
@Override
|
||||
public FileTime creationTime() {
|
||||
return node.creationTime();
|
||||
return node.getFileAttributes().creationTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,12 +69,12 @@ final class JrtFileAttributes implements BasicFileAttributes {
|
|||
|
||||
@Override
|
||||
public FileTime lastAccessTime() {
|
||||
return node.lastAccessTime();
|
||||
return node.getFileAttributes().lastAccessTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime lastModifiedTime() {
|
||||
return node.lastModifiedTime();
|
||||
return node.getFileAttributes().lastModifiedTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -54,7 +54,6 @@ import java.nio.file.attribute.FileAttribute;
|
|||
import java.nio.file.attribute.FileTime;
|
||||
import java.nio.file.attribute.UserPrincipalLookupService;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
@ -64,7 +63,6 @@ import java.util.Objects;
|
|||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import jdk.internal.jimage.ImageReader.Node;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* jrt file system implementation built on System jimage files.
|
||||
|
@ -225,19 +223,19 @@ class JrtFileSystem extends FileSystem {
|
|||
throw new NotDirectoryException(path.getName());
|
||||
}
|
||||
if (filter == null) {
|
||||
return node.getChildren()
|
||||
.stream()
|
||||
.map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))
|
||||
.iterator();
|
||||
return node.getChildNames()
|
||||
.map(child -> (Path) (path.resolve(new JrtPath(this, child).getFileName())))
|
||||
.iterator();
|
||||
}
|
||||
return node.getChildren()
|
||||
.stream()
|
||||
.map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))
|
||||
.filter(p -> { try { return filter.accept(p);
|
||||
} catch (IOException x) {}
|
||||
return false;
|
||||
})
|
||||
.iterator();
|
||||
return node.getChildNames()
|
||||
.map(child -> (Path) (path.resolve(new JrtPath(this, child).getFileName())))
|
||||
.filter(p -> {
|
||||
try {
|
||||
return filter.accept(p);
|
||||
} catch (IOException x) {}
|
||||
return false;
|
||||
})
|
||||
.iterator();
|
||||
}
|
||||
|
||||
// returns the content of the file resource specified by the path
|
||||
|
|
|
@ -58,7 +58,6 @@ abstract class SystemImage {
|
|||
if (modulesImageExists) {
|
||||
// open a .jimage and build directory structure
|
||||
final ImageReader image = ImageReader.open(moduleImageFile);
|
||||
image.getRootDirectory();
|
||||
return new SystemImage() {
|
||||
@Override
|
||||
Node findNode(String path) throws IOException {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2025, 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
|
||||
|
@ -34,7 +34,6 @@ import java.lang.module.ModuleReader;
|
|||
import java.lang.module.ModuleReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.URI;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
@ -54,7 +53,6 @@ import java.util.function.Supplier;
|
|||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import jdk.internal.jimage.ImageLocation;
|
||||
import jdk.internal.jimage.ImageReader;
|
||||
import jdk.internal.jimage.ImageReaderFactory;
|
||||
import jdk.internal.access.JavaNetUriAccess;
|
||||
|
@ -210,7 +208,7 @@ public final class SystemModuleFinders {
|
|||
}
|
||||
|
||||
/**
|
||||
* Parses the module-info.class of all module in the runtime image and
|
||||
* Parses the {@code module-info.class} of all modules in the runtime image and
|
||||
* returns a ModuleFinder to find the modules.
|
||||
*
|
||||
* @apiNote The returned ModuleFinder is thread safe.
|
||||
|
@ -219,20 +217,16 @@ public final class SystemModuleFinders {
|
|||
// parse the module-info.class in every module
|
||||
Map<String, ModuleInfo.Attributes> nameToAttributes = new HashMap<>();
|
||||
Map<String, byte[]> nameToHash = new HashMap<>();
|
||||
ImageReader reader = SystemImage.reader();
|
||||
for (String mn : reader.getModuleNames()) {
|
||||
ImageLocation loc = reader.findLocation(mn, "module-info.class");
|
||||
ModuleInfo.Attributes attrs
|
||||
= ModuleInfo.read(reader.getResourceBuffer(loc), null);
|
||||
|
||||
nameToAttributes.put(mn, attrs);
|
||||
allModuleAttributes().forEach(attrs -> {
|
||||
nameToAttributes.put(attrs.descriptor().name(), attrs);
|
||||
ModuleHashes hashes = attrs.recordedHashes();
|
||||
if (hashes != null) {
|
||||
for (String name : hashes.names()) {
|
||||
nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// create a ModuleReference for each module
|
||||
Set<ModuleReference> mrefs = new HashSet<>();
|
||||
|
@ -253,6 +247,40 @@ public final class SystemModuleFinders {
|
|||
return new SystemModuleFinder(mrefs, nameToModule);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@code module-info.class} of all modules in the runtime image and
|
||||
* returns a stream of {@link ModuleInfo.Attributes Attributes} for them. The
|
||||
* returned attributes are in no specific order.
|
||||
*/
|
||||
private static Stream<ModuleInfo.Attributes> allModuleAttributes() {
|
||||
// System-wide image reader.
|
||||
ImageReader reader = SystemImage.reader();
|
||||
try {
|
||||
return reader.findNode("/modules")
|
||||
.getChildNames()
|
||||
.map(mn -> readModuleAttributes(reader, mn));
|
||||
} catch (IOException e) {
|
||||
throw new Error("Error reading root /modules entry", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the module's "module-info", returning a holder for its class file
|
||||
* attributes. Every module is required to have a valid {@code module-info.class}.
|
||||
*/
|
||||
private static ModuleInfo.Attributes readModuleAttributes(ImageReader reader, String moduleName) {
|
||||
Exception err = null;
|
||||
try {
|
||||
ImageReader.Node node = reader.findNode(moduleName + "/module-info.class");
|
||||
if (node != null && node.isResource()) {
|
||||
return ModuleInfo.read(reader.getResourceBuffer(node), null);
|
||||
}
|
||||
} catch (IOException | UncheckedIOException e) {
|
||||
err = e;
|
||||
}
|
||||
throw new Error("Missing or invalid module-info.class for module: " + moduleName, err);
|
||||
}
|
||||
|
||||
/**
|
||||
* A ModuleFinder that finds module in an array or set of modules.
|
||||
*/
|
||||
|
@ -382,34 +410,18 @@ public final class SystemModuleFinders {
|
|||
this.module = module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ImageLocation for the given resource, {@code null}
|
||||
* if not found.
|
||||
*/
|
||||
private ImageLocation findImageLocation(String name) throws IOException {
|
||||
Objects.requireNonNull(name);
|
||||
if (closed)
|
||||
throw new IOException("ModuleReader is closed");
|
||||
ImageReader imageReader = SystemImage.reader();
|
||||
if (imageReader != null) {
|
||||
return imageReader.findLocation(module, name);
|
||||
} else {
|
||||
// not an images build
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given resource exists, {@code false}
|
||||
* if not found.
|
||||
*/
|
||||
private boolean containsImageLocation(String name) throws IOException {
|
||||
Objects.requireNonNull(name);
|
||||
private boolean containsResource(String resourcePath) throws IOException {
|
||||
Objects.requireNonNull(resourcePath);
|
||||
if (closed)
|
||||
throw new IOException("ModuleReader is closed");
|
||||
ImageReader imageReader = SystemImage.reader();
|
||||
if (imageReader != null) {
|
||||
return imageReader.verifyLocation(module, name);
|
||||
ImageReader.Node node = imageReader.findNode("/modules" + resourcePath);
|
||||
return node != null && node.isResource();
|
||||
} else {
|
||||
// not an images build
|
||||
return false;
|
||||
|
@ -418,8 +430,9 @@ public final class SystemModuleFinders {
|
|||
|
||||
@Override
|
||||
public Optional<URI> find(String name) throws IOException {
|
||||
if (containsImageLocation(name)) {
|
||||
URI u = JNUA.create("jrt", "/" + module + "/" + name);
|
||||
String resourcePath = "/" + module + "/" + name;
|
||||
if (containsResource(resourcePath)) {
|
||||
URI u = JNUA.create("jrt", resourcePath);
|
||||
return Optional.of(u);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
|
@ -442,14 +455,25 @@ public final class SystemModuleFinders {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node for the given resource if found. If the name references
|
||||
* a non-resource node, then {@code null} is returned.
|
||||
*/
|
||||
private ImageReader.Node findResource(ImageReader reader, String name) throws IOException {
|
||||
Objects.requireNonNull(name);
|
||||
if (closed) {
|
||||
throw new IOException("ModuleReader is closed");
|
||||
}
|
||||
String nodeName = "/modules/" + module + "/" + name;
|
||||
ImageReader.Node node = reader.findNode(nodeName);
|
||||
return (node != null && node.isResource()) ? node : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ByteBuffer> read(String name) throws IOException {
|
||||
ImageLocation location = findImageLocation(name);
|
||||
if (location != null) {
|
||||
return Optional.of(SystemImage.reader().getResourceBuffer(location));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
ImageReader reader = SystemImage.reader();
|
||||
return Optional.ofNullable(findResource(reader, name))
|
||||
.map(reader::getResourceBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -481,7 +505,7 @@ public final class SystemModuleFinders {
|
|||
private static class ModuleContentSpliterator implements Spliterator<String> {
|
||||
final String moduleRoot;
|
||||
final Deque<ImageReader.Node> stack;
|
||||
Iterator<ImageReader.Node> iterator;
|
||||
Iterator<String> iterator;
|
||||
|
||||
ModuleContentSpliterator(String module) throws IOException {
|
||||
moduleRoot = "/modules/" + module;
|
||||
|
@ -502,13 +526,10 @@ public final class SystemModuleFinders {
|
|||
private String next() throws IOException {
|
||||
for (;;) {
|
||||
while (iterator.hasNext()) {
|
||||
ImageReader.Node node = iterator.next();
|
||||
String name = node.getName();
|
||||
String name = iterator.next();
|
||||
ImageReader.Node node = SystemImage.reader().findNode(name);
|
||||
if (node.isDirectory()) {
|
||||
// build node
|
||||
ImageReader.Node dir = SystemImage.reader().findNode(name);
|
||||
assert dir.isDirectory();
|
||||
stack.push(dir);
|
||||
stack.push(node);
|
||||
} else {
|
||||
// strip /modules/$MODULE/ prefix
|
||||
return name.substring(moduleRoot.length() + 1);
|
||||
|
@ -520,7 +541,7 @@ public final class SystemModuleFinders {
|
|||
} else {
|
||||
ImageReader.Node dir = stack.poll();
|
||||
assert dir.isDirectory();
|
||||
iterator = dir.getChildren().iterator();
|
||||
iterator = dir.getChildNames().iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
279
test/jdk/jdk/internal/jimage/ImageReaderTest.java
Normal file
279
test/jdk/jdk/internal/jimage/ImageReaderTest.java
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Copyright (c) 2025, 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 jdk.internal.jimage.ImageReader;
|
||||
import jdk.internal.jimage.ImageReader.Node;
|
||||
import jdk.test.lib.compiler.InMemoryJavaCompiler;
|
||||
import jdk.test.lib.util.JarBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.opentest4j.TestSkippedException;
|
||||
import tests.Helper;
|
||||
import tests.JImageGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Tests for ImageReader.
|
||||
* @modules java.base/jdk.internal.jimage
|
||||
* jdk.jlink/jdk.tools.jimage
|
||||
* @library /test/jdk/tools/lib
|
||||
* /test/lib
|
||||
* @build tests.*
|
||||
* @run junit/othervm ImageReaderTest
|
||||
*/
|
||||
|
||||
/// Using PER_CLASS lifecycle means the (expensive) image file is only build once.
|
||||
/// There is no mutable test instance state to worry about.
|
||||
@TestInstance(PER_CLASS)
|
||||
public class ImageReaderTest {
|
||||
|
||||
private static final Map<String, List<String>> IMAGE_ENTRIES = Map.of(
|
||||
"modfoo", Arrays.asList(
|
||||
"com.foo.Alpha",
|
||||
"com.foo.Beta",
|
||||
"com.foo.bar.Gamma"),
|
||||
"modbar", Arrays.asList(
|
||||
"com.bar.One",
|
||||
"com.bar.Two"));
|
||||
private final Path image = buildJImage(IMAGE_ENTRIES);
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"/",
|
||||
"/modules",
|
||||
"/modules/modfoo",
|
||||
"/modules/modbar",
|
||||
"/modules/modfoo/com",
|
||||
"/modules/modfoo/com/foo",
|
||||
"/modules/modfoo/com/foo/bar"})
|
||||
public void testModuleDirectories_expected(String name) throws IOException {
|
||||
try (ImageReader reader = ImageReader.open(image)) {
|
||||
assertDir(reader, name);
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"",
|
||||
"//",
|
||||
"/modules/",
|
||||
"/modules/unknown",
|
||||
"/modules/modbar/",
|
||||
"/modules/modfoo//com",
|
||||
"/modules/modfoo/com/"})
|
||||
public void testModuleNodes_absent(String name) throws IOException {
|
||||
try (ImageReader reader = ImageReader.open(image)) {
|
||||
assertAbsent(reader, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModuleResources() throws IOException {
|
||||
try (ImageReader reader = ImageReader.open(image)) {
|
||||
assertNode(reader, "/modules/modfoo/com/foo/Alpha.class");
|
||||
assertNode(reader, "/modules/modbar/com/bar/One.class");
|
||||
|
||||
ImageClassLoader loader = new ImageClassLoader(reader, IMAGE_ENTRIES.keySet());
|
||||
assertEquals("Class: com.foo.Alpha", loader.loadAndGetToString("modfoo", "com.foo.Alpha"));
|
||||
assertEquals("Class: com.foo.Beta", loader.loadAndGetToString("modfoo", "com.foo.Beta"));
|
||||
assertEquals("Class: com.foo.bar.Gamma", loader.loadAndGetToString("modfoo", "com.foo.bar.Gamma"));
|
||||
assertEquals("Class: com.bar.One", loader.loadAndGetToString("modbar", "com.bar.One"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackageDirectories() throws IOException {
|
||||
try (ImageReader reader = ImageReader.open(image)) {
|
||||
Node root = assertDir(reader, "/packages");
|
||||
Set<String> pkgNames = root.getChildNames().collect(Collectors.toSet());
|
||||
assertTrue(pkgNames.contains("/packages/com"));
|
||||
assertTrue(pkgNames.contains("/packages/com.foo"));
|
||||
assertTrue(pkgNames.contains("/packages/com.bar"));
|
||||
|
||||
// Even though no classes exist directly in the "com" package, it still
|
||||
// creates a directory with links back to all the modules which contain it.
|
||||
Set<String> comLinks = assertDir(reader, "/packages/com").getChildNames().collect(Collectors.toSet());
|
||||
assertTrue(comLinks.contains("/packages/com/modfoo"));
|
||||
assertTrue(comLinks.contains("/packages/com/modbar"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackageLinks() throws IOException {
|
||||
try (ImageReader reader = ImageReader.open(image)) {
|
||||
Node moduleFoo = assertDir(reader, "/modules/modfoo");
|
||||
Node moduleBar = assertDir(reader, "/modules/modbar");
|
||||
assertSame(assertLink(reader, "/packages/com.foo/modfoo").resolveLink(), moduleFoo);
|
||||
assertSame(assertLink(reader, "/packages/com.bar/modbar").resolveLink(), moduleBar);
|
||||
}
|
||||
}
|
||||
|
||||
private static ImageReader.Node assertNode(ImageReader reader, String name) throws IOException {
|
||||
ImageReader.Node node = reader.findNode(name);
|
||||
assertNotNull(node, "Could not find node: " + name);
|
||||
return node;
|
||||
}
|
||||
|
||||
private static ImageReader.Node assertDir(ImageReader reader, String name) throws IOException {
|
||||
ImageReader.Node dir = assertNode(reader, name);
|
||||
assertTrue(dir.isDirectory(), "Node was not a directory: " + name);
|
||||
return dir;
|
||||
}
|
||||
|
||||
private static ImageReader.Node assertLink(ImageReader reader, String name) throws IOException {
|
||||
ImageReader.Node link = assertNode(reader, name);
|
||||
assertTrue(link.isLink(), "Node was not a symbolic link: " + name);
|
||||
return link;
|
||||
}
|
||||
|
||||
private static void assertAbsent(ImageReader reader, String name) throws IOException {
|
||||
assertNull(reader.findNode(name), "Should not be able to find node: " + name);
|
||||
}
|
||||
|
||||
/// Builds a jimage file with the specified class entries. The classes in the built
|
||||
/// image can be loaded and executed to return their names via `toString()` to confirm
|
||||
/// the correct bytes were returned.
|
||||
public static Path buildJImage(Map<String, List<String>> entries) {
|
||||
Helper helper = getHelper();
|
||||
Path outDir = helper.createNewImageDir("test");
|
||||
JImageGenerator.JLinkTask jlink = JImageGenerator.getJLinkTask()
|
||||
.modulePath(helper.defaultModulePath())
|
||||
.output(outDir);
|
||||
|
||||
Path jarDir = helper.getJarDir();
|
||||
entries.forEach((module, classes) -> {
|
||||
JarBuilder jar = new JarBuilder(jarDir.resolve(module + ".jar").toString());
|
||||
String moduleInfo = "module " + module + " {}";
|
||||
jar.addEntry("module-info.class", InMemoryJavaCompiler.compile("module-info", moduleInfo));
|
||||
|
||||
classes.forEach(fqn -> {
|
||||
int lastDot = fqn.lastIndexOf('.');
|
||||
String pkg = fqn.substring(0, lastDot);
|
||||
String cls = fqn.substring(lastDot + 1);
|
||||
|
||||
String path = fqn.replace('.', '/') + ".class";
|
||||
String source = String.format(
|
||||
"""
|
||||
package %s;
|
||||
public class %s {
|
||||
public String toString() {
|
||||
return "Class: %s";
|
||||
}
|
||||
}
|
||||
""", pkg, cls, fqn);
|
||||
jar.addEntry(path, InMemoryJavaCompiler.compile(fqn, source));
|
||||
});
|
||||
try {
|
||||
jar.build();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
jlink.addMods(module);
|
||||
});
|
||||
return jlink.call().assertSuccess().resolve("lib", "modules");
|
||||
}
|
||||
|
||||
/// Returns the helper for building JAR and jimage files.
|
||||
private static Helper getHelper() {
|
||||
try {
|
||||
Helper helper = Helper.newHelper();
|
||||
if (helper == null) {
|
||||
throw new TestSkippedException("Cannot create test helper (exploded image?)");
|
||||
}
|
||||
return helper;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads and performs actions on classes stored in a given `ImageReader`.
|
||||
private static class ImageClassLoader extends ClassLoader {
|
||||
private final ImageReader reader;
|
||||
private final Set<String> testModules;
|
||||
|
||||
private ImageClassLoader(ImageReader reader, Set<String> testModules) {
|
||||
this.reader = reader;
|
||||
this.testModules = testModules;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ClassAction<R, T extends Exception> {
|
||||
R call(Class<?> cls) throws T;
|
||||
}
|
||||
|
||||
String loadAndGetToString(String module, String fqn) {
|
||||
return loadAndCall(module, fqn, c -> c.getDeclaredConstructor().newInstance().toString());
|
||||
}
|
||||
|
||||
<R> R loadAndCall(String module, String fqn, ClassAction<R, ?> action) {
|
||||
Class<?> cls = findClass(module, fqn);
|
||||
assertNotNull(cls, "Could not load class: " + module + "/" + fqn);
|
||||
try {
|
||||
return action.call(cls);
|
||||
} catch (Exception e) {
|
||||
fail("Class loading failed", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String module, String fqn) {
|
||||
assumeTrue(testModules.contains(module), "Can only load classes in modules: " + testModules);
|
||||
String name = "/modules/" + module + "/" + fqn.replace('.', '/') + ".class";
|
||||
Class<?> cls = findLoadedClass(fqn);
|
||||
if (cls == null) {
|
||||
try {
|
||||
ImageReader.Node node = reader.findNode(name);
|
||||
if (node != null && node.isResource()) {
|
||||
byte[] classBytes = reader.getResource(node);
|
||||
cls = defineClass(fqn, classBytes, 0, classBytes.length);
|
||||
resolveClass(cls);
|
||||
return cls;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2025, 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
|
||||
|
@ -49,6 +49,9 @@ import org.testng.annotations.Test;
|
|||
import org.testng.Assert;
|
||||
import org.testng.TestNG;
|
||||
|
||||
import static java.nio.ByteOrder.BIG_ENDIAN;
|
||||
import static java.nio.ByteOrder.LITTLE_ENDIAN;
|
||||
|
||||
@Test
|
||||
public class JImageReadTest {
|
||||
|
||||
|
@ -333,32 +336,21 @@ public class JImageReadTest {
|
|||
*/
|
||||
@Test
|
||||
static void test5_imageReaderEndianness() throws IOException {
|
||||
ImageReader nativeReader = ImageReader.open(imageFile);
|
||||
Assert.assertEquals(nativeReader.getByteOrder(), ByteOrder.nativeOrder());
|
||||
|
||||
try {
|
||||
ImageReader leReader = ImageReader.open(imageFile, ByteOrder.LITTLE_ENDIAN);
|
||||
Assert.assertEquals(leReader.getByteOrder(), ByteOrder.LITTLE_ENDIAN);
|
||||
leReader.close();
|
||||
} catch (IOException io) {
|
||||
// IOException expected if LITTLE_ENDIAN not the nativeOrder()
|
||||
Assert.assertNotEquals(ByteOrder.nativeOrder(), ByteOrder.LITTLE_ENDIAN);
|
||||
// Will be opened with native byte order.
|
||||
try (ImageReader nativeReader = ImageReader.open(imageFile)) {
|
||||
// Just ensure something works as expected.
|
||||
Assert.assertNotNull(nativeReader.findNode("/"));
|
||||
} catch (IOException expected) {
|
||||
Assert.fail("Reader should be openable with native byte order.");
|
||||
}
|
||||
|
||||
try {
|
||||
ImageReader beReader = ImageReader.open(imageFile, ByteOrder.BIG_ENDIAN);
|
||||
Assert.assertEquals(beReader.getByteOrder(), ByteOrder.BIG_ENDIAN);
|
||||
beReader.close();
|
||||
} catch (IOException io) {
|
||||
// IOException expected if LITTLE_ENDIAN not the nativeOrder()
|
||||
Assert.assertNotEquals(ByteOrder.nativeOrder(), ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
|
||||
nativeReader.close();
|
||||
// Reader should not be openable with the wrong byte order.
|
||||
ByteOrder otherOrder = ByteOrder.nativeOrder() == BIG_ENDIAN ? LITTLE_ENDIAN : BIG_ENDIAN;
|
||||
Assert.assertThrows(IOException.class, () -> ImageReader.open(imageFile, otherOrder));
|
||||
}
|
||||
// main method to run standalone from jtreg
|
||||
|
||||
@Test(enabled=false)
|
||||
// main method to run standalone from jtreg
|
||||
@Test(enabled = false)
|
||||
@Parameters({"x"})
|
||||
@SuppressWarnings("raw_types")
|
||||
public static void main(@Optional String[] args) {
|
||||
|
|
|
@ -68,17 +68,17 @@ public class ImageReaderDuplicateChildNodesTest {
|
|||
+ " in " + imagePath);
|
||||
}
|
||||
// now verify that the parent node which is a directory, doesn't have duplicate children
|
||||
final List<ImageReader.Node> children = parent.getChildren();
|
||||
if (children == null || children.isEmpty()) {
|
||||
final List<String> childNames = parent.getChildNames().toList();
|
||||
if (childNames.isEmpty()) {
|
||||
throw new RuntimeException("ImageReader did not return any child resources under "
|
||||
+ integersParentResource + " in " + imagePath);
|
||||
}
|
||||
final Set<ImageReader.Node> uniqueChildren = new HashSet<>();
|
||||
for (final ImageReader.Node child : children) {
|
||||
final boolean unique = uniqueChildren.add(child);
|
||||
for (final String childName : childNames) {
|
||||
final boolean unique = uniqueChildren.add(reader.findNode(childName));
|
||||
if (!unique) {
|
||||
throw new RuntimeException("ImageReader returned duplicate child resource "
|
||||
+ child + " under " + parent + " from image " + imagePath);
|
||||
+ childName + " under " + parent + " from image " + imagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -195,9 +195,9 @@ public class ImageReaderBenchmark {
|
|||
static long countAllNodes(ImageReader reader, Node node) {
|
||||
long count = 1;
|
||||
if (node.isDirectory()) {
|
||||
count += node.getChildren().stream().mapToLong(n -> {
|
||||
count += node.getChildNames().mapToLong(n -> {
|
||||
try {
|
||||
return countAllNodes(reader, reader.findNode(n.getName()));
|
||||
return countAllNodes(reader, reader.findNode(n));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue