Skip to content

Commit 72539b5

Browse files
david-beaumontRoger Riggs
authored andcommitted
8368467: [lworld] Add new flag generation for jimage to support preview mode
Reviewed-by: rriggs
1 parent a680c71 commit 72539b5

File tree

16 files changed

+1926
-1196
lines changed

16 files changed

+1926
-1196
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
package jdk.internal.jimage;
2727

2828
import java.nio.ByteBuffer;
29+
import java.util.List;
2930
import java.util.Objects;
3031
import java.util.function.Predicate;
3132

@@ -159,6 +160,29 @@ public static int getFlags(String name, Predicate<String> hasEntry) {
159160
}
160161
}
161162

163+
/**
164+
* Helper function to calculate package flags for {@code "/packages/xxx"}
165+
* directory entries.
166+
*
167+
* <p>Based on the module references, the flags are:
168+
* <ul>
169+
* <li>{@code FLAGS_HAS_PREVIEW_VERSION} if <em>any</em> referenced
170+
* package has a preview version.
171+
* <li>{@code FLAGS_IS_PREVIEW_ONLY} if <em>all</em> referenced packages
172+
* are preview only.
173+
* </ul>
174+
*
175+
* @return package flags for {@code "/packages/xxx"} directory entries.
176+
*/
177+
public static int getPackageFlags(List<ModuleReference> moduleReferences) {
178+
boolean hasPreviewVersion =
179+
moduleReferences.stream().anyMatch(ModuleReference::hasPreviewVersion);
180+
boolean isPreviewOnly =
181+
moduleReferences.stream().allMatch(ModuleReference::isPreviewOnly);
182+
return (hasPreviewVersion ? ImageLocation.FLAGS_HAS_PREVIEW_VERSION : 0)
183+
| (isPreviewOnly ? ImageLocation.FLAGS_IS_PREVIEW_ONLY : 0);
184+
}
185+
162186
/**
163187
* Tests a non-preview image location's flags to see if it has preview
164188
* content associated with it.

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

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ public final class ModuleReference implements Comparable<ModuleReference> {
5353
/** If set, this package exists in non-preview mode. */
5454
private static final int FLAGS_PKG_HAS_NORMAL_VERSION = 0x2;
5555
/** If set, the associated module has resources (in normal or preview mode). */
56-
// TODO: Make this private again when image writer code is updated.
57-
public static final int FLAGS_PKG_HAS_RESOURCES = 0x4;
56+
private static final int FLAGS_PKG_HAS_RESOURCES = 0x4;
5857

5958
/**
6059
* References are ordered with preview versions first which permits early
@@ -71,7 +70,7 @@ public final class ModuleReference implements Comparable<ModuleReference> {
7170
*
7271
* <p>The same reference can be used for multiple packages in the same module.
7372
*/
74-
public static ModuleReference forPackageIn(String moduleName, boolean isPreview) {
73+
public static ModuleReference forPackage(String moduleName, boolean isPreview) {
7574
return new ModuleReference(moduleName, FLAGS_PKG_HAS_RESOURCES | previewFlag(isPreview));
7675
}
7776

@@ -80,7 +79,7 @@ public static ModuleReference forPackageIn(String moduleName, boolean isPreview)
8079
*
8180
* <p>The same reference can be used for multiple packages in the same module.
8281
*/
83-
public static ModuleReference forEmptyPackageIn(String moduleName, boolean isPreview) {
82+
public static ModuleReference forEmptyPackage(String moduleName, boolean isPreview) {
8483
return new ModuleReference(moduleName, previewFlag(isPreview));
8584
}
8685

@@ -118,7 +117,7 @@ public String name() {
118117
* under many modules, it only has resources in one.
119118
*/
120119
public boolean hasResources() {
121-
return ((flags & FLAGS_PKG_HAS_RESOURCES) != 0);
120+
return (flags & FLAGS_PKG_HAS_RESOURCES) != 0;
122121
}
123122

124123
/**
@@ -176,9 +175,9 @@ public static Iterator<Integer> readNameOffsets(
176175
if (bufferSize == 0 || (bufferSize & 0x1) != 0) {
177176
throw new IllegalArgumentException("Invalid buffer size");
178177
}
179-
int testFlags = (includeNormal ? FLAGS_PKG_HAS_NORMAL_VERSION : 0)
178+
int includeMask = (includeNormal ? FLAGS_PKG_HAS_NORMAL_VERSION : 0)
180179
+ (includePreview ? FLAGS_PKG_HAS_PREVIEW_VERSION : 0);
181-
if (testFlags == 0) {
180+
if (includeMask == 0) {
182181
throw new IllegalArgumentException("Invalid flags");
183182
}
184183

@@ -188,14 +187,7 @@ public static Iterator<Integer> readNameOffsets(
188187
int nextIdx(int idx) {
189188
for (; idx < bufferSize; idx += 2) {
190189
// If any of the test flags are set, include this entry.
191-
192-
// Temporarily allow for *neither* flag to be set. This is what would
193-
// be written by a 1.0 version of the jimage flag, and indicates a
194-
// normal resource without a preview version.
195-
// TODO: Remove the zero-check below once image writer code is updated.
196-
int previewFlags =
197-
buffer.get(idx) & (FLAGS_PKG_HAS_NORMAL_VERSION | FLAGS_PKG_HAS_PREVIEW_VERSION);
198-
if (previewFlags == 0 || (previewFlags & testFlags) != 0) {
190+
if ((buffer.get(idx) & includeMask) != 0) {
199191
return idx;
200192
} else if (!includeNormal) {
201193
// Preview entries are first in the offset buffer, so we

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public enum PreviewMode {
5656
* Resolves whether preview mode should be enabled for an {@link ImageReader}.
5757
*/
5858
public boolean isPreviewModeEnabled() {
59+
if (!DISABLE_PREVIEW_PATCHING) {
60+
return false;
61+
}
5962
// A switch, instead of an abstract method, saves 3 subclasses.
6063
switch (this) {
6164
case DISABLED:
@@ -83,4 +86,13 @@ public boolean isPreviewModeEnabled() {
8386
throw new IllegalStateException("Invalid mode: " + this);
8487
}
8588
}
89+
90+
// Temporary system property to disable preview patching and enable the new preview mode
91+
// feature for testing/development. Once the preview mode feature is finished, the value
92+
// will be always 'true' and this code, and all related dead-code can be removed.
93+
private static final boolean DISABLE_PREVIEW_PATCHING_DEFAULT = false;
94+
private static final boolean DISABLE_PREVIEW_PATCHING = Boolean.parseBoolean(
95+
System.getProperty(
96+
"DISABLE_PREVIEW_PATCHING",
97+
Boolean.toString(DISABLE_PREVIEW_PATCHING_DEFAULT)));
8698
}

src/java.base/share/native/libjimage/imageFile.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -319,12 +319,7 @@ bool ImageFileReader::open() {
319319
!read_at((u1*)&_header, header_size, 0) ||
320320
_header.magic(_endian) != IMAGE_MAGIC ||
321321
_header.major_version(_endian) != MAJOR_VERSION ||
322-
// Temporarily, we allow either version (1.1 or 1.0) of the file to
323-
// be read so this code can be committed before image writing changes
324-
// for preview mode. Preview mode changes do not modify any structure,
325-
// so a 1.0 file will look like a jimage without any preview resources.
326-
// TODO: Restore equality check for MINOR_VERSION.
327-
_header.minor_version(_endian) > MINOR_VERSION) {
322+
_header.minor_version(_endian) != MINOR_VERSION) {
328323
close();
329324
return false;
330325
}

src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ private void extract(BasicImageReader reader, String name,
298298
parent.getAbsolutePath());
299299
}
300300

301-
if (!ImageResourcesTree.isTreeInfoResource(name)) {
301+
if (location.getType() == ImageLocation.LocationType.RESOURCE) {
302302
Files.write(resource.toPath(), bytes);
303303
}
304304
}
@@ -415,21 +415,18 @@ private void iterate(JImageAction jimageAction,
415415
continue;
416416
}
417417

418-
if (!ImageResourcesTree.isTreeInfoResource(name)) {
418+
ImageLocation location = reader.findLocation(name);
419+
if (location.getType() == ImageLocation.LocationType.RESOURCE) {
419420
if (moduleAction != null) {
420-
int offset = name.indexOf('/', 1);
421-
422-
String newModule = offset != -1 ?
423-
name.substring(1, offset) :
424-
"<unknown>";
425-
421+
String newModule = location.getModule();
422+
if (newModule.isEmpty()) {
423+
newModule = "<unknown>";
424+
}
426425
if (!oldModule.equals(newModule)) {
427426
moduleAction.apply(reader, oldModule, newModule);
428427
oldModule = newModule;
429428
}
430429
}
431-
432-
ImageLocation location = reader.findLocation(name);
433430
resourceAction.apply(reader, name, location);
434431
}
435432
}

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

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -36,21 +36,17 @@
3636
public final class BasicImageWriter {
3737
public static final String MODULES_IMAGE_NAME = "modules";
3838

39-
private ByteOrder byteOrder;
40-
private ImageStringsWriter strings;
39+
private final ByteOrder byteOrder;
40+
private final ImageStringsWriter strings;
4141
private int length;
4242
private int[] redirect;
4343
private ImageLocationWriter[] locations;
44-
private List<ImageLocationWriter> input;
45-
private ImageStream headerStream;
46-
private ImageStream redirectStream;
47-
private ImageStream locationOffsetStream;
48-
private ImageStream locationStream;
49-
private ImageStream allIndexStream;
50-
51-
public BasicImageWriter() {
52-
this(ByteOrder.nativeOrder());
53-
}
44+
private final List<ImageLocationWriter> input;
45+
private final ImageStream headerStream;
46+
private final ImageStream redirectStream;
47+
private final ImageStream locationOffsetStream;
48+
private final ImageStream locationStream;
49+
private final ImageStream allIndexStream;
5450

5551
public BasicImageWriter(ByteOrder byteOrder) {
5652
this.byteOrder = Objects.requireNonNull(byteOrder);
@@ -75,11 +71,15 @@ public String getString(int offset) {
7571
return strings.get(offset);
7672
}
7773

78-
public void addLocation(String fullname, long contentOffset,
79-
long compressedSize, long uncompressedSize) {
74+
public void addLocation(
75+
String fullname,
76+
long contentOffset,
77+
long compressedSize,
78+
long uncompressedSize,
79+
int previewFlags) {
8080
ImageLocationWriter location =
8181
ImageLocationWriter.newLocation(fullname, strings,
82-
contentOffset, compressedSize, uncompressedSize);
82+
contentOffset, compressedSize, uncompressedSize, previewFlags);
8383
input.add(location);
8484
length++;
8585
}
@@ -88,10 +88,6 @@ ImageLocationWriter[] getLocations() {
8888
return locations;
8989
}
9090

91-
int getLocationsCount() {
92-
return input.size();
93-
}
94-
9591
private void generatePerfectHash() {
9692
PerfectHashBuilder<ImageLocationWriter> builder =
9793
new PerfectHashBuilder<>(
@@ -174,16 +170,4 @@ public byte[] getBytes() {
174170

175171
return allIndexStream.toArray();
176172
}
177-
178-
ImageLocationWriter find(String key) {
179-
int index = redirect[ImageStringsReader.hashCode(key) % length];
180-
181-
if (index < 0) {
182-
index = -index - 1;
183-
} else {
184-
index = ImageStringsReader.hashCode(key, index) % length;
185-
}
186-
187-
return locations[index];
188-
}
189173
}

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

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,7 @@
4949
import java.util.stream.Collectors;
5050
import java.util.stream.Stream;
5151

52+
import jdk.internal.jimage.ImageLocation;
5253
import jdk.tools.jlink.internal.Archive.Entry;
5354
import jdk.tools.jlink.internal.Archive.Entry.EntryType;
5455
import jdk.tools.jlink.internal.JRTArchive.ResourceFileEntry;
@@ -227,31 +228,8 @@ private static ResourcePool generateJImage(ResourcePoolManager allContent,
227228
DataOutputStream out,
228229
boolean generateRuntimeImage
229230
) throws IOException {
230-
ResourcePool resultResources;
231-
try {
232-
resultResources = pluginSupport.visitResources(allContent);
233-
if (generateRuntimeImage) {
234-
// Keep track of non-modules resources for linking from a run-time image
235-
resultResources = addNonClassResourcesTrackFiles(resultResources,
236-
writer);
237-
// Generate the diff between the input resources from packaged
238-
// modules in 'allContent' to the plugin- or otherwise
239-
// generated-content in 'resultResources'
240-
resultResources = addResourceDiffFiles(allContent.resourcePool(),
241-
resultResources,
242-
writer);
243-
}
244-
} catch (PluginException pe) {
245-
if (JlinkTask.DEBUG) {
246-
pe.printStackTrace();
247-
}
248-
throw pe;
249-
} catch (Exception ex) {
250-
if (JlinkTask.DEBUG) {
251-
ex.printStackTrace();
252-
}
253-
throw new IOException(ex);
254-
}
231+
ResourcePool resultResources =
232+
getResourcePool(allContent, writer, pluginSupport, generateRuntimeImage);
255233
Set<String> duplicates = new HashSet<>();
256234
long[] offset = new long[1];
257235

@@ -282,8 +260,10 @@ private static ResourcePool generateJImage(ResourcePoolManager allContent,
282260
offset[0] += onFileSize;
283261
return;
284262
}
263+
int locFlags = ImageLocation.getFlags(
264+
res.path(), p -> resultResources.findEntry(p).isPresent());
285265
duplicates.add(path);
286-
writer.addLocation(path, offset[0], compressedSize, uncompressedSize);
266+
writer.addLocation(path, offset[0], compressedSize, uncompressedSize, locFlags);
287267
paths.add(path);
288268
offset[0] += onFileSize;
289269
}
@@ -307,6 +287,40 @@ private static ResourcePool generateJImage(ResourcePoolManager allContent,
307287
return resultResources;
308288
}
309289

290+
private static ResourcePool getResourcePool(
291+
ResourcePoolManager allContent,
292+
BasicImageWriter writer,
293+
ImagePluginStack pluginSupport,
294+
boolean generateRuntimeImage)
295+
throws IOException {
296+
ResourcePool resultResources;
297+
try {
298+
resultResources = pluginSupport.visitResources(allContent);
299+
if (generateRuntimeImage) {
300+
// Keep track of non-modules resources for linking from a run-time image
301+
resultResources = addNonClassResourcesTrackFiles(resultResources,
302+
writer);
303+
// Generate the diff between the input resources from packaged
304+
// modules in 'allContent' to the plugin- or otherwise
305+
// generated-content in 'resultResources'
306+
resultResources = addResourceDiffFiles(allContent.resourcePool(),
307+
resultResources,
308+
writer);
309+
}
310+
} catch (PluginException pe) {
311+
if (JlinkTask.DEBUG) {
312+
pe.printStackTrace();
313+
}
314+
throw pe;
315+
} catch (Exception ex) {
316+
if (JlinkTask.DEBUG) {
317+
ex.printStackTrace();
318+
}
319+
throw new IOException(ex);
320+
}
321+
return resultResources;
322+
}
323+
310324
/**
311325
* Support for creating a runtime suitable for linking from the run-time
312326
* image.

0 commit comments

Comments
 (0)