Skip to content

Commit 5d2101c

Browse files
Resync after dependent PR submit.
* Removing package root flag based on feedback. * Changing existing package flags during writing to match altered flag values. * Feedback changes, and fixing some comments. * Renaming slightly confusing "testEncoder" method. * Fixing unit tests to use new constructor. * Word smithing flags definitions. * Add workaround until new image writing code is in * Clarifying flag docs for /packages/xxx case * Java ImageReader changes for preview mode
1 parent 7257a07 commit 5d2101c

File tree

19 files changed

+1293
-261
lines changed

19 files changed

+1293
-261
lines changed

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2016, 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
@@ -30,6 +30,23 @@
3030
import java.util.Objects;
3131

3232
/**
33+
* Defines the header and version information for jimage files.
34+
*
35+
* <p>Version number changes must be synced in a single change across all code
36+
* which reads/writes jimage files, and code which tries to open a jimage file
37+
* with an unexpected version should fail.
38+
*
39+
* <p>Known jimage file code which needs updating on version change:
40+
* <ul>
41+
* <li>src/java.base/share/native/libjimage/imageFile.hpp
42+
* </ul>
43+
*
44+
* <p>Version history:
45+
* <ul>
46+
* <li>{@code 1.0}: Original version.
47+
* <li>{@code 1.1}: Support preview mode with new flags.
48+
* </ul>
49+
*
3350
* @implNote This class needs to maintain JDK 8 source compatibility.
3451
*
3552
* It is used internally in the JDK to implement jimage/jrtfs access,
@@ -39,7 +56,7 @@
3956
public final class ImageHeader {
4057
public static final int MAGIC = 0xCAFEDADA;
4158
public static final int MAJOR_VERSION = 1;
42-
public static final int MINOR_VERSION = 0;
59+
public static final int MINOR_VERSION = 1;
4360
private static final int HEADER_SLOTS = 7;
4461

4562
private final int magic;

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

Lines changed: 163 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2021, 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
@@ -27,6 +27,7 @@
2727

2828
import java.nio.ByteBuffer;
2929
import java.util.Objects;
30+
import java.util.function.Predicate;
3031

3132
/**
3233
* @implNote This class needs to maintain JDK 8 source compatibility.
@@ -44,7 +45,126 @@ public class ImageLocation {
4445
public static final int ATTRIBUTE_OFFSET = 5;
4546
public static final int ATTRIBUTE_COMPRESSED = 6;
4647
public static final int ATTRIBUTE_UNCOMPRESSED = 7;
47-
public static final int ATTRIBUTE_COUNT = 8;
48+
public static final int ATTRIBUTE_PREVIEW_FLAGS = 8;
49+
public static final int ATTRIBUTE_COUNT = 9;
50+
51+
// Flag masks for the ATTRIBUTE_PREVIEW_FLAGS attribute. Defined so
52+
// that zero is the overwhelmingly common case for normal resources.
53+
54+
/**
55+
* Indicates that a non-preview location is associated with preview
56+
* resources.
57+
*
58+
* <p>This can apply to both resources and directories in the
59+
* {@code /modules/xxx/...} namespace, as well as {@code /packages/xxx}
60+
* directories.
61+
*
62+
* <p>For {@code /packages/xxx} directories, it indicates that the package
63+
* has preview resources in one of the modules in which it exists.
64+
*/
65+
private static final int FLAGS_HAS_PREVIEW_VERSION = 0x1;
66+
/**
67+
* Set on all locations in the {@code /modules/xxx/META-INF/preview/...}
68+
* namespace.
69+
*
70+
* <p>This flag is mutually exclusive with {@link #FLAGS_HAS_PREVIEW_VERSION}.
71+
*/
72+
private static final int FLAGS_IS_PREVIEW_VERSION = 0x2;
73+
/**
74+
* Indicates that a location only exists due to preview resources.
75+
*
76+
* <p>This can apply to both resources and directories in the
77+
* {@code /modules/xxx/...} namespace, as well as {@code /packages/xxx}
78+
* directories.
79+
*
80+
* <p>For {@code /packages/xxx} directories it indicates that, for every
81+
* module in which the package exists, it is preview only.
82+
*
83+
* <p>This flag is mutually exclusive with {@link #FLAGS_HAS_PREVIEW_VERSION}
84+
* and need not imply that {@link #FLAGS_IS_PREVIEW_VERSION} is set (i.e.
85+
* for {@code /packages/xxx} directories).
86+
*/
87+
private static final int FLAGS_IS_PREVIEW_ONLY = 0x4;
88+
89+
// Also used in ImageReader.
90+
static final String MODULES_PREFIX = "/modules";
91+
static final String PACKAGES_PREFIX = "/packages";
92+
static final String PREVIEW_INFIX = "/META-INF/preview";
93+
94+
/**
95+
* Helper function to calculate preview flags (ATTRIBUTE_PREVIEW_FLAGS).
96+
*
97+
* <p>Since preview flags are calculated separately for resource nodes and
98+
* directory nodes (in two quite different places) it's useful to have a
99+
* common helper.
100+
*
101+
* <p>Based on the entry name, the flags are:
102+
* <ul>
103+
* <li>{@code "[/modules]/<module>/<path>"} normal resource or directory:<br>
104+
* Zero, or {@code FLAGS_HAS_PREVIEW_VERSION} if a preview entry exists.
105+
* <li>{@code "[/modules]/<module>/META-INF/preview/<path>"} preview
106+
* resource or directory:<br>
107+
* {@code FLAGS_IS_PREVIEW_VERSION}, and additionally {@code
108+
* FLAGS_IS_PREVIEW_ONLY} if no normal version of the resource exists.
109+
* <li>In all other cases, returned flags are zero (note that {@code
110+
* "/packages/xxx"} entries may have flags, but these are calculated
111+
* elsewhere).
112+
* </ul>
113+
*
114+
* @param name the jimage name of the resource or directory.
115+
* @param hasEntry a predicate for jimage names returning whether an entry
116+
* is present.
117+
* @return flags for the ATTRIBUTE_PREVIEW_FLAGS attribute.
118+
*/
119+
public static int getFlags(String name, Predicate<String> hasEntry) {
120+
if (name.startsWith(PACKAGES_PREFIX + "/")) {
121+
throw new IllegalArgumentException(
122+
"Package sub-directory flags handled separately: " + name);
123+
}
124+
// Find suffix for either '/modules/xxx/suffix' or '/xxx/suffix' paths.
125+
int idx = name.startsWith(MODULES_PREFIX + "/") ? MODULES_PREFIX.length() + 1 : 1;
126+
int suffixStart = name.indexOf('/', idx);
127+
if (suffixStart == -1) {
128+
// No flags for '[/modules]/xxx' paths (esp. '/modules', '/packages').
129+
// '/packages/xxx' entries have flags, but not calculated here.
130+
return 0;
131+
}
132+
// Prefix is either '/modules/xxx' or '/xxx', and suffix starts with '/'.
133+
String prefix = name.substring(0, suffixStart);
134+
String suffix = name.substring(suffixStart);
135+
if (suffix.startsWith(PREVIEW_INFIX + "/")) {
136+
// Preview resources/directories.
137+
String nonPreviewName = prefix + suffix.substring(PREVIEW_INFIX.length());
138+
return FLAGS_IS_PREVIEW_VERSION
139+
| (hasEntry.test(nonPreviewName) ? 0 : FLAGS_IS_PREVIEW_ONLY);
140+
} else if (!suffix.startsWith("/META-INF/")) {
141+
// Non-preview resources/directories.
142+
String previewName = prefix + PREVIEW_INFIX + suffix;
143+
return hasEntry.test(previewName) ? FLAGS_HAS_PREVIEW_VERSION : 0;
144+
} else {
145+
// Suffix is '/META-INF/xxx' and no preview version is even possible.
146+
return 0;
147+
}
148+
}
149+
150+
/**
151+
* Tests a non-preview image location's flags to see if it has preview
152+
* content associated with it.
153+
*/
154+
public static boolean hasPreviewVersion(int flags) {
155+
return (flags & FLAGS_HAS_PREVIEW_VERSION) != 0;
156+
}
157+
158+
/**
159+
* Tests an image location's flags to see if it only exists in preview mode.
160+
*/
161+
public static boolean isPreviewOnly(int flags) {
162+
return (flags & FLAGS_IS_PREVIEW_ONLY) != 0;
163+
}
164+
165+
public enum LocationType {
166+
RESOURCE, MODULES_ROOT, MODULES_DIR, PACKAGES_ROOT, PACKAGES_DIR;
167+
}
48168

49169
protected final long[] attributes;
50170

@@ -285,6 +405,10 @@ public int getExtensionOffset() {
285405
return (int)getAttribute(ATTRIBUTE_EXTENSION);
286406
}
287407

408+
public int getFlags() {
409+
return (int) getAttribute(ATTRIBUTE_PREVIEW_FLAGS);
410+
}
411+
288412
public String getFullName() {
289413
return getFullName(false);
290414
}
@@ -294,7 +418,7 @@ public String getFullName(boolean modulesPrefix) {
294418

295419
if (getModuleOffset() != 0) {
296420
if (modulesPrefix) {
297-
builder.append("/modules");
421+
builder.append(MODULES_PREFIX);
298422
}
299423

300424
builder.append('/');
@@ -317,36 +441,6 @@ public String getFullName(boolean modulesPrefix) {
317441
return builder.toString();
318442
}
319443

320-
String buildName(boolean includeModule, boolean includeParent,
321-
boolean includeName) {
322-
StringBuilder builder = new StringBuilder();
323-
324-
if (includeModule && getModuleOffset() != 0) {
325-
builder.append("/modules/");
326-
builder.append(getModule());
327-
}
328-
329-
if (includeParent && getParentOffset() != 0) {
330-
builder.append('/');
331-
builder.append(getParent());
332-
}
333-
334-
if (includeName) {
335-
if (includeModule || includeParent) {
336-
builder.append('/');
337-
}
338-
339-
builder.append(getBase());
340-
341-
if (getExtensionOffset() != 0) {
342-
builder.append('.');
343-
builder.append(getExtension());
344-
}
345-
}
346-
347-
return builder.toString();
348-
}
349-
350444
public long getContentOffset() {
351445
return getAttribute(ATTRIBUTE_OFFSET);
352446
}
@@ -359,6 +453,42 @@ public long getUncompressedSize() {
359453
return getAttribute(ATTRIBUTE_UNCOMPRESSED);
360454
}
361455

456+
// Fast (zero allocation) type determination for locations.
457+
public LocationType getType() {
458+
switch (getModuleOffset()) {
459+
case ImageStrings.MODULES_STRING_OFFSET:
460+
// Locations in /modules/... namespace are directory entries.
461+
return LocationType.MODULES_DIR;
462+
case ImageStrings.PACKAGES_STRING_OFFSET:
463+
// Locations in /packages/... namespace are always 2-level
464+
// "/packages/xxx" directories.
465+
return LocationType.PACKAGES_DIR;
466+
case ImageStrings.EMPTY_STRING_OFFSET:
467+
// Only 2 choices, either the "/modules" or "/packages" root.
468+
assert isRootDir() : "Invalid root directory: " + getFullName();
469+
return getBase().charAt(1) == 'p'
470+
? LocationType.PACKAGES_ROOT
471+
: LocationType.MODULES_ROOT;
472+
default:
473+
// Anything else is /<module>/<path> and references a resource.
474+
return LocationType.RESOURCE;
475+
}
476+
}
477+
478+
private boolean isRootDir() {
479+
if (getModuleOffset() == 0 && getParentOffset() == 0) {
480+
String name = getFullName();
481+
return name.equals(MODULES_PREFIX) || name.equals(PACKAGES_PREFIX);
482+
}
483+
return false;
484+
}
485+
486+
@Override
487+
public String toString() {
488+
// Cannot use String.format() (too early in startup for locale code).
489+
return "ImageLocation[name='" + getFullName() + "', type=" + getType() + ", flags=" + getFlags() + "]";
490+
}
491+
362492
static ImageLocation readFrom(BasicImageReader reader, int offset) {
363493
Objects.requireNonNull(reader);
364494
long[] attributes = reader.getAttributes(offset);

0 commit comments

Comments
 (0)