Skip to content

Commit 66a87ce

Browse files
david-beaumontRoger Riggs
authored andcommitted
8371292: [lworld] Switch JLink to not use ImageReader API
Reviewed-by: rriggs
1 parent 7fba9f0 commit 66a87ce

File tree

8 files changed

+351
-169
lines changed

8 files changed

+351
-169
lines changed

src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@
3737
import java.nio.file.StandardOpenOption;
3838
import java.security.AccessController;
3939
import java.security.PrivilegedAction;
40+
import java.util.NoSuchElementException;
4041
import java.util.Objects;
42+
import java.util.Random;
4143
import java.util.stream.IntStream;
44+
import java.util.stream.Stream;
45+
4246
import jdk.internal.jimage.decompressor.Decompressor;
4347

4448
/**
@@ -326,6 +330,56 @@ public String[] getEntryNames() {
326330
.toArray(String[]::new);
327331
}
328332

333+
/**
334+
* Returns the "raw" API for accessing underlying jimage resource entries.
335+
*
336+
* <p>This is only meaningful for use by code dealing directly with jimage
337+
* files, and cannot be used to reliably lookup resources used at runtime.
338+
*
339+
* <p>This API remains valid until the image reader from which it was
340+
* obtained is closed.
341+
*/
342+
// Package visible for use by ImageReader.
343+
ResourceEntries getResourceEntries() {
344+
return new ResourceEntries() {
345+
@Override
346+
public Stream<String> getEntryNames(String module) {
347+
if (module.isEmpty() || module.equals("modules") || module.equals("packages")) {
348+
throw new IllegalArgumentException("Invalid module name: " + module);
349+
}
350+
return IntStream.range(0, offsets.capacity())
351+
.map(offsets::get)
352+
.filter(offset -> offset != 0)
353+
// Reusing a location instance or getting the module
354+
// offset directly would save a lot of allocations here.
355+
.mapToObj(offset -> ImageLocation.readFrom(BasicImageReader.this, offset))
356+
// Reverse lookup of module offset would be faster here.
357+
.filter(loc -> module.equals(loc.getModule()))
358+
.map(ImageLocation::getFullName);
359+
}
360+
361+
private ImageLocation getResourceLocation(String name) {
362+
if (!name.startsWith("/modules/") && !name.startsWith("/packages/")) {
363+
ImageLocation location = BasicImageReader.this.findLocation(name);
364+
if (location != null) {
365+
return location;
366+
}
367+
}
368+
throw new NoSuchElementException("No such resource entry: " + name);
369+
}
370+
371+
@Override
372+
public long getSize(String name) {
373+
return getResourceLocation(name).getUncompressedSize();
374+
}
375+
376+
@Override
377+
public byte[] getBytes(String name) {
378+
return BasicImageReader.this.getResource(getResourceLocation(name));
379+
}
380+
};
381+
}
382+
329383
ImageLocation getLocation(int offset) {
330384
return ImageLocation.readFrom(this, offset);
331385
}

src/java.base/share/classes/jdk/internal/jimage/ImageReader.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,11 @@ public ByteBuffer getResourceBuffer(Node node) {
230230
return reader.getResourceBuffer(node.getLocation());
231231
}
232232

233+
// Package protected for use only by SystemImageReader.
234+
ResourceEntries getResourceEntries() {
235+
return reader.getResourceEntries();
236+
}
237+
233238
private static final class SharedImageReader extends BasicImageReader {
234239
// There are >30,000 nodes in a complete jimage tree, and even relatively
235240
// common tasks (e.g. starting up javac) load somewhere in the region of
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.internal.jimage;
26+
27+
import java.io.InputStream;
28+
import java.util.stream.Stream;
29+
30+
/**
31+
* Accesses the underlying resource entries in a jimage file.
32+
*
33+
* <p>This API is designed only for use by the jlink classes, which read the raw
34+
* jimage files. Use the {@link ImageReader} API to read jimage contents at
35+
* runtime to correctly account for preview mode.
36+
*
37+
* <p>This API ignores the {@code previewMode} of the {@link ImageReader} from
38+
* which it is obtained, and returns an unmapped view of entries (e.g. allowing
39+
* for direct access of resources in the {@code META-INF/preview/...} namespace).
40+
*
41+
* <p>It disallows access to resource directories (i.e. {@code "/modules/..."})
42+
* or packages entries (i.e. {@code "/packages/..."}).
43+
*
44+
* @implNote This class needs to maintain JDK 8 source compatibility.
45+
*
46+
* It is used internally in the JDK to implement jimage/jrtfs access,
47+
* but also compiled and delivered as part of the jrtfs.jar to support access
48+
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
49+
*/
50+
public interface ResourceEntries {
51+
/**
52+
* Returns the jimage names for all resources in the given module, in
53+
* random order. Entry names will always be prefixed by the given module
54+
* name (e.g. {@code "/<module-name>/..."}).
55+
*/
56+
Stream<String> getEntryNames(String module);
57+
58+
/**
59+
* Returns the (uncompressed) size of a resource given its jimage name.
60+
*
61+
* @throws java.util.NoSuchElementException if the resource does not exist.
62+
*/
63+
long getSize(String name);
64+
65+
/**
66+
* Returns a copy of a resource's content given its jimage name.
67+
*
68+
* @throws java.util.NoSuchElementException if the resource does not exist.
69+
*/
70+
byte[] getBytes(String name);
71+
}

src/java.base/share/classes/jdk/internal/jimage/SystemImageReader.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,15 @@ public static ImageReader get() {
7373
return SYSTEM_IMAGE_READER;
7474
}
7575

76+
/**
77+
* Returns the "raw" API for accessing underlying jimage resource entries.
78+
*
79+
* <p>This is only meaningful for use by code dealing directly with jimage
80+
* files, and cannot be used to reliably lookup resources used at runtime.
81+
*/
82+
public static ResourceEntries getResourceEntries() {
83+
return get().getResourceEntries();
84+
}
85+
7686
private SystemImageReader() {}
7787
}

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Archive.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
package jdk.tools.jlink.internal;
2626

27+
import java.io.Closeable;
2728
import java.io.IOException;
2829
import java.io.InputStream;
2930
import java.nio.file.Path;
@@ -34,7 +35,7 @@
3435
* An Archive of all content, classes, resources, configuration files, and
3536
* other, for a module.
3637
*/
37-
public interface Archive {
38+
public interface Archive extends Closeable {
3839

3940
/**
4041
* Entry is contained in an Archive
@@ -59,11 +60,12 @@ public static enum EntryType {
5960
private final String path;
6061

6162
/**
62-
* Constructs an entry of the given archive
63-
* @param archive archive
64-
* @param path
65-
* @param name an entry name that does not contain the module name
66-
* @param type
63+
* Constructs an entry of the given archive.
64+
*
65+
* @param archive the archive in which this entry exists.
66+
* @param path the complete path of the entry, including the module.
67+
* @param name an entry name relative to its containing module.
68+
* @param type the entry type.
6769
*/
6870
public Entry(Archive archive, String path, String name, EntryType type) {
6971
this.archive = Objects.requireNonNull(archive);
@@ -72,10 +74,6 @@ public Entry(Archive archive, String path, String name, EntryType type) {
7274
this.type = Objects.requireNonNull(type);
7375
}
7476

75-
public final Archive archive() {
76-
return archive;
77-
}
78-
7977
public final EntryType type() {
8078
return type;
8179
}
@@ -134,5 +132,6 @@ public String toString() {
134132
/*
135133
* Close the archive
136134
*/
135+
@Override
137136
void close() throws IOException;
138137
}

0 commit comments

Comments
 (0)