1 /*
2 * Copyright (c) 2014, 2021, 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
26 package jdk.internal.jimage;
27
28 import java.nio.ByteBuffer;
29 import java.util.Objects;
30
31 /**
32 * @implNote This class needs to maintain JDK 8 source compatibility.
33 *
34 * It is used internally in the JDK to implement jimage/jrtfs access,
35 * but also compiled and delivered as part of the jrtfs.jar to support access
36 * to the jimage file provided by the shipped JDK by tools running on JDK 8.
37 */
38 public class ImageLocation {
39 public static final int ATTRIBUTE_END = 0;
40 public static final int ATTRIBUTE_MODULE = 1;
41 public static final int ATTRIBUTE_PARENT = 2;
42 public static final int ATTRIBUTE_BASE = 3;
43 public static final int ATTRIBUTE_EXTENSION = 4;
44 public static final int ATTRIBUTE_OFFSET = 5;
45 public static final int ATTRIBUTE_COMPRESSED = 6;
46 public static final int ATTRIBUTE_UNCOMPRESSED = 7;
47 public static final int ATTRIBUTE_COUNT = 8;
48
49 protected final long[] attributes;
50
51 protected final ImageStrings strings;
52
53 public ImageLocation(long[] attributes, ImageStrings strings) {
54 this.attributes = Objects.requireNonNull(attributes);
55 this.strings = Objects.requireNonNull(strings);
56 }
57
58 ImageStrings getStrings() {
59 return strings;
60 }
61
62 static long[] decompress(ByteBuffer bytes, int offset) {
63 Objects.requireNonNull(bytes);
64 long[] attributes = new long[ATTRIBUTE_COUNT];
65
66 int limit = bytes.limit();
67 while (offset < limit) {
268 public int getBaseOffset() {
269 return (int)getAttribute(ATTRIBUTE_BASE);
270 }
271
272 public String getParent() {
273 return getAttributeString(ATTRIBUTE_PARENT);
274 }
275
276 public int getParentOffset() {
277 return (int)getAttribute(ATTRIBUTE_PARENT);
278 }
279
280 public String getExtension() {
281 return getAttributeString(ATTRIBUTE_EXTENSION);
282 }
283
284 public int getExtensionOffset() {
285 return (int)getAttribute(ATTRIBUTE_EXTENSION);
286 }
287
288 public String getFullName() {
289 return getFullName(false);
290 }
291
292 public String getFullName(boolean modulesPrefix) {
293 StringBuilder builder = new StringBuilder();
294
295 if (getModuleOffset() != 0) {
296 if (modulesPrefix) {
297 builder.append("/modules");
298 }
299
300 builder.append('/');
301 builder.append(getModule());
302 builder.append('/');
303 }
304
305 if (getParentOffset() != 0) {
306 builder.append(getParent());
307 builder.append('/');
308 }
309
310 builder.append(getBase());
311
312 if (getExtensionOffset() != 0) {
313 builder.append('.');
314 builder.append(getExtension());
315 }
316
317 return builder.toString();
318 }
319
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
350 public long getContentOffset() {
351 return getAttribute(ATTRIBUTE_OFFSET);
352 }
353
354 public long getCompressedSize() {
355 return getAttribute(ATTRIBUTE_COMPRESSED);
356 }
357
358 public long getUncompressedSize() {
359 return getAttribute(ATTRIBUTE_UNCOMPRESSED);
360 }
361
362 static ImageLocation readFrom(BasicImageReader reader, int offset) {
363 Objects.requireNonNull(reader);
364 long[] attributes = reader.getAttributes(offset);
365 ImageStringsReader strings = reader.getStrings();
366
367 return new ImageLocation(attributes, strings);
368 }
369 }
|
1 /*
2 * Copyright (c) 2014, 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
26 package jdk.internal.jimage;
27
28 import java.nio.ByteBuffer;
29 import java.util.List;
30 import java.util.Objects;
31 import java.util.function.Predicate;
32
33 /**
34 * @implNote This class needs to maintain JDK 8 source compatibility.
35 *
36 * It is used internally in the JDK to implement jimage/jrtfs access,
37 * but also compiled and delivered as part of the jrtfs.jar to support access
38 * to the jimage file provided by the shipped JDK by tools running on JDK 8.
39 */
40 public class ImageLocation {
41 // Also defined in src/java.base/share/native/libjimage/imageFile.hpp
42
43 /** End of attribute stream marker. */
44 public static final int ATTRIBUTE_END = 0;
45 /** String table offset of module name. */
46 public static final int ATTRIBUTE_MODULE = 1;
47 /** String table offset of resource path parent. */
48 public static final int ATTRIBUTE_PARENT = 2;
49 /** String table offset of resource path base. */
50 public static final int ATTRIBUTE_BASE = 3;
51 /** String table offset of resource path extension. */
52 public static final int ATTRIBUTE_EXTENSION = 4;
53 /** Container byte offset of resource. */
54 public static final int ATTRIBUTE_OFFSET = 5;
55 /** In-image byte size of the compressed resource. */
56 public static final int ATTRIBUTE_COMPRESSED = 6;
57 /** In-memory byte size of the uncompressed resource. */
58 public static final int ATTRIBUTE_UNCOMPRESSED = 7;
59 /** Flags relating to preview mode resources. */
60 public static final int ATTRIBUTE_PREVIEW_FLAGS = 8;
61 /** Number of attribute kinds. */
62 public static final int ATTRIBUTE_COUNT = 9;
63
64 // Flag masks for the ATTRIBUTE_PREVIEW_FLAGS attribute. Defined so
65 // that zero is the overwhelmingly common case for normal resources.
66
67 /**
68 * Indicates that a non-preview location is associated with preview
69 * resources.
70 *
71 * <p>This can apply to both resources and directories in the
72 * {@code /modules/xxx/...} namespace, as well as {@code /packages/xxx}
73 * directories.
74 *
75 * <p>For {@code /packages/xxx} directories, it indicates that the package
76 * has preview resources in one of the modules in which it exists.
77 */
78 private static final int FLAGS_HAS_PREVIEW_VERSION = 0x1;
79 /**
80 * Set on all locations in the {@code /modules/xxx/META-INF/preview/...}
81 * namespace.
82 *
83 * <p>This flag is mutually exclusive with {@link #FLAGS_HAS_PREVIEW_VERSION}.
84 */
85 private static final int FLAGS_IS_PREVIEW_VERSION = 0x2;
86 /**
87 * Indicates that a location only exists due to preview resources.
88 *
89 * <p>This can apply to both resources and directories in the
90 * {@code /modules/xxx/...} namespace, as well as {@code /packages/xxx}
91 * directories.
92 *
93 * <p>For {@code /packages/xxx} directories it indicates that, for every
94 * module in which the package exists, it is preview only.
95 *
96 * <p>This flag is mutually exclusive with {@link #FLAGS_HAS_PREVIEW_VERSION}
97 * and need not imply that {@link #FLAGS_IS_PREVIEW_VERSION} is set (i.e.
98 * for {@code /packages/xxx} directories).
99 */
100 private static final int FLAGS_IS_PREVIEW_ONLY = 0x4;
101
102 // Also used in ImageReader.
103 static final String MODULES_PREFIX = "/modules";
104 static final String PACKAGES_PREFIX = "/packages";
105 static final String PREVIEW_INFIX = "/META-INF/preview";
106
107 /**
108 * Helper function to calculate preview flags (ATTRIBUTE_PREVIEW_FLAGS).
109 *
110 * <p>Since preview flags are calculated separately for resource nodes and
111 * directory nodes (in two quite different places) it's useful to have a
112 * common helper.
113 *
114 * <p>Based on the entry name, the flags are:
115 * <ul>
116 * <li>{@code "[/modules]/<module>/<path>"} normal resource or directory:<br>
117 * Zero, or {@code FLAGS_HAS_PREVIEW_VERSION} if a preview entry exists.
118 * <li>{@code "[/modules]/<module>/META-INF/preview/<path>"} preview
119 * resource or directory:<br>
120 * {@code FLAGS_IS_PREVIEW_VERSION}, and additionally {@code
121 * FLAGS_IS_PREVIEW_ONLY} if no normal version of the resource exists.
122 * <li>In all other cases, returned flags are zero (note that {@code
123 * "/packages/xxx"} entries may have flags, but these are calculated
124 * elsewhere).
125 * </ul>
126 *
127 * @param name the jimage name of the resource or directory.
128 * @param hasEntry a predicate for jimage names returning whether an entry
129 * is present.
130 * @return flags for the ATTRIBUTE_PREVIEW_FLAGS attribute.
131 */
132 public static int getFlags(String name, Predicate<String> hasEntry) {
133 if (name.startsWith(PACKAGES_PREFIX + "/")) {
134 throw new IllegalArgumentException(
135 "Package sub-directory flags handled separately: " + name);
136 }
137 // Find suffix for either '/modules/xxx/suffix' or '/xxx/suffix' paths.
138 int idx = name.startsWith(MODULES_PREFIX + "/") ? MODULES_PREFIX.length() + 1 : 1;
139 int suffixStart = name.indexOf('/', idx);
140 if (suffixStart == -1) {
141 // No flags for '[/modules]/xxx' paths (esp. '/modules', '/packages').
142 // '/packages/xxx' entries have flags, but not calculated here.
143 return 0;
144 }
145 // Prefix is either '/modules/xxx' or '/xxx', and suffix starts with '/'.
146 String prefix = name.substring(0, suffixStart);
147 String suffix = name.substring(suffixStart);
148 if (suffix.startsWith(PREVIEW_INFIX + "/")) {
149 // Preview resources/directories.
150 String nonPreviewName = prefix + suffix.substring(PREVIEW_INFIX.length());
151 return FLAGS_IS_PREVIEW_VERSION
152 | (hasEntry.test(nonPreviewName) ? 0 : FLAGS_IS_PREVIEW_ONLY);
153 } else if (!suffix.startsWith("/META-INF/")) {
154 // Non-preview resources/directories.
155 String previewName = prefix + PREVIEW_INFIX + suffix;
156 return hasEntry.test(previewName) ? FLAGS_HAS_PREVIEW_VERSION : 0;
157 } else {
158 // Suffix is '/META-INF/xxx' and no preview version is even possible.
159 return 0;
160 }
161 }
162
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
186 /**
187 * Tests a non-preview image location's flags to see if it has preview
188 * content associated with it.
189 */
190 public static boolean hasPreviewVersion(int flags) {
191 return (flags & FLAGS_HAS_PREVIEW_VERSION) != 0;
192 }
193
194 /**
195 * Tests an image location's flags to see if it only exists in preview mode.
196 */
197 public static boolean isPreviewOnly(int flags) {
198 return (flags & FLAGS_IS_PREVIEW_ONLY) != 0;
199 }
200
201 public enum LocationType {
202 RESOURCE, MODULES_ROOT, MODULES_DIR, PACKAGES_ROOT, PACKAGES_DIR;
203 }
204
205 protected final long[] attributes;
206
207 protected final ImageStrings strings;
208
209 public ImageLocation(long[] attributes, ImageStrings strings) {
210 this.attributes = Objects.requireNonNull(attributes);
211 this.strings = Objects.requireNonNull(strings);
212 }
213
214 ImageStrings getStrings() {
215 return strings;
216 }
217
218 static long[] decompress(ByteBuffer bytes, int offset) {
219 Objects.requireNonNull(bytes);
220 long[] attributes = new long[ATTRIBUTE_COUNT];
221
222 int limit = bytes.limit();
223 while (offset < limit) {
424 public int getBaseOffset() {
425 return (int)getAttribute(ATTRIBUTE_BASE);
426 }
427
428 public String getParent() {
429 return getAttributeString(ATTRIBUTE_PARENT);
430 }
431
432 public int getParentOffset() {
433 return (int)getAttribute(ATTRIBUTE_PARENT);
434 }
435
436 public String getExtension() {
437 return getAttributeString(ATTRIBUTE_EXTENSION);
438 }
439
440 public int getExtensionOffset() {
441 return (int)getAttribute(ATTRIBUTE_EXTENSION);
442 }
443
444 public int getFlags() {
445 return (int) getAttribute(ATTRIBUTE_PREVIEW_FLAGS);
446 }
447
448 public String getFullName() {
449 return getFullName(false);
450 }
451
452 public String getFullName(boolean modulesPrefix) {
453 StringBuilder builder = new StringBuilder();
454
455 if (getModuleOffset() != 0) {
456 if (modulesPrefix) {
457 builder.append(MODULES_PREFIX);
458 }
459
460 builder.append('/');
461 builder.append(getModule());
462 builder.append('/');
463 }
464
465 if (getParentOffset() != 0) {
466 builder.append(getParent());
467 builder.append('/');
468 }
469
470 builder.append(getBase());
471
472 if (getExtensionOffset() != 0) {
473 builder.append('.');
474 builder.append(getExtension());
475 }
476
477 return builder.toString();
478 }
479
480 public long getContentOffset() {
481 return getAttribute(ATTRIBUTE_OFFSET);
482 }
483
484 public long getCompressedSize() {
485 return getAttribute(ATTRIBUTE_COMPRESSED);
486 }
487
488 public long getUncompressedSize() {
489 return getAttribute(ATTRIBUTE_UNCOMPRESSED);
490 }
491
492 // Fast (zero allocation) type determination for locations.
493 public LocationType getType() {
494 switch (getModuleOffset()) {
495 case ImageStrings.MODULES_STRING_OFFSET:
496 // Locations in /modules/... namespace are directory entries.
497 return LocationType.MODULES_DIR;
498 case ImageStrings.PACKAGES_STRING_OFFSET:
499 // Locations in /packages/... namespace are always 2-level
500 // "/packages/xxx" directories.
501 return LocationType.PACKAGES_DIR;
502 case ImageStrings.EMPTY_STRING_OFFSET:
503 // Only 2 choices, either the "/modules" or "/packages" root.
504 assert isRootDir() : "Invalid root directory: " + getFullName();
505 return getBase().charAt(1) == 'p'
506 ? LocationType.PACKAGES_ROOT
507 : LocationType.MODULES_ROOT;
508 default:
509 // Anything else is /<module>/<path> and references a resource.
510 return LocationType.RESOURCE;
511 }
512 }
513
514 private boolean isRootDir() {
515 if (getModuleOffset() == 0 && getParentOffset() == 0) {
516 String name = getFullName();
517 return name.equals(MODULES_PREFIX) || name.equals(PACKAGES_PREFIX);
518 }
519 return false;
520 }
521
522 @Override
523 public String toString() {
524 // Cannot use String.format() (too early in startup for locale code).
525 return "ImageLocation[name='" + getFullName() + "', type=" + getType() + ", flags=" + getFlags() + "]";
526 }
527
528 static ImageLocation readFrom(BasicImageReader reader, int offset) {
529 Objects.requireNonNull(reader);
530 long[] attributes = reader.getAttributes(offset);
531 ImageStringsReader strings = reader.getStrings();
532
533 return new ImageLocation(attributes, strings);
534 }
535 }
|