On Wed, 13 Aug 2025 12:20:23 GMT, David Beaumont <[email protected]> wrote:
> This PR addresses several issues found while adding unit tests for
> ExplodedImage.
> I have added review comments for changes (some of which are a little
> preferential, but bring the code into line with things like ImageReader in
> terms of the name choices for variables).
> Later it is likely that this code will be adapted for the up-coming preview
> mode support in Valhalla, so having it unit-testable is important.
Adding explanatory notes for the non-test changes/fixes.
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 59:
> 57: private static final String PACKAGES = "/packages/";
> 58:
> 59: private final Path modulesDir;
Needed to make this class actually testable and avoid just using the static
field from SystemImage.
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 65:
> 63:
> 64: ExplodedImage(Path modulesDir) throws IOException {
> 65: this.modulesDir = modulesDir;
Avoid unnecessary assumptions about file systems.
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 95:
> 93: }
> 94:
> 95: @Override
Discovered this method was missing during testing. I *think* this logic is
what's needed here, but I would like someone to just double check.
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 132:
> 130: try (DirectoryStream<Path> stream =
> Files.newDirectoryStream(path)) {
> 131: for (Path p : stream) {
> 132: p = modulesDir.relativize(p);
Avoid using static field.
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 158:
> 156:
> 157: @Override
> 158: public synchronized void close() throws IOException {
The nodes map is always accessed synchronised except here, so by synchronizing
this we can stop using ConcurrentHashMap.
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 171:
> 169: return node;
> 170: }
> 171: // lazily created for paths like /packages/<package>/<module>/xyz
This code is simply wrong. The lookup of
'packages/java.lang/java.base/java/lang' should fail, since there is no node
for it. It's the wrapping file system's job to test for symbolic links in the
path and resolve this sort of thing.
Only /packages, /packages/xxx and /packages/xxx/yyy nodes need to exist in
SystemImage.
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 216:
> 214: } catch (IOException x) {
> 215: // Since the path reference a file, any errors should not be
> ignored.
> 216: throw new UncheckedIOException(x);
Silently ignoring IOException was both a risk, and a possible performance
issue, since it was being used for normal code flow whenever a non-existent
node was being asked for. Now, an exception here is unconditionally a problem,
since the given path does exist.
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 226:
> 224: */
> 225: Path underlyingModulesPath(String name) {
> 226: if (name.startsWith(MODULES) && name.length() >
> MODULES.length()) {
The extra length test avoids issues when "/modules/" is given, since that
*should* be invalid but otherwise gets turned into a path to the parent dir.
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 258:
> 256: String moduleName = module.getFileName().toString();
> 257: // make sure "/modules/<moduleName>" is created
> 258: Objects.requireNonNull(createModulesNode(MODULES +
> moduleName, module));
This happens once per module, so it's where we create the root dirs (see below).
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 276:
> 274: // create "/modules" directory
> 275: // "nodes" map contains only /modules/<foo> nodes only so far
> and so add all as children of /modules
> 276: PathNode modulesRootNode = new PathNode("/modules", new
> ArrayList<>(nodes.values()));
Renamed because there's already "modulesDir" elsewhere.
src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java line 286:
> 284: List<Node> moduleLinkNodes = new
> ArrayList<>(moduleNameList.size());
> 285: for (String moduleName : moduleNameList) {
> 286: Node moduleNode =
> Objects.requireNonNull(nodes.get(MODULES + moduleName));
There's no need to call code that "creates" the module directory node here, we
did it above, and here we can just guarantee it's in the cache.
src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java line 81:
> 79: }
> 80:
> 81: private static final String RUNTIME_HOME;
Hiding these prevents unwanted use by other classes (which would make them
effectively untestable).
test/jdk/jdk/internal/jrtfs/whitebox/ExplodedImageTestDriver.java line 30:
> 28: * @run junit/othervm java.base/jdk.internal.jrtfs.ExplodedImageTest
> 29: */
> 30: public class ExplodedImageTestDriver {}
Not 100% sure if a class declaration is needed here. It seems to work fine, but
I've seen a case where there wasn't one (but that felt odd). Happy to remove if
not wanted.
test/jdk/jdk/internal/jrtfs/whitebox/TEST.properties line 1:
> 1: modules = \
Cargo-culted without a true understand of what's going on. This seems to be
what's needed for module access.
-------------
PR Review: https://git.openjdk.org/jdk/pull/26757#pullrequestreview-3115737360
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273278111
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273280133
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273285948
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273289787
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273293717
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273300351
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273308453
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273325040
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273310217
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273313715
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273312772
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273315520
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273317906
PR Review Comment: https://git.openjdk.org/jdk/pull/26757#discussion_r2273319482