Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java
41161 views
1
/*
2
* Copyright (c) 2012, 2020, 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 com.sun.tools.jdeps;
27
28
import com.sun.tools.classfile.AccessFlags;
29
import com.sun.tools.classfile.ClassFile;
30
import com.sun.tools.classfile.ConstantPoolException;
31
import com.sun.tools.classfile.Dependencies.ClassFileError;
32
33
import java.io.Closeable;
34
import java.io.File;
35
import java.io.FileNotFoundException;
36
import java.io.IOException;
37
import java.io.InputStream;
38
import java.io.UncheckedIOException;
39
import java.nio.file.FileSystem;
40
import java.nio.file.FileSystems;
41
import java.nio.file.Files;
42
import java.nio.file.Path;
43
import java.util.ArrayList;
44
import java.util.Collections;
45
import java.util.Enumeration;
46
import java.util.Iterator;
47
import java.util.List;
48
import java.util.NoSuchElementException;
49
import java.util.Set;
50
import java.util.jar.JarEntry;
51
import java.util.jar.JarFile;
52
import java.util.stream.Collectors;
53
import java.util.stream.Stream;
54
import java.util.zip.ZipFile;
55
56
/**
57
* ClassFileReader reads ClassFile(s) of a given path that can be
58
* a .class file, a directory, or a JAR file.
59
*/
60
public class ClassFileReader implements Closeable {
61
/**
62
* Returns a ClassFileReader instance of a given path.
63
*/
64
public static ClassFileReader newInstance(Path path, Runtime.Version version) throws IOException {
65
if (Files.notExists(path)) {
66
throw new FileNotFoundException(path.toString());
67
}
68
69
if (Files.isDirectory(path)) {
70
return new DirectoryReader(path);
71
} else if (path.getFileName().toString().endsWith(".jar")) {
72
return new JarFileReader(path, version);
73
} else {
74
return new ClassFileReader(path);
75
}
76
}
77
78
/**
79
* Returns a ClassFileReader instance of a given FileSystem and path.
80
*
81
* This method is used for reading classes from jrtfs.
82
*/
83
public static ClassFileReader newInstance(FileSystem fs, Path path) throws IOException {
84
return new DirectoryReader(fs, path);
85
}
86
87
protected final Path path;
88
protected final String baseFileName;
89
protected Set<String> entries; // binary names
90
91
protected final List<String> skippedEntries = new ArrayList<>();
92
protected ClassFileReader(Path path) {
93
this.path = path;
94
this.baseFileName = path.getFileName() != null
95
? path.getFileName().toString()
96
: path.toString();
97
}
98
99
public String getFileName() {
100
return baseFileName;
101
}
102
103
public List<String> skippedEntries() {
104
return skippedEntries;
105
}
106
107
/**
108
* Returns all entries in this archive.
109
*/
110
public Set<String> entries() {
111
Set<String> es = this.entries;
112
if (es == null) {
113
// lazily scan the entries
114
this.entries = scan();
115
}
116
return this.entries;
117
}
118
119
/**
120
* Returns the ClassFile matching the given binary name
121
* or a fully-qualified class name.
122
*/
123
public ClassFile getClassFile(String name) throws IOException {
124
if (name.indexOf('.') > 0) {
125
int i = name.lastIndexOf('.');
126
String pathname = name.replace('.', File.separatorChar) + ".class";
127
if (baseFileName.equals(pathname) ||
128
baseFileName.equals(pathname.substring(0, i) + "$" +
129
pathname.substring(i+1, pathname.length()))) {
130
return readClassFile(path);
131
}
132
} else {
133
if (baseFileName.equals(name.replace('/', File.separatorChar) + ".class")) {
134
return readClassFile(path);
135
}
136
}
137
return null;
138
}
139
140
public Iterable<ClassFile> getClassFiles() throws IOException {
141
return FileIterator::new;
142
}
143
144
protected ClassFile readClassFile(Path p) throws IOException {
145
InputStream is = null;
146
try {
147
is = Files.newInputStream(p);
148
return ClassFile.read(is);
149
} catch (ConstantPoolException e) {
150
throw new ClassFileError(e);
151
} finally {
152
if (is != null) {
153
is.close();
154
}
155
}
156
}
157
158
protected Set<String> scan() {
159
try {
160
ClassFile cf = ClassFile.read(path);
161
String name = cf.access_flags.is(AccessFlags.ACC_MODULE)
162
? "module-info" : cf.getName();
163
return Collections.singleton(name);
164
} catch (ConstantPoolException|IOException e) {
165
throw new ClassFileError(e);
166
}
167
}
168
169
static boolean isClass(Path file) {
170
String fn = file.getFileName().toString();
171
return fn.endsWith(".class");
172
}
173
174
@Override
175
public void close() throws IOException {
176
}
177
178
class FileIterator implements Iterator<ClassFile> {
179
int count;
180
FileIterator() {
181
this.count = 0;
182
}
183
public boolean hasNext() {
184
return count == 0 && baseFileName.endsWith(".class");
185
}
186
187
public ClassFile next() {
188
if (!hasNext()) {
189
throw new NoSuchElementException();
190
}
191
try {
192
ClassFile cf = readClassFile(path);
193
count++;
194
return cf;
195
} catch (IOException e) {
196
throw new ClassFileError(e);
197
}
198
}
199
200
public void remove() {
201
throw new UnsupportedOperationException("Not supported yet.");
202
}
203
}
204
205
public String toString() {
206
return path.toString();
207
}
208
209
private static class DirectoryReader extends ClassFileReader {
210
protected final String fsSep;
211
DirectoryReader(Path path) throws IOException {
212
this(FileSystems.getDefault(), path);
213
}
214
DirectoryReader(FileSystem fs, Path path) throws IOException {
215
super(path);
216
this.fsSep = fs.getSeparator();
217
}
218
219
protected Set<String> scan() {
220
try (Stream<Path> stream = Files.walk(path, Integer.MAX_VALUE)) {
221
return stream.filter(ClassFileReader::isClass)
222
.map(path::relativize)
223
.map(Path::toString)
224
.map(p -> p.replace(File.separatorChar, '/'))
225
.collect(Collectors.toSet());
226
} catch (IOException e) {
227
throw new UncheckedIOException(e);
228
}
229
}
230
231
public ClassFile getClassFile(String name) throws IOException {
232
if (name.indexOf('.') > 0) {
233
int i = name.lastIndexOf('.');
234
String pathname = name.replace(".", fsSep) + ".class";
235
Path p = path.resolve(pathname);
236
if (Files.notExists(p)) {
237
p = path.resolve(pathname.substring(0, i) + "$" +
238
pathname.substring(i+1, pathname.length()));
239
}
240
if (Files.exists(p)) {
241
return readClassFile(p);
242
}
243
} else {
244
Path p = path.resolve(name + ".class");
245
if (Files.exists(p)) {
246
return readClassFile(p);
247
}
248
}
249
return null;
250
}
251
252
public Iterable<ClassFile> getClassFiles() throws IOException {
253
final Iterator<ClassFile> iter = new DirectoryIterator();
254
return () -> iter;
255
}
256
257
class DirectoryIterator implements Iterator<ClassFile> {
258
private final List<Path> entries;
259
private int index = 0;
260
DirectoryIterator() throws IOException {
261
List<Path> paths = null;
262
try (Stream<Path> stream = Files.walk(path, Integer.MAX_VALUE)) {
263
paths = stream.filter(ClassFileReader::isClass).toList();
264
265
}
266
this.entries = paths;
267
this.index = 0;
268
}
269
270
public boolean hasNext() {
271
return index != entries.size();
272
}
273
274
public ClassFile next() {
275
if (!hasNext()) {
276
throw new NoSuchElementException();
277
}
278
Path path = entries.get(index++);
279
try {
280
return readClassFile(path);
281
} catch (IOException e) {
282
throw new ClassFileError(e);
283
}
284
}
285
286
public void remove() {
287
throw new UnsupportedOperationException("Not supported yet.");
288
}
289
}
290
}
291
292
static class JarFileReader extends ClassFileReader {
293
private final JarFile jarfile;
294
private final Runtime.Version version;
295
296
JarFileReader(Path path, Runtime.Version version) throws IOException {
297
this(path, openJarFile(path.toFile(), version), version);
298
}
299
300
JarFileReader(Path path, JarFile jf, Runtime.Version version) throws IOException {
301
super(path);
302
this.jarfile = jf;
303
this.version = version;
304
}
305
306
@Override
307
public void close() throws IOException {
308
jarfile.close();
309
}
310
311
private static JarFile openJarFile(File f, Runtime.Version version)
312
throws IOException {
313
JarFile jf;
314
if (version == null) {
315
jf = new JarFile(f, false);
316
if (jf.isMultiRelease()) {
317
throw new MultiReleaseException("err.multirelease.option.notfound", f.getName());
318
}
319
} else {
320
jf = new JarFile(f, false, ZipFile.OPEN_READ, version);
321
}
322
return jf;
323
}
324
325
protected Set<String> scan() {
326
try (JarFile jf = openJarFile(path.toFile(), version)) {
327
return jf.versionedStream().map(JarEntry::getName)
328
.filter(n -> n.endsWith(".class"))
329
.collect(Collectors.toSet());
330
} catch (IOException e) {
331
throw new UncheckedIOException(e);
332
}
333
}
334
335
public ClassFile getClassFile(String name) throws IOException {
336
if (name.indexOf('.') > 0) {
337
int i = name.lastIndexOf('.');
338
String entryName = name.replace('.', '/') + ".class";
339
JarEntry e = jarfile.getJarEntry(entryName);
340
if (e == null) {
341
e = jarfile.getJarEntry(entryName.substring(0, i) + "$"
342
+ entryName.substring(i + 1, entryName.length()));
343
}
344
if (e != null) {
345
return readClassFile(jarfile, e);
346
}
347
} else {
348
JarEntry e = jarfile.getJarEntry(name + ".class");
349
if (e != null) {
350
return readClassFile(jarfile, e);
351
}
352
}
353
return null;
354
}
355
356
protected ClassFile readClassFile(JarFile jarfile, JarEntry e) throws IOException {
357
try (InputStream is = jarfile.getInputStream(e)) {
358
ClassFile cf = ClassFile.read(is);
359
if (jarfile.isMultiRelease()) {
360
VersionHelper.add(jarfile, e, cf);
361
}
362
return cf;
363
} catch (ConstantPoolException ex) {
364
throw new ClassFileError(ex);
365
}
366
}
367
368
public Iterable<ClassFile> getClassFiles() throws IOException {
369
final Iterator<ClassFile> iter = new JarFileIterator(this, jarfile);
370
return () -> iter;
371
}
372
}
373
374
class JarFileIterator implements Iterator<ClassFile> {
375
protected final JarFileReader reader;
376
protected Iterator<JarEntry> entries;
377
protected JarFile jf;
378
protected JarEntry nextEntry;
379
protected ClassFile cf;
380
JarFileIterator(JarFileReader reader) {
381
this(reader, null);
382
}
383
JarFileIterator(JarFileReader reader, JarFile jarfile) {
384
this.reader = reader;
385
setJarFile(jarfile);
386
}
387
388
void setJarFile(JarFile jarfile) {
389
if (jarfile == null) return;
390
391
this.jf = jarfile;
392
this.entries = jarfile.versionedStream().iterator();
393
this.nextEntry = nextEntry();
394
}
395
396
public boolean hasNext() {
397
if (nextEntry != null && cf != null) {
398
return true;
399
}
400
while (nextEntry != null) {
401
try {
402
cf = reader.readClassFile(jf, nextEntry);
403
return true;
404
} catch (ClassFileError | IOException ex) {
405
skippedEntries.add(String.format("%s: %s (%s)",
406
ex.getMessage(),
407
nextEntry.getName(),
408
jf.getName()));
409
}
410
nextEntry = nextEntry();
411
}
412
return false;
413
}
414
415
public ClassFile next() {
416
if (!hasNext()) {
417
throw new NoSuchElementException();
418
}
419
ClassFile classFile = cf;
420
cf = null;
421
nextEntry = nextEntry();
422
return classFile;
423
}
424
425
protected JarEntry nextEntry() {
426
while (entries.hasNext()) {
427
JarEntry e = entries.next();
428
String name = e.getName();
429
if (name.endsWith(".class")) {
430
return e;
431
}
432
}
433
return null;
434
}
435
436
public void remove() {
437
throw new UnsupportedOperationException("Not supported yet.");
438
}
439
}
440
private static final String MODULE_INFO = "module-info.class";
441
}
442
443