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/JdepsConfiguration.java
41161 views
1
/*
2
* Copyright (c) 2012, 2018, 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 static com.sun.tools.jdeps.Module.trace;
29
import static java.util.stream.Collectors.*;
30
31
import com.sun.tools.classfile.Dependency;
32
33
import java.io.BufferedInputStream;
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.lang.module.Configuration;
40
import java.lang.module.ModuleDescriptor;
41
import java.lang.module.ModuleFinder;
42
import java.lang.module.ModuleReader;
43
import java.lang.module.ModuleReference;
44
import java.lang.module.ResolvedModule;
45
import java.net.URI;
46
import java.nio.file.DirectoryStream;
47
import java.nio.file.FileSystem;
48
import java.nio.file.FileSystems;
49
import java.nio.file.Files;
50
import java.nio.file.Path;
51
import java.nio.file.Paths;
52
import java.util.ArrayList;
53
import java.util.Collections;
54
import java.util.HashMap;
55
import java.util.HashSet;
56
import java.util.LinkedHashMap;
57
import java.util.LinkedHashSet;
58
import java.util.List;
59
import java.util.Map;
60
import java.util.Objects;
61
import java.util.Optional;
62
import java.util.Set;
63
import java.util.function.Function;
64
import java.util.function.Supplier;
65
import java.util.stream.Collectors;
66
import java.util.stream.Stream;
67
68
public class JdepsConfiguration implements AutoCloseable {
69
// the token for "all modules on the module path"
70
public static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
71
public static final String ALL_DEFAULT = "ALL-DEFAULT";
72
public static final String ALL_SYSTEM = "ALL-SYSTEM";
73
74
public static final String MODULE_INFO = "module-info.class";
75
76
private final SystemModuleFinder system;
77
private final ModuleFinder finder;
78
79
private final Map<String, Module> nameToModule = new LinkedHashMap<>();
80
private final Map<String, Module> packageToModule = new HashMap<>();
81
private final Map<String, List<Archive>> packageToUnnamedModule = new HashMap<>();
82
83
private final List<Archive> classpathArchives = new ArrayList<>();
84
private final List<Archive> initialArchives = new ArrayList<>();
85
private final Set<Module> rootModules = new HashSet<>();
86
private final Runtime.Version version;
87
88
private JdepsConfiguration(Configuration config,
89
SystemModuleFinder systemModulePath,
90
ModuleFinder finder,
91
Set<String> roots,
92
List<Path> classpaths,
93
List<Archive> initialArchives,
94
Runtime.Version version)
95
throws IOException
96
{
97
trace("root: %s%n", roots);
98
trace("initial archives: %s%n", initialArchives);
99
trace("class path: %s%n", classpaths);
100
this.system = systemModulePath;
101
this.finder = finder;
102
this.version = version;
103
104
config.modules().stream()
105
.map(ResolvedModule::reference)
106
.forEach(this::addModuleReference);
107
108
// packages in unnamed module
109
initialArchives.forEach(archive -> {
110
addPackagesInUnnamedModule(archive);
111
this.initialArchives.add(archive);
112
});
113
114
// classpath archives
115
for (Path p : classpaths) {
116
if (Files.exists(p)) {
117
Archive archive = Archive.getInstance(p, version);
118
addPackagesInUnnamedModule(archive);
119
classpathArchives.add(archive);
120
}
121
}
122
123
// all roots specified in --add-modules or -m are included
124
// as the initial set for analysis.
125
roots.stream()
126
.map(nameToModule::get)
127
.forEach(this.rootModules::add);
128
129
initProfiles();
130
131
trace("resolved modules: %s%n", nameToModule.keySet().stream()
132
.sorted().collect(joining("\n", "\n", "")));
133
}
134
135
private void initProfiles() {
136
// other system modules are not observed and not added in nameToModule map
137
Map<String, Module> systemModules =
138
system.moduleNames()
139
.collect(toMap(Function.identity(), (mn) -> {
140
Module m = nameToModule.get(mn);
141
if (m == null) {
142
ModuleReference mref = finder.find(mn).get();
143
m = toModule(mref);
144
}
145
return m;
146
}));
147
Profile.init(systemModules);
148
}
149
150
private void addModuleReference(ModuleReference mref) {
151
Module module = toModule(mref);
152
nameToModule.put(mref.descriptor().name(), module);
153
mref.descriptor().packages()
154
.forEach(pn -> packageToModule.putIfAbsent(pn, module));
155
}
156
157
private void addPackagesInUnnamedModule(Archive archive) {
158
archive.reader().entries().stream()
159
.filter(e -> e.endsWith(".class") && !e.equals(MODULE_INFO))
160
.map(this::toPackageName)
161
.distinct()
162
.forEach(pn -> packageToUnnamedModule
163
.computeIfAbsent(pn, _n -> new ArrayList<>()).add(archive));
164
}
165
166
private String toPackageName(String name) {
167
int i = name.lastIndexOf('/');
168
return i > 0 ? name.replace('/', '.').substring(0, i) : "";
169
}
170
171
public Optional<Module> findModule(String name) {
172
Objects.requireNonNull(name);
173
Module m = nameToModule.get(name);
174
return m!= null ? Optional.of(m) : Optional.empty();
175
176
}
177
178
public Optional<ModuleDescriptor> findModuleDescriptor(String name) {
179
Objects.requireNonNull(name);
180
Module m = nameToModule.get(name);
181
return m!= null ? Optional.of(m.descriptor()) : Optional.empty();
182
}
183
184
public static boolean isToken(String name) {
185
return ALL_MODULE_PATH.equals(name) ||
186
ALL_DEFAULT.equals(name) ||
187
ALL_SYSTEM.equals(name);
188
}
189
190
/**
191
* Returns the list of packages that split between resolved module and
192
* unnamed module
193
*/
194
public Map<String, Set<String>> splitPackages() {
195
Set<String> splitPkgs = packageToModule.keySet().stream()
196
.filter(packageToUnnamedModule::containsKey)
197
.collect(toSet());
198
if (splitPkgs.isEmpty())
199
return Collections.emptyMap();
200
201
return splitPkgs.stream().collect(toMap(Function.identity(), (pn) -> {
202
Set<String> sources = new LinkedHashSet<>();
203
sources.add(packageToModule.get(pn).getModule().location().toString());
204
packageToUnnamedModule.get(pn).stream()
205
.map(Archive::getPathName)
206
.forEach(sources::add);
207
return sources;
208
}));
209
}
210
211
/**
212
* Returns an optional archive containing the given Location
213
*/
214
public Optional<Archive> findClass(Dependency.Location location) {
215
String name = location.getName();
216
int i = name.lastIndexOf('/');
217
String pn = i > 0 ? name.substring(0, i).replace('/', '.') : "";
218
Archive archive = packageToModule.get(pn);
219
if (archive != null) {
220
return archive.contains(name + ".class")
221
? Optional.of(archive)
222
: Optional.empty();
223
}
224
225
if (packageToUnnamedModule.containsKey(pn)) {
226
return packageToUnnamedModule.get(pn).stream()
227
.filter(a -> a.contains(name + ".class"))
228
.findFirst();
229
}
230
return Optional.empty();
231
}
232
233
/**
234
* Returns the list of Modules that can be found in the specified
235
* module paths.
236
*/
237
public Map<String, Module> getModules() {
238
return nameToModule;
239
}
240
241
/**
242
* Returns Configuration with the given roots
243
*/
244
public Configuration resolve(Set<String> roots) {
245
if (roots.isEmpty())
246
throw new IllegalArgumentException("empty roots");
247
248
return Configuration.empty()
249
.resolve(finder, ModuleFinder.of(), roots);
250
}
251
252
public List<Archive> classPathArchives() {
253
return classpathArchives;
254
}
255
256
public List<Archive> initialArchives() {
257
return initialArchives;
258
}
259
260
public Set<Module> rootModules() {
261
return rootModules;
262
}
263
264
public Module toModule(ModuleReference mref) {
265
try {
266
String mn = mref.descriptor().name();
267
URI location = mref.location().orElseThrow(FileNotFoundException::new);
268
ModuleDescriptor md = mref.descriptor();
269
// is this module from the system module path?
270
URI loc = system.find(mn).flatMap(ModuleReference::location).orElse(null);
271
boolean isSystem = location.equals(loc);
272
273
final ClassFileReader reader;
274
if (location.getScheme().equals("jrt")) {
275
reader = system.getClassReader(mn);
276
} else {
277
reader = ClassFileReader.newInstance(Paths.get(location), version);
278
}
279
Module.Builder builder = new Module.Builder(md, isSystem);
280
builder.classes(reader);
281
builder.location(location);
282
283
return builder.build();
284
} catch (IOException e) {
285
throw new UncheckedIOException(e);
286
}
287
}
288
289
public Runtime.Version getVersion() {
290
return version;
291
}
292
293
/*
294
* Close all archives e.g. JarFile
295
*/
296
@Override
297
public void close() throws IOException {
298
for (Archive archive : initialArchives)
299
archive.close();
300
for (Archive archive : classpathArchives)
301
archive.close();
302
for (Module module : nameToModule.values())
303
module.close();
304
}
305
306
static class SystemModuleFinder implements ModuleFinder {
307
private static final String JAVA_HOME = System.getProperty("java.home");
308
309
private final FileSystem fileSystem;
310
private final Path root;
311
private final Map<String, ModuleReference> systemModules;
312
313
SystemModuleFinder() {
314
if (Files.isRegularFile(Paths.get(JAVA_HOME, "lib", "modules"))) {
315
// jrt file system
316
this.fileSystem = FileSystems.getFileSystem(URI.create("jrt:/"));
317
this.root = fileSystem.getPath("/modules");
318
this.systemModules = walk(root);
319
} else {
320
// exploded image
321
this.fileSystem = FileSystems.getDefault();
322
root = Paths.get(JAVA_HOME, "modules");
323
this.systemModules = ModuleFinder.ofSystem().findAll().stream()
324
.collect(toMap(mref -> mref.descriptor().name(), Function.identity()));
325
}
326
}
327
328
SystemModuleFinder(String javaHome) throws IOException {
329
if (javaHome == null) {
330
// --system none
331
this.fileSystem = null;
332
this.root = null;
333
this.systemModules = Collections.emptyMap();
334
} else {
335
if (!Files.isRegularFile(Paths.get(javaHome, "lib", "modules")))
336
throw new IllegalArgumentException("Invalid java.home: " + javaHome);
337
338
// alternate java.home
339
Map<String, String> env = new HashMap<>();
340
env.put("java.home", javaHome);
341
// a remote run-time image
342
this.fileSystem = FileSystems.newFileSystem(URI.create("jrt:/"), env);
343
this.root = fileSystem.getPath("/modules");
344
this.systemModules = walk(root);
345
}
346
}
347
348
private Map<String, ModuleReference> walk(Path root) {
349
try (Stream<Path> stream = Files.walk(root, 1)) {
350
return stream.filter(path -> !path.equals(root))
351
.map(this::toModuleReference)
352
.collect(toMap(mref -> mref.descriptor().name(),
353
Function.identity()));
354
} catch (IOException e) {
355
throw new UncheckedIOException(e);
356
}
357
}
358
359
private ModuleReference toModuleReference(Path path) {
360
Path minfo = path.resolve(MODULE_INFO);
361
try (InputStream in = Files.newInputStream(minfo);
362
BufferedInputStream bin = new BufferedInputStream(in)) {
363
364
ModuleDescriptor descriptor = dropHashes(ModuleDescriptor.read(bin));
365
String mn = descriptor.name();
366
URI uri = URI.create("jrt:/" + path.getFileName().toString());
367
Supplier<ModuleReader> readerSupplier = () -> new ModuleReader() {
368
@Override
369
public Optional<URI> find(String name) throws IOException {
370
return name.equals(mn)
371
? Optional.of(uri) : Optional.empty();
372
}
373
374
@Override
375
public Stream<String> list() {
376
return Stream.empty();
377
}
378
379
@Override
380
public void close() {
381
}
382
};
383
384
return new ModuleReference(descriptor, uri) {
385
@Override
386
public ModuleReader open() {
387
return readerSupplier.get();
388
}
389
};
390
} catch (IOException e) {
391
throw new UncheckedIOException(e);
392
}
393
}
394
395
private ModuleDescriptor dropHashes(ModuleDescriptor md) {
396
ModuleDescriptor.Builder builder = ModuleDescriptor.newModule(md.name());
397
md.requires().forEach(builder::requires);
398
md.exports().forEach(builder::exports);
399
md.opens().forEach(builder::opens);
400
md.provides().stream().forEach(builder::provides);
401
md.uses().stream().forEach(builder::uses);
402
builder.packages(md.packages());
403
return builder.build();
404
}
405
406
@Override
407
public Set<ModuleReference> findAll() {
408
return systemModules.values().stream().collect(toSet());
409
}
410
411
@Override
412
public Optional<ModuleReference> find(String mn) {
413
return systemModules.containsKey(mn)
414
? Optional.of(systemModules.get(mn)) : Optional.empty();
415
}
416
417
public Stream<String> moduleNames() {
418
return systemModules.values().stream()
419
.map(mref -> mref.descriptor().name());
420
}
421
422
public ClassFileReader getClassReader(String modulename) throws IOException {
423
Path mp = root.resolve(modulename);
424
if (Files.exists(mp) && Files.isDirectory(mp)) {
425
return ClassFileReader.newInstance(fileSystem, mp);
426
} else {
427
throw new FileNotFoundException(mp.toString());
428
}
429
}
430
431
public Set<String> defaultSystemRoots() {
432
return systemModules.values().stream()
433
.map(ModuleReference::descriptor)
434
.filter(descriptor -> descriptor.exports()
435
.stream()
436
.filter(e -> !e.isQualified())
437
.findAny()
438
.isPresent())
439
.map(ModuleDescriptor::name)
440
.collect(Collectors.toSet());
441
}
442
}
443
444
public static class Builder {
445
446
final SystemModuleFinder systemModulePath;
447
final Set<String> rootModules = new HashSet<>();
448
final List<Archive> initialArchives = new ArrayList<>();
449
final List<Path> paths = new ArrayList<>();
450
final List<Path> classPaths = new ArrayList<>();
451
final Set<String> tokens = new HashSet<>();
452
453
ModuleFinder upgradeModulePath;
454
ModuleFinder appModulePath;
455
Runtime.Version version;
456
457
public Builder() {
458
this.systemModulePath = new SystemModuleFinder();
459
}
460
461
public Builder(String javaHome) throws IOException {
462
this.systemModulePath = SystemModuleFinder.JAVA_HOME.equals(javaHome)
463
? new SystemModuleFinder()
464
: new SystemModuleFinder(javaHome);
465
}
466
467
public Builder upgradeModulePath(String upgradeModulePath) {
468
this.upgradeModulePath = createModulePathFinder(upgradeModulePath);
469
return this;
470
}
471
472
public Builder appModulePath(String modulePath) {
473
this.appModulePath = createModulePathFinder(modulePath);
474
return this;
475
}
476
477
public Builder addmods(Set<String> addmods) {
478
for (String mn : addmods) {
479
if (isToken(mn)) {
480
tokens.add(mn);
481
} else {
482
rootModules.add(mn);
483
}
484
}
485
return this;
486
}
487
488
public Builder multiRelease(Runtime.Version version) {
489
this.version = version;
490
return this;
491
}
492
493
public Builder addRoot(Path path) {
494
Archive archive = Archive.getInstance(path, version);
495
if (archive.contains(MODULE_INFO)) {
496
paths.add(path);
497
} else {
498
initialArchives.add(archive);
499
}
500
return this;
501
}
502
503
public Builder addClassPath(String classPath) {
504
this.classPaths.addAll(getClassPaths(classPath));
505
return this;
506
}
507
508
public JdepsConfiguration build() throws IOException {
509
ModuleFinder finder = systemModulePath;
510
if (upgradeModulePath != null) {
511
finder = ModuleFinder.compose(upgradeModulePath, systemModulePath);
512
}
513
if (appModulePath != null) {
514
finder = ModuleFinder.compose(finder, appModulePath);
515
}
516
if (!paths.isEmpty()) {
517
ModuleFinder otherModulePath = ModuleFinder.of(paths.toArray(new Path[0]));
518
519
finder = ModuleFinder.compose(finder, otherModulePath);
520
// add modules specified on command-line (convenience) as root set
521
otherModulePath.findAll().stream()
522
.map(mref -> mref.descriptor().name())
523
.forEach(rootModules::add);
524
}
525
526
// no archive is specified for analysis
527
// add all system modules as root if --add-modules ALL-SYSTEM is specified
528
if (tokens.contains(ALL_SYSTEM) && rootModules.isEmpty() &&
529
initialArchives.isEmpty() && classPaths.isEmpty()) {
530
systemModulePath.findAll()
531
.stream()
532
.map(mref -> mref.descriptor().name())
533
.forEach(rootModules::add);
534
}
535
536
// add all modules on app module path as roots if ALL-MODULE-PATH is specified
537
if ((tokens.contains(ALL_MODULE_PATH)) && appModulePath != null) {
538
appModulePath.findAll().stream()
539
.map(mref -> mref.descriptor().name())
540
.forEach(rootModules::add);
541
}
542
543
544
// build root set for module resolution
545
Set<String> mods = new HashSet<>(rootModules);
546
// if archives are specified for analysis, then consider as unnamed module
547
boolean unnamed = !initialArchives.isEmpty() || !classPaths.isEmpty();
548
if (tokens.contains(ALL_DEFAULT)) {
549
mods.addAll(systemModulePath.defaultSystemRoots());
550
} else if (tokens.contains(ALL_SYSTEM) || unnamed) {
551
// resolve all system modules as unnamed module may reference any class
552
systemModulePath.findAll().stream()
553
.map(mref -> mref.descriptor().name())
554
.forEach(mods::add);
555
}
556
if (unnamed && appModulePath != null) {
557
// resolve all modules on module path as unnamed module may reference any class
558
appModulePath.findAll().stream()
559
.map(mref -> mref.descriptor().name())
560
.forEach(mods::add);
561
}
562
563
// resolve the module graph
564
Configuration config = Configuration.empty().resolve(finder, ModuleFinder.of(), mods);
565
return new JdepsConfiguration(config,
566
systemModulePath,
567
finder,
568
rootModules,
569
classPaths,
570
initialArchives,
571
version);
572
}
573
574
private static ModuleFinder createModulePathFinder(String mpaths) {
575
if (mpaths == null) {
576
return null;
577
} else {
578
String[] dirs = mpaths.split(File.pathSeparator);
579
Path[] paths = new Path[dirs.length];
580
int i = 0;
581
for (String dir : dirs) {
582
paths[i++] = Paths.get(dir);
583
}
584
return ModuleFinder.of(paths);
585
}
586
}
587
588
/*
589
* Returns the list of Archive specified in cpaths and not included
590
* initialArchives
591
*/
592
private List<Path> getClassPaths(String cpaths) {
593
if (cpaths.isEmpty()) {
594
return Collections.emptyList();
595
}
596
List<Path> paths = new ArrayList<>();
597
for (String p : cpaths.split(File.pathSeparator)) {
598
if (p.length() > 0) {
599
// wildcard to parse all JAR files e.g. -classpath dir/*
600
int i = p.lastIndexOf(".*");
601
if (i > 0) {
602
Path dir = Paths.get(p.substring(0, i));
603
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.jar")) {
604
for (Path entry : stream) {
605
paths.add(entry);
606
}
607
} catch (IOException e) {
608
throw new UncheckedIOException(e);
609
}
610
} else {
611
paths.add(Paths.get(p));
612
}
613
}
614
}
615
return paths;
616
}
617
}
618
619
}
620
621