Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java
41159 views
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
package jdk.internal.jimage;
26
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.io.UncheckedIOException;
30
import java.nio.ByteBuffer;
31
import java.nio.ByteOrder;
32
import java.nio.IntBuffer;
33
import java.nio.file.Files;
34
import java.nio.file.attribute.BasicFileAttributes;
35
import java.nio.file.attribute.FileTime;
36
import java.nio.file.Path;
37
import java.util.ArrayList;
38
import java.util.Collections;
39
import java.util.HashMap;
40
import java.util.HashSet;
41
import java.util.List;
42
import java.util.Map;
43
import java.util.Objects;
44
import java.util.Set;
45
import java.util.function.Consumer;
46
47
/**
48
* @implNote This class needs to maintain JDK 8 source compatibility.
49
*
50
* It is used internally in the JDK to implement jimage/jrtfs access,
51
* but also compiled and delivered as part of the jrtfs.jar to support access
52
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
53
*/
54
public final class ImageReader implements AutoCloseable {
55
private final SharedImageReader reader;
56
57
private volatile boolean closed;
58
59
private ImageReader(SharedImageReader reader) {
60
this.reader = reader;
61
}
62
63
public static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException {
64
Objects.requireNonNull(imagePath);
65
Objects.requireNonNull(byteOrder);
66
67
return SharedImageReader.open(imagePath, byteOrder);
68
}
69
70
public static ImageReader open(Path imagePath) throws IOException {
71
return open(imagePath, ByteOrder.nativeOrder());
72
}
73
74
@Override
75
public void close() throws IOException {
76
if (closed) {
77
throw new IOException("image file already closed");
78
}
79
reader.close(this);
80
closed = true;
81
}
82
83
private void ensureOpen() throws IOException {
84
if (closed) {
85
throw new IOException("image file closed");
86
}
87
}
88
89
private void requireOpen() {
90
if (closed) {
91
throw new IllegalStateException("image file closed");
92
}
93
}
94
95
// directory management interface
96
public Directory getRootDirectory() throws IOException {
97
ensureOpen();
98
return reader.getRootDirectory();
99
}
100
101
102
public Node findNode(String name) throws IOException {
103
ensureOpen();
104
return reader.findNode(name);
105
}
106
107
public byte[] getResource(Node node) throws IOException {
108
ensureOpen();
109
return reader.getResource(node);
110
}
111
112
public byte[] getResource(Resource rs) throws IOException {
113
ensureOpen();
114
return reader.getResource(rs);
115
}
116
117
public ImageHeader getHeader() {
118
requireOpen();
119
return reader.getHeader();
120
}
121
122
public static void releaseByteBuffer(ByteBuffer buffer) {
123
BasicImageReader.releaseByteBuffer(buffer);
124
}
125
126
public String getName() {
127
requireOpen();
128
return reader.getName();
129
}
130
131
public ByteOrder getByteOrder() {
132
requireOpen();
133
return reader.getByteOrder();
134
}
135
136
public Path getImagePath() {
137
requireOpen();
138
return reader.getImagePath();
139
}
140
141
public ImageStringsReader getStrings() {
142
requireOpen();
143
return reader.getStrings();
144
}
145
146
public ImageLocation findLocation(String mn, String rn) {
147
requireOpen();
148
return reader.findLocation(mn, rn);
149
}
150
151
public boolean verifyLocation(String mn, String rn) {
152
requireOpen();
153
return reader.verifyLocation(mn, rn);
154
}
155
156
public ImageLocation findLocation(String name) {
157
requireOpen();
158
return reader.findLocation(name);
159
}
160
161
public String[] getEntryNames() {
162
requireOpen();
163
return reader.getEntryNames();
164
}
165
166
public String[] getModuleNames() {
167
requireOpen();
168
int off = "/modules/".length();
169
return reader.findNode("/modules")
170
.getChildren()
171
.stream()
172
.map(Node::getNameString)
173
.map(s -> s.substring(off, s.length()))
174
.toArray(String[]::new);
175
}
176
177
public long[] getAttributes(int offset) {
178
requireOpen();
179
return reader.getAttributes(offset);
180
}
181
182
public String getString(int offset) {
183
requireOpen();
184
return reader.getString(offset);
185
}
186
187
public byte[] getResource(String name) {
188
requireOpen();
189
return reader.getResource(name);
190
}
191
192
public byte[] getResource(ImageLocation loc) {
193
requireOpen();
194
return reader.getResource(loc);
195
}
196
197
public ByteBuffer getResourceBuffer(ImageLocation loc) {
198
requireOpen();
199
return reader.getResourceBuffer(loc);
200
}
201
202
public InputStream getResourceStream(ImageLocation loc) {
203
requireOpen();
204
return reader.getResourceStream(loc);
205
}
206
207
private static final class SharedImageReader extends BasicImageReader {
208
static final int SIZE_OF_OFFSET = Integer.BYTES;
209
210
static final Map<Path, SharedImageReader> OPEN_FILES = new HashMap<>();
211
212
// List of openers for this shared image.
213
final Set<ImageReader> openers;
214
215
// attributes of the .jimage file. jimage file does not contain
216
// attributes for the individual resources (yet). We use attributes
217
// of the jimage file itself (creation, modification, access times).
218
// Iniitalized lazily, see {@link #imageFileAttributes()}.
219
BasicFileAttributes imageFileAttributes;
220
221
// directory management implementation
222
final HashMap<String, Node> nodes;
223
volatile Directory rootDir;
224
225
Directory packagesDir;
226
Directory modulesDir;
227
228
private SharedImageReader(Path imagePath, ByteOrder byteOrder) throws IOException {
229
super(imagePath, byteOrder);
230
this.openers = new HashSet<>();
231
this.nodes = new HashMap<>();
232
}
233
234
public static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException {
235
Objects.requireNonNull(imagePath);
236
Objects.requireNonNull(byteOrder);
237
238
synchronized (OPEN_FILES) {
239
SharedImageReader reader = OPEN_FILES.get(imagePath);
240
241
if (reader == null) {
242
// Will fail with an IOException if wrong byteOrder.
243
reader = new SharedImageReader(imagePath, byteOrder);
244
OPEN_FILES.put(imagePath, reader);
245
} else if (reader.getByteOrder() != byteOrder) {
246
throw new IOException("\"" + reader.getName() + "\" is not an image file");
247
}
248
249
ImageReader image = new ImageReader(reader);
250
reader.openers.add(image);
251
252
return image;
253
}
254
}
255
256
public void close(ImageReader image) throws IOException {
257
Objects.requireNonNull(image);
258
259
synchronized (OPEN_FILES) {
260
if (!openers.remove(image)) {
261
throw new IOException("image file already closed");
262
}
263
264
if (openers.isEmpty()) {
265
close();
266
nodes.clear();
267
rootDir = null;
268
269
if (!OPEN_FILES.remove(this.getImagePath(), this)) {
270
throw new IOException("image file not found in open list");
271
}
272
}
273
}
274
}
275
276
void addOpener(ImageReader reader) {
277
synchronized (OPEN_FILES) {
278
openers.add(reader);
279
}
280
}
281
282
boolean removeOpener(ImageReader reader) {
283
synchronized (OPEN_FILES) {
284
return openers.remove(reader);
285
}
286
}
287
288
// directory management interface
289
Directory getRootDirectory() {
290
return buildRootDirectory();
291
}
292
293
/**
294
* Lazily build a node from a name.
295
*/
296
synchronized Node buildNode(String name) {
297
Node n;
298
boolean isPackages = name.startsWith("/packages");
299
boolean isModules = !isPackages && name.startsWith("/modules");
300
301
if (!(isModules || isPackages)) {
302
return null;
303
}
304
305
ImageLocation loc = findLocation(name);
306
307
if (loc != null) { // A sub tree node
308
if (isPackages) {
309
n = handlePackages(name, loc);
310
} else { // modules sub tree
311
n = handleModulesSubTree(name, loc);
312
}
313
} else { // Asking for a resource? /modules/java.base/java/lang/Object.class
314
if (isModules) {
315
n = handleResource(name);
316
} else {
317
// Possibly ask for /packages/java.lang/java.base
318
// although /packages/java.base not created
319
n = handleModuleLink(name);
320
}
321
}
322
return n;
323
}
324
325
synchronized Directory buildRootDirectory() {
326
Directory root = rootDir; // volatile read
327
if (root != null) {
328
return root;
329
}
330
331
root = newDirectory(null, "/");
332
root.setIsRootDir();
333
334
// /packages dir
335
packagesDir = newDirectory(root, "/packages");
336
packagesDir.setIsPackagesDir();
337
338
// /modules dir
339
modulesDir = newDirectory(root, "/modules");
340
modulesDir.setIsModulesDir();
341
342
root.setCompleted(true);
343
return rootDir = root;
344
}
345
346
/**
347
* To visit sub tree resources.
348
*/
349
interface LocationVisitor {
350
void visit(ImageLocation loc);
351
}
352
353
void visitLocation(ImageLocation loc, LocationVisitor visitor) {
354
byte[] offsets = getResource(loc);
355
ByteBuffer buffer = ByteBuffer.wrap(offsets);
356
buffer.order(getByteOrder());
357
IntBuffer intBuffer = buffer.asIntBuffer();
358
for (int i = 0; i < offsets.length / SIZE_OF_OFFSET; i++) {
359
int offset = intBuffer.get(i);
360
ImageLocation pkgLoc = getLocation(offset);
361
visitor.visit(pkgLoc);
362
}
363
}
364
365
void visitPackageLocation(ImageLocation loc) {
366
// Retrieve package name
367
String pkgName = getBaseExt(loc);
368
// Content is array of offsets in Strings table
369
byte[] stringsOffsets = getResource(loc);
370
ByteBuffer buffer = ByteBuffer.wrap(stringsOffsets);
371
buffer.order(getByteOrder());
372
IntBuffer intBuffer = buffer.asIntBuffer();
373
// For each module, create a link node.
374
for (int i = 0; i < stringsOffsets.length / SIZE_OF_OFFSET; i++) {
375
// skip empty state, useless.
376
intBuffer.get(i);
377
i++;
378
int offset = intBuffer.get(i);
379
String moduleName = getString(offset);
380
Node targetNode = findNode("/modules/" + moduleName);
381
if (targetNode != null) {
382
String pkgDirName = packagesDir.getName() + "/" + pkgName;
383
Directory pkgDir = (Directory) nodes.get(pkgDirName);
384
newLinkNode(pkgDir, pkgDir.getName() + "/" + moduleName, targetNode);
385
}
386
}
387
}
388
389
Node handlePackages(String name, ImageLocation loc) {
390
long size = loc.getUncompressedSize();
391
Node n = null;
392
// Only possiblities are /packages, /packages/package/module
393
if (name.equals("/packages")) {
394
visitLocation(loc, (childloc) -> {
395
findNode(childloc.getFullName());
396
});
397
packagesDir.setCompleted(true);
398
n = packagesDir;
399
} else {
400
if (size != 0) { // children are offsets to module in StringsTable
401
String pkgName = getBaseExt(loc);
402
Directory pkgDir = newDirectory(packagesDir, packagesDir.getName() + "/" + pkgName);
403
visitPackageLocation(loc);
404
pkgDir.setCompleted(true);
405
n = pkgDir;
406
} else { // Link to module
407
String pkgName = loc.getParent();
408
String modName = getBaseExt(loc);
409
Node targetNode = findNode("/modules/" + modName);
410
if (targetNode != null) {
411
String pkgDirName = packagesDir.getName() + "/" + pkgName;
412
Directory pkgDir = (Directory) nodes.get(pkgDirName);
413
Node linkNode = newLinkNode(pkgDir, pkgDir.getName() + "/" + modName, targetNode);
414
n = linkNode;
415
}
416
}
417
}
418
return n;
419
}
420
421
// Asking for /packages/package/module although
422
// /packages/<pkg>/ not yet created, need to create it
423
// prior to return the link to module node.
424
Node handleModuleLink(String name) {
425
// eg: unresolved /packages/package/module
426
// Build /packages/package node
427
Node ret = null;
428
String radical = "/packages/";
429
String path = name;
430
if (path.startsWith(radical)) {
431
int start = radical.length();
432
int pkgEnd = path.indexOf('/', start);
433
if (pkgEnd != -1) {
434
String pkg = path.substring(start, pkgEnd);
435
String pkgPath = radical + pkg;
436
Node n = findNode(pkgPath);
437
// If not found means that this is a symbolic link such as:
438
// /packages/java.util/java.base/java/util/Vector.class
439
// and will be done by a retry of the filesystem
440
for (Node child : n.getChildren()) {
441
if (child.name.equals(name)) {
442
ret = child;
443
break;
444
}
445
}
446
}
447
}
448
return ret;
449
}
450
451
Node handleModulesSubTree(String name, ImageLocation loc) {
452
Node n;
453
assert (name.equals(loc.getFullName()));
454
Directory dir = makeDirectories(name);
455
visitLocation(loc, (childloc) -> {
456
String path = childloc.getFullName();
457
if (path.startsWith("/modules")) { // a package
458
makeDirectories(path);
459
} else { // a resource
460
makeDirectories(childloc.buildName(true, true, false));
461
newResource(dir, childloc);
462
}
463
});
464
dir.setCompleted(true);
465
n = dir;
466
return n;
467
}
468
469
Node handleResource(String name) {
470
Node n = null;
471
if (!name.startsWith("/modules/")) {
472
return null;
473
}
474
// Make sure that the thing that follows "/modules/" is a module name.
475
int moduleEndIndex = name.indexOf('/', "/modules/".length());
476
if (moduleEndIndex == -1) {
477
return null;
478
}
479
ImageLocation moduleLoc = findLocation(name.substring(0, moduleEndIndex));
480
if (moduleLoc == null || moduleLoc.getModuleOffset() == 0) {
481
return null;
482
}
483
484
String locationPath = name.substring("/modules".length());
485
ImageLocation resourceLoc = findLocation(locationPath);
486
if (resourceLoc != null) {
487
Directory dir = makeDirectories(resourceLoc.buildName(true, true, false));
488
Resource res = newResource(dir, resourceLoc);
489
n = res;
490
}
491
return n;
492
}
493
494
String getBaseExt(ImageLocation loc) {
495
String base = loc.getBase();
496
String ext = loc.getExtension();
497
if (ext != null && !ext.isEmpty()) {
498
base = base + "." + ext;
499
}
500
return base;
501
}
502
503
synchronized Node findNode(String name) {
504
buildRootDirectory();
505
Node n = nodes.get(name);
506
if (n == null || !n.isCompleted()) {
507
n = buildNode(name);
508
}
509
return n;
510
}
511
512
/**
513
* Returns the file attributes of the image file.
514
*/
515
BasicFileAttributes imageFileAttributes() {
516
BasicFileAttributes attrs = imageFileAttributes;
517
if (attrs == null) {
518
try {
519
Path file = getImagePath();
520
attrs = Files.readAttributes(file, BasicFileAttributes.class);
521
} catch (IOException ioe) {
522
throw new UncheckedIOException(ioe);
523
}
524
imageFileAttributes = attrs;
525
}
526
return attrs;
527
}
528
529
Directory newDirectory(Directory parent, String name) {
530
Directory dir = Directory.create(parent, name, imageFileAttributes());
531
nodes.put(dir.getName(), dir);
532
return dir;
533
}
534
535
Resource newResource(Directory parent, ImageLocation loc) {
536
Resource res = Resource.create(parent, loc, imageFileAttributes());
537
nodes.put(res.getName(), res);
538
return res;
539
}
540
541
LinkNode newLinkNode(Directory dir, String name, Node link) {
542
LinkNode linkNode = LinkNode.create(dir, name, link);
543
nodes.put(linkNode.getName(), linkNode);
544
return linkNode;
545
}
546
547
Directory makeDirectories(String parent) {
548
Directory last = rootDir;
549
for (int offset = parent.indexOf('/', 1);
550
offset != -1;
551
offset = parent.indexOf('/', offset + 1)) {
552
String dir = parent.substring(0, offset);
553
last = makeDirectory(dir, last);
554
}
555
return makeDirectory(parent, last);
556
557
}
558
559
Directory makeDirectory(String dir, Directory last) {
560
Directory nextDir = (Directory) nodes.get(dir);
561
if (nextDir == null) {
562
nextDir = newDirectory(last, dir);
563
}
564
return nextDir;
565
}
566
567
byte[] getResource(Node node) throws IOException {
568
if (node.isResource()) {
569
return super.getResource(node.getLocation());
570
}
571
throw new IOException("Not a resource: " + node);
572
}
573
574
byte[] getResource(Resource rs) throws IOException {
575
return super.getResource(rs.getLocation());
576
}
577
}
578
579
// jimage file does not store directory structure. We build nodes
580
// using the "path" strings found in the jimage file.
581
// Node can be a directory or a resource
582
public abstract static class Node {
583
private static final int ROOT_DIR = 0b0000_0000_0000_0001;
584
private static final int PACKAGES_DIR = 0b0000_0000_0000_0010;
585
private static final int MODULES_DIR = 0b0000_0000_0000_0100;
586
587
private int flags;
588
private final String name;
589
private final BasicFileAttributes fileAttrs;
590
private boolean completed;
591
592
protected Node(String name, BasicFileAttributes fileAttrs) {
593
this.name = Objects.requireNonNull(name);
594
this.fileAttrs = Objects.requireNonNull(fileAttrs);
595
}
596
597
/**
598
* A node is completed when all its direct children have been built.
599
*
600
* @return
601
*/
602
public boolean isCompleted() {
603
return completed;
604
}
605
606
public void setCompleted(boolean completed) {
607
this.completed = completed;
608
}
609
610
public final void setIsRootDir() {
611
flags |= ROOT_DIR;
612
}
613
614
public final boolean isRootDir() {
615
return (flags & ROOT_DIR) != 0;
616
}
617
618
public final void setIsPackagesDir() {
619
flags |= PACKAGES_DIR;
620
}
621
622
public final boolean isPackagesDir() {
623
return (flags & PACKAGES_DIR) != 0;
624
}
625
626
public final void setIsModulesDir() {
627
flags |= MODULES_DIR;
628
}
629
630
public final boolean isModulesDir() {
631
return (flags & MODULES_DIR) != 0;
632
}
633
634
public final String getName() {
635
return name;
636
}
637
638
public final BasicFileAttributes getFileAttributes() {
639
return fileAttrs;
640
}
641
642
// resolve this Node (if this is a soft link, get underlying Node)
643
public final Node resolveLink() {
644
return resolveLink(false);
645
}
646
647
public Node resolveLink(boolean recursive) {
648
return this;
649
}
650
651
// is this a soft link Node?
652
public boolean isLink() {
653
return false;
654
}
655
656
public boolean isDirectory() {
657
return false;
658
}
659
660
public List<Node> getChildren() {
661
throw new IllegalArgumentException("not a directory: " + getNameString());
662
}
663
664
public boolean isResource() {
665
return false;
666
}
667
668
public ImageLocation getLocation() {
669
throw new IllegalArgumentException("not a resource: " + getNameString());
670
}
671
672
public long size() {
673
return 0L;
674
}
675
676
public long compressedSize() {
677
return 0L;
678
}
679
680
public String extension() {
681
return null;
682
}
683
684
public long contentOffset() {
685
return 0L;
686
}
687
688
public final FileTime creationTime() {
689
return fileAttrs.creationTime();
690
}
691
692
public final FileTime lastAccessTime() {
693
return fileAttrs.lastAccessTime();
694
}
695
696
public final FileTime lastModifiedTime() {
697
return fileAttrs.lastModifiedTime();
698
}
699
700
public final String getNameString() {
701
return name;
702
}
703
704
@Override
705
public final String toString() {
706
return getNameString();
707
}
708
709
@Override
710
public final int hashCode() {
711
return name.hashCode();
712
}
713
714
@Override
715
public final boolean equals(Object other) {
716
if (this == other) {
717
return true;
718
}
719
720
if (other instanceof Node) {
721
return name.equals(((Node) other).name);
722
}
723
724
return false;
725
}
726
}
727
728
// directory node - directory has full path name without '/' at end.
729
static final class Directory extends Node {
730
private final List<Node> children;
731
732
private Directory(String name, BasicFileAttributes fileAttrs) {
733
super(name, fileAttrs);
734
children = new ArrayList<>();
735
}
736
737
static Directory create(Directory parent, String name, BasicFileAttributes fileAttrs) {
738
Directory d = new Directory(name, fileAttrs);
739
if (parent != null) {
740
parent.addChild(d);
741
}
742
return d;
743
}
744
745
@Override
746
public boolean isDirectory() {
747
return true;
748
}
749
750
@Override
751
public List<Node> getChildren() {
752
return Collections.unmodifiableList(children);
753
}
754
755
void addChild(Node node) {
756
children.add(node);
757
}
758
759
public void walk(Consumer<? super Node> consumer) {
760
consumer.accept(this);
761
for (Node child : children) {
762
if (child.isDirectory()) {
763
((Directory)child).walk(consumer);
764
} else {
765
consumer.accept(child);
766
}
767
}
768
}
769
}
770
771
// "resource" is .class or any other resource (compressed/uncompressed) in a jimage.
772
// full path of the resource is the "name" of the resource.
773
static class Resource extends Node {
774
private final ImageLocation loc;
775
776
private Resource(ImageLocation loc, BasicFileAttributes fileAttrs) {
777
super(loc.getFullName(true), fileAttrs);
778
this.loc = loc;
779
}
780
781
static Resource create(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) {
782
Resource rs = new Resource(loc, fileAttrs);
783
parent.addChild(rs);
784
return rs;
785
}
786
787
@Override
788
public boolean isCompleted() {
789
return true;
790
}
791
792
@Override
793
public boolean isResource() {
794
return true;
795
}
796
797
@Override
798
public ImageLocation getLocation() {
799
return loc;
800
}
801
802
@Override
803
public long size() {
804
return loc.getUncompressedSize();
805
}
806
807
@Override
808
public long compressedSize() {
809
return loc.getCompressedSize();
810
}
811
812
@Override
813
public String extension() {
814
return loc.getExtension();
815
}
816
817
@Override
818
public long contentOffset() {
819
return loc.getContentOffset();
820
}
821
}
822
823
// represents a soft link to another Node
824
static class LinkNode extends Node {
825
private final Node link;
826
827
private LinkNode(String name, Node link) {
828
super(name, link.getFileAttributes());
829
this.link = link;
830
}
831
832
static LinkNode create(Directory parent, String name, Node link) {
833
LinkNode ln = new LinkNode(name, link);
834
parent.addChild(ln);
835
return ln;
836
}
837
838
@Override
839
public boolean isCompleted() {
840
return true;
841
}
842
843
@Override
844
public Node resolveLink(boolean recursive) {
845
return (recursive && link instanceof LinkNode) ? ((LinkNode)link).resolveLink(true) : link;
846
}
847
848
@Override
849
public boolean isLink() {
850
return true;
851
}
852
}
853
}
854
855