Skip to content

Commit fe0bd75

Browse files
Adjusting tests and fixing loose ends from merge.
1 parent ece6a1a commit fe0bd75

File tree

4 files changed

+190
-117
lines changed

4 files changed

+190
-117
lines changed

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

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import java.util.TreeMap;
4747
import java.util.function.Function;
4848
import java.util.function.Supplier;
49-
import java.util.stream.Collectors;
5049
import java.util.stream.Stream;
5150

5251
import static jdk.internal.jimage.ImageLocation.FLAGS_HAS_PREVIEW_VERSION;
@@ -337,15 +336,12 @@ private ArrayList<Node> processPackagesDirectory(boolean previewMode) {
337336
if (!previewMode || (flags & FLAGS_HAS_PREVIEW_VERSION) == 0) {
338337
continue;
339338
}
340-
// Only do this in preview mode for the small set of packages
341-
// with preview versions (these offset buffers should be small).
342-
List<ModuleReference> modRefs =
343-
ModuleReference.read(getOffsetBuffer(pkgDir), true, this::getString);
344-
List<String> previewModuleNames = modRefs.stream()
345-
.filter(ModuleReference::hasPreviewVersion)
346-
.map(ModuleReference::name)
347-
.collect(Collectors.toList());
348-
previewPackagesToModules.put(pkgDir.getBase().replace('.', '/'), previewModuleNames);
339+
// Only do this in preview mode for the small set of packages with
340+
// preview versions (the number of preview entries should be small).
341+
List<String> moduleNames = new ArrayList<>();
342+
ModuleReference.readNameOffsets(getOffsetBuffer(pkgDir), /*normal*/ false, /*preview*/ true)
343+
.forEachRemaining(n -> moduleNames.add(getString(n)));
344+
previewPackagesToModules.put(pkgDir.getBase().replace('.', '/'), moduleNames);
349345
}
350346
// Reverse sorted map means child directories are processed first.
351347
previewPackagesToModules.forEach((pkgPath, modules) ->
@@ -489,7 +485,7 @@ Node findResourceNode(String moduleName, String resourcePath) {
489485
if (moduleName.indexOf('/') >= 0) {
490486
throw new IllegalArgumentException("invalid module name: " + moduleName);
491487
}
492-
String nodeName = MODULES_ROOT + "/" + moduleName + "/" + resourcePath;
488+
String nodeName = MODULES_PREFIX + "/" + moduleName + "/" + resourcePath;
493489
// Synchronize as tightly as possible to reduce locking contention.
494490
synchronized (this) {
495491
Node node = nodes.get(nodeName);
@@ -523,7 +519,7 @@ boolean containsResource(String moduleName, String resourcePath) {
523519
// In preview mode, preview-only resources are eagerly added to the
524520
// cache, so we must check that first.
525521
if (previewMode) {
526-
String nodeName = MODULES_ROOT + "/" + moduleName + "/" + resourcePath;
522+
String nodeName = MODULES_PREFIX + "/" + moduleName + "/" + resourcePath;
527523
// Synchronize as tightly as possible to reduce locking contention.
528524
synchronized (this) {
529525
Node node = nodes.get(nodeName);
@@ -612,8 +608,10 @@ private Node buildAndCacheLinkNode(String name) {
612608
// If no parent exists here, the name cannot be valid.
613609
Directory parent = (Directory) nodes.get(dirName);
614610
if (parent != null) {
615-
// This caches all child links of the parent directory.
616-
completePackageSubdirectory(parent, findLocation(dirName));
611+
if (!parent.isCompleted()) {
612+
// This caches all child links of the parent directory.
613+
completePackageSubdirectory(parent, findLocation(dirName));
614+
}
617615
return nodes.get(name);
618616
}
619617
}
@@ -665,25 +663,22 @@ private Directory completeModuleDirectory(Directory dir, ImageLocation loc) {
665663
return dir;
666664
}
667665

668-
/**
669-
* Completes a package directory by setting the list of child nodes.
670-
*
671-
* <p>The given directory can be the top level {@code /packages} directory,
672-
* so it is NOT safe to use {@code isPackagesSubdirectory(loc)} here.
673-
*/
666+
/** Completes a package directory by setting the list of child nodes. */
674667
private void completePackageSubdirectory(Directory dir, ImageLocation loc) {
675668
assert dir.getName().equals(loc.getFullName()) : "Mismatched location for directory: " + dir;
676-
LocationType type = loc.getType();
677-
assert type == PACKAGES_DIR : "Invalid location type: " + loc;
678-
679-
List<ModuleReference> modRefs =
680-
ModuleReference.read(getOffsetBuffer(loc), previewMode, this::getString);
681-
List<Node> children = modRefs.stream()
682-
.map(mod -> ensureCached(
683-
newLinkNode(
684-
dir.getName() + "/" + mod.name(),
685-
MODULES_PREFIX + "/" + mod.name())))
686-
.collect(Collectors.toList());
669+
assert !dir.isCompleted() : "Directory already completed: " + dir;
670+
assert loc.getType() == PACKAGES_DIR : "Invalid location type: " + loc.getType();
671+
672+
// In non-preview mode we might skip a very small number of preview-only
673+
// entries, but it's not worth "right-sizing" the array for that.
674+
IntBuffer offsets = getOffsetBuffer(loc);
675+
List<Node> children = new ArrayList<>(offsets.capacity() / 2);
676+
ModuleReference.readNameOffsets(offsets, /*normal*/ true, previewMode)
677+
.forEachRemaining(n -> {
678+
String modName = getString(n);
679+
Node link = newLinkNode(dir.getName() + "/" + modName, MODULES_PREFIX + "/" + modName);
680+
children.add(ensureCached(link));
681+
});
687682
// If the parent directory exists, there must be at least one child node.
688683
assert !children.isEmpty() : "Invalid empty package directory: " + dir;
689684
dir.setChildren(children);

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

Lines changed: 67 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,16 @@
2828
import java.nio.IntBuffer;
2929
import java.util.ArrayList;
3030
import java.util.Comparator;
31+
import java.util.Iterator;
3132
import java.util.List;
33+
import java.util.NoSuchElementException;
3234
import java.util.Objects;
3335
import java.util.function.Function;
36+
import java.util.function.IntFunction;
37+
import java.util.function.IntPredicate;
38+
import java.util.function.Predicate;
39+
import java.util.function.Supplier;
40+
import java.util.stream.Stream;
3441

3542
/**
3643
* Represents the module entries stored in the buffer of {@code "/packages/xxx"}
@@ -59,11 +66,12 @@ public final class ModuleReference implements Comparable<ModuleReference> {
5966
private static final int FLAGS_HAS_PREVIEW_VERSION = 0x4;
6067

6168
/**
62-
* References are ordered so the unique module in which the associated
63-
* package is non-empty comes first.
69+
* References are ordered with preview versions first which permits early
70+
* exit when processing preview entries (it's reversed because the default
71+
* order for a boolean is {@code false < true}).
6472
*/
65-
private static final Comparator<ModuleReference> NON_EMPTY_FIRST =
66-
Comparator.comparing(ModuleReference::isEmpty)
73+
private static final Comparator<ModuleReference> PREVIEW_FIRST =
74+
Comparator.comparing(ModuleReference::hasPreviewVersion).reversed()
6775
.thenComparing(ModuleReference::name);
6876

6977
/** Creates a reference for an empty package (one without content in). */
@@ -109,8 +117,8 @@ public String name() {
109117
* <p>An invariant of the module system is that while a package may exist
110118
* under many modules, it is only non-empty in one.
111119
*/
112-
public boolean isEmpty() {
113-
return ((flags & FLAGS_HAS_CONTENT) == 0);
120+
public boolean hasContent() {
121+
return ((flags & FLAGS_HAS_CONTENT) != 0);
114122
}
115123

116124
/**
@@ -132,7 +140,7 @@ private static boolean hasNormalVersion(int flags) {
132140

133141
@Override
134142
public int compareTo(ModuleReference rhs) {
135-
return NON_EMPTY_FIRST.compare(this, rhs);
143+
return PREVIEW_FIRST.compare(this, rhs);
136144
}
137145

138146
@Override
@@ -155,40 +163,62 @@ public int hashCode() {
155163
}
156164

157165
/**
158-
* Reads the content buffer of a package subdirectory to construct a list
159-
* of module references.
166+
* Reads the content buffer of a package subdirectory to return a sequence
167+
* of module name offsets in the jimage.
160168
*
161169
* @param buffer the content buffer of an {@link ImageLocation} with type
162170
* {@link ImageLocation.LocationType#PACKAGES_DIR PACKAGES_DIR}.
163-
* @param previewMode if {@code true} include all reference; otherwise skip
164-
* preview-only ones.
165-
* @param nameDecoder decoder for module names.
166-
* @return the list of module references for the source package subdirectory.
171+
* @param includeNormal whether to include name offsets for modules present
172+
* in normal (non-preview) mode.
173+
* @param includePreview whether to include name offsets for modules present
174+
* in preview mode.
175+
* @return an iterator of module name offsets.
167176
*/
168-
public static List<ModuleReference> read(
169-
IntBuffer buffer, boolean previewMode, Function<Integer, String> nameDecoder) {
177+
public static Iterator<Integer> readNameOffsets(
178+
IntBuffer buffer, boolean includeNormal, boolean includePreview) {
170179
int bufferSize = buffer.capacity();
171180
if (bufferSize == 0 || (bufferSize & 0x1) != 0) {
172181
throw new IllegalArgumentException("Invalid buffer size");
173182
}
174-
// In non-preview mode we might skip a very small number of preview-only
175-
// entries, but it's not worth "right-sizing" the array for that.
176-
List<ModuleReference> refs = new ArrayList<>(bufferSize / 2);
177-
for (int i = 0; i < bufferSize; i += 2) {
178-
int packageFlags = buffer.get(i);
179-
if ((packageFlags & (FLAGS_HAS_NORMAL_VERSION | FLAGS_HAS_PREVIEW_VERSION)) == 0) {
180-
throw new IllegalArgumentException("Invalid package flags");
183+
int testFlags = (includeNormal ? FLAGS_HAS_NORMAL_VERSION : 0)
184+
+ (includePreview ? FLAGS_HAS_PREVIEW_VERSION : 0);
185+
if (testFlags == 0) {
186+
throw new IllegalArgumentException("Invalid flags");
187+
}
188+
189+
return new Iterator<Integer>() {
190+
private int idx = nextIdx(0);
191+
192+
int nextIdx(int idx) {
193+
for (; idx < bufferSize; idx += 2) {
194+
// If any of the test flags are set, include this entry.
195+
if ((buffer.get(idx) & testFlags) != 0) {
196+
return idx;
197+
} else if (!includeNormal) {
198+
// Preview entries are first in the offset buffer, so we
199+
// can exit early (by returning the end index) if we are
200+
// only iterating preview entries, and have run out.
201+
break;
202+
}
203+
}
204+
return bufferSize;
205+
}
206+
207+
@Override
208+
public boolean hasNext() {
209+
return idx < bufferSize;
181210
}
182-
if (previewMode || hasNormalVersion(packageFlags)) {
183-
ModuleReference modRef = new ModuleReference(nameDecoder.apply(buffer.get(i + 1)), packageFlags);
184-
// Validate the unique non-empty reference (if it exists) is first.
185-
if (!refs.isEmpty() && !modRef.isEmpty()) {
186-
throw new IllegalArgumentException("Only first reference can have content: " + modRef);
211+
212+
@Override
213+
public Integer next() {
214+
if (idx < bufferSize) {
215+
int nameOffset = buffer.get(idx + 1);
216+
idx = nextIdx(idx + 2);
217+
return nameOffset;
187218
}
188-
refs.add(modRef);
219+
throw new NoSuchElementException();
189220
}
190-
}
191-
return refs;
221+
};
192222
}
193223

194224
/**
@@ -206,13 +236,16 @@ public static void write(
206236
if (buffer.capacity() != (2 * refs.size())) {
207237
throw new IllegalArgumentException("Incorrect output buffer capacity");
208238
}
209-
for (int i = 0; i < refs.size(); i++) {
210-
ModuleReference modRef = refs.get(i);
211-
if (i > 0 && !modRef.isEmpty()) {
212-
throw new IllegalArgumentException("Only first reference can be non-empty: " + modRef);
239+
int withContent = 0;
240+
for (ModuleReference modRef : refs) {
241+
if (modRef.hasContent()) {
242+
withContent++;
213243
}
214244
buffer.put(modRef.flags);
215245
buffer.put(nameEncoder.apply(modRef.name));
216246
}
247+
if (withContent > 1) {
248+
throw new IllegalArgumentException("At most one reference can have content: " + refs);
249+
}
217250
}
218251
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,11 @@ List<ModuleReference> getModuleReferences() {
174174
}
175175

176176
private int validate() {
177-
// If there's a module for which this package has content, it should be first and unique.
178177
if (moduleReferences.isEmpty()) {
179178
throw new IllegalStateException("Package must be associated with modules: " + getName());
180179
}
181-
if (moduleReferences.stream().skip(1).anyMatch(ref -> !ref.isEmpty())) {
182-
throw new IllegalStateException("Multiple modules contain non-empty package: " + getName() + "\n\t" + moduleReferences.stream().map(Objects::toString).collect(joining("\n\t")));
180+
if (moduleReferences.stream().filter(ModuleReference::hasContent).count() > 1) {
181+
throw new IllegalStateException("Multiple modules contain non-empty package: " + getName());
183182
}
184183
// Calculate the package's ImageLocation flags.
185184
boolean hasPreviewVersion =

0 commit comments

Comments
 (0)