Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.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
26
package jdk.internal.module;
27
28
import java.io.File;
29
import java.io.PrintStream;
30
import java.lang.module.Configuration;
31
import java.lang.module.ModuleDescriptor;
32
import java.lang.module.ModuleFinder;
33
import java.lang.module.ModuleReference;
34
import java.lang.module.ResolvedModule;
35
import java.net.URI;
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.Iterator;
42
import java.util.LinkedHashMap;
43
import java.util.List;
44
import java.util.Map;
45
import java.util.Objects;
46
import java.util.Optional;
47
import java.util.Set;
48
import java.util.function.Function;
49
import java.util.stream.Collectors;
50
51
import jdk.internal.access.JavaLangAccess;
52
import jdk.internal.access.JavaLangModuleAccess;
53
import jdk.internal.access.SharedSecrets;
54
import jdk.internal.loader.BootLoader;
55
import jdk.internal.loader.BuiltinClassLoader;
56
import jdk.internal.loader.ClassLoaders;
57
import jdk.internal.misc.CDS;
58
import jdk.internal.perf.PerfCounter;
59
60
/**
61
* Initializes/boots the module system.
62
*
63
* The {@link #boot() boot} method is called early in the startup to initialize
64
* the module system. In summary, the boot method creates a Configuration by
65
* resolving a set of module names specified via the launcher (or equivalent)
66
* -m and --add-modules options. The modules are located on a module path that
67
* is constructed from the upgrade module path, system modules, and application
68
* module path. The Configuration is instantiated as the boot layer with each
69
* module in the configuration defined to a class loader.
70
*/
71
72
public final class ModuleBootstrap {
73
private ModuleBootstrap() { }
74
75
private static final String JAVA_BASE = "java.base";
76
77
// the token for "all default modules"
78
private static final String ALL_DEFAULT = "ALL-DEFAULT";
79
80
// the token for "all unnamed modules"
81
private static final String ALL_UNNAMED = "ALL-UNNAMED";
82
83
// the token for "all system modules"
84
private static final String ALL_SYSTEM = "ALL-SYSTEM";
85
86
// the token for "all modules on the module path"
87
private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
88
89
// access to java.lang/module
90
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
91
private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
92
93
// The ModulePatcher for the initial configuration
94
private static final ModulePatcher patcher = initModulePatcher();
95
96
/**
97
* Returns the ModulePatcher for the initial configuration.
98
*/
99
public static ModulePatcher patcher() {
100
return patcher;
101
}
102
103
// ModuleFinders for the initial configuration
104
private static volatile ModuleFinder unlimitedFinder;
105
private static volatile ModuleFinder limitedFinder;
106
107
/**
108
* Returns the ModuleFinder for the initial configuration before
109
* observability is limited by the --limit-modules command line option.
110
*
111
* @apiNote Used to support locating modules {@code java.instrument} and
112
* {@code jdk.management.agent} modules when they are loaded dynamically.
113
*/
114
public static ModuleFinder unlimitedFinder() {
115
ModuleFinder finder = unlimitedFinder;
116
if (finder == null) {
117
return ModuleFinder.ofSystem();
118
} else {
119
return finder;
120
}
121
}
122
123
/**
124
* Returns the ModuleFinder for the initial configuration.
125
*
126
* @apiNote Used to support "{@code java --list-modules}".
127
*/
128
public static ModuleFinder limitedFinder() {
129
ModuleFinder finder = limitedFinder;
130
if (finder == null) {
131
return unlimitedFinder();
132
} else {
133
return finder;
134
}
135
}
136
137
/**
138
* Returns true if the archived boot layer can be used. The system properties
139
* are checked in the order that they are used by boot2.
140
*/
141
private static boolean canUseArchivedBootLayer() {
142
return getProperty("jdk.module.upgrade.path") == null &&
143
getProperty("jdk.module.path") == null &&
144
getProperty("jdk.module.patch.0") == null && // --patch-module
145
getProperty("jdk.module.main") == null && // --module
146
getProperty("jdk.module.addmods.0") == null && // --add-modules
147
getProperty("jdk.module.limitmods") == null && // --limit-modules
148
getProperty("jdk.module.addreads.0") == null && // --add-reads
149
getProperty("jdk.module.addexports.0") == null && // --add-exports
150
getProperty("jdk.module.addopens.0") == null; // --add-opens
151
}
152
153
/**
154
* Initialize the module system, returning the boot layer. The boot layer
155
* is obtained from the CDS archive if possible, otherwise it is generated
156
* from the module graph.
157
*
158
* @see java.lang.System#initPhase2(boolean, boolean)
159
*/
160
public static ModuleLayer boot() {
161
Counters.start();
162
163
ModuleLayer bootLayer;
164
ArchivedBootLayer archivedBootLayer = ArchivedBootLayer.get();
165
if (archivedBootLayer != null) {
166
assert canUseArchivedBootLayer();
167
bootLayer = archivedBootLayer.bootLayer();
168
BootLoader.getUnnamedModule(); // trigger <clinit> of BootLoader.
169
CDS.defineArchivedModules(ClassLoaders.platformClassLoader(), ClassLoaders.appClassLoader());
170
171
// assume boot layer has at least one module providing a service
172
// that is mapped to the application class loader.
173
JLA.bindToLoader(bootLayer, ClassLoaders.appClassLoader());
174
} else {
175
bootLayer = boot2();
176
}
177
178
Counters.publish("jdk.module.boot.totalTime");
179
return bootLayer;
180
}
181
182
private static ModuleLayer boot2() {
183
// Step 0: Command line options
184
185
ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path");
186
ModuleFinder appModulePath = finderFor("jdk.module.path");
187
boolean isPatched = patcher.hasPatches();
188
String mainModule = System.getProperty("jdk.module.main");
189
Set<String> addModules = addModules();
190
Set<String> limitModules = limitModules();
191
192
PrintStream traceOutput = null;
193
String trace = getAndRemoveProperty("jdk.module.showModuleResolution");
194
if (trace != null && Boolean.parseBoolean(trace))
195
traceOutput = System.out;
196
197
Counters.add("jdk.module.boot.0.commandLineTime");
198
199
// Step 1: The observable system modules, either all system modules
200
// or the system modules pre-generated for the initial module (the
201
// initial module may be the unnamed module). If the system modules
202
// are pre-generated for the initial module then resolution can be
203
// skipped.
204
205
SystemModules systemModules = null;
206
ModuleFinder systemModuleFinder;
207
208
boolean haveModulePath = (appModulePath != null || upgradeModulePath != null);
209
boolean needResolution = true;
210
boolean canArchive = false;
211
boolean hasSplitPackages;
212
boolean hasIncubatorModules;
213
214
// If the java heap was archived at CDS dump time and the environment
215
// at dump time matches the current environment then use the archived
216
// system modules and finder.
217
ArchivedModuleGraph archivedModuleGraph = ArchivedModuleGraph.get(mainModule);
218
if (archivedModuleGraph != null
219
&& !haveModulePath
220
&& addModules.isEmpty()
221
&& limitModules.isEmpty()
222
&& !isPatched) {
223
systemModuleFinder = archivedModuleGraph.finder();
224
hasSplitPackages = archivedModuleGraph.hasSplitPackages();
225
hasIncubatorModules = archivedModuleGraph.hasIncubatorModules();
226
needResolution = (traceOutput != null);
227
} else {
228
if (!haveModulePath && addModules.isEmpty() && limitModules.isEmpty()) {
229
systemModules = SystemModuleFinders.systemModules(mainModule);
230
if (systemModules != null && !isPatched) {
231
needResolution = (traceOutput != null);
232
canArchive = true;
233
}
234
}
235
if (systemModules == null) {
236
// all system modules are observable
237
systemModules = SystemModuleFinders.allSystemModules();
238
}
239
if (systemModules != null) {
240
// images build
241
systemModuleFinder = SystemModuleFinders.of(systemModules);
242
} else {
243
// exploded build or testing
244
systemModules = new ExplodedSystemModules();
245
systemModuleFinder = SystemModuleFinders.ofSystem();
246
}
247
248
hasSplitPackages = systemModules.hasSplitPackages();
249
hasIncubatorModules = systemModules.hasIncubatorModules();
250
// not using the archived module graph - avoid accidental use
251
archivedModuleGraph = null;
252
}
253
254
Counters.add("jdk.module.boot.1.systemModulesTime");
255
256
// Step 2: Define and load java.base. This patches all classes loaded
257
// to date so that they are members of java.base. Once java.base is
258
// loaded then resources in java.base are available for error messages
259
// needed from here on.
260
261
ModuleReference base = systemModuleFinder.find(JAVA_BASE).orElse(null);
262
if (base == null)
263
throw new InternalError(JAVA_BASE + " not found");
264
URI baseUri = base.location().orElse(null);
265
if (baseUri == null)
266
throw new InternalError(JAVA_BASE + " does not have a location");
267
BootLoader.loadModule(base);
268
269
Module baseModule = Modules.defineModule(null, base.descriptor(), baseUri);
270
JLA.addEnableNativeAccess(baseModule);
271
272
// Step 2a: Scan all modules when --validate-modules specified
273
274
if (getAndRemoveProperty("jdk.module.validation") != null) {
275
int errors = ModulePathValidator.scanAllModules(System.out);
276
if (errors > 0) {
277
fail("Validation of module path failed");
278
}
279
}
280
281
Counters.add("jdk.module.boot.2.defineBaseTime");
282
283
// Step 3: If resolution is needed then create the module finder and
284
// the set of root modules to resolve.
285
286
ModuleFinder savedModuleFinder = null;
287
ModuleFinder finder;
288
Set<String> roots;
289
if (needResolution) {
290
291
// upgraded modules override the modules in the run-time image
292
if (upgradeModulePath != null)
293
systemModuleFinder = ModuleFinder.compose(upgradeModulePath,
294
systemModuleFinder);
295
296
// The module finder: [--upgrade-module-path] system [--module-path]
297
if (appModulePath != null) {
298
finder = ModuleFinder.compose(systemModuleFinder, appModulePath);
299
} else {
300
finder = systemModuleFinder;
301
}
302
303
// The root modules to resolve
304
roots = new HashSet<>();
305
306
// launcher -m option to specify the main/initial module
307
if (mainModule != null)
308
roots.add(mainModule);
309
310
// additional module(s) specified by --add-modules
311
boolean addAllDefaultModules = false;
312
boolean addAllSystemModules = false;
313
boolean addAllApplicationModules = false;
314
for (String mod : addModules) {
315
switch (mod) {
316
case ALL_DEFAULT:
317
addAllDefaultModules = true;
318
break;
319
case ALL_SYSTEM:
320
addAllSystemModules = true;
321
break;
322
case ALL_MODULE_PATH:
323
addAllApplicationModules = true;
324
break;
325
default:
326
roots.add(mod);
327
}
328
}
329
330
// --limit-modules
331
savedModuleFinder = finder;
332
if (!limitModules.isEmpty()) {
333
finder = limitFinder(finder, limitModules, roots);
334
}
335
336
// If there is no initial module specified then assume that the initial
337
// module is the unnamed module of the application class loader. This
338
// is implemented by resolving all observable modules that export an
339
// API. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT bit set in
340
// their ModuleResolution attribute flags are excluded from the
341
// default set of roots.
342
if (mainModule == null || addAllDefaultModules) {
343
roots.addAll(DefaultRoots.compute(systemModuleFinder, finder));
344
}
345
346
// If `--add-modules ALL-SYSTEM` is specified then all observable system
347
// modules will be resolved.
348
if (addAllSystemModules) {
349
ModuleFinder f = finder; // observable modules
350
systemModuleFinder.findAll()
351
.stream()
352
.map(ModuleReference::descriptor)
353
.map(ModuleDescriptor::name)
354
.filter(mn -> f.find(mn).isPresent()) // observable
355
.forEach(mn -> roots.add(mn));
356
}
357
358
// If `--add-modules ALL-MODULE-PATH` is specified then all observable
359
// modules on the application module path will be resolved.
360
if (appModulePath != null && addAllApplicationModules) {
361
ModuleFinder f = finder; // observable modules
362
appModulePath.findAll()
363
.stream()
364
.map(ModuleReference::descriptor)
365
.map(ModuleDescriptor::name)
366
.filter(mn -> f.find(mn).isPresent()) // observable
367
.forEach(mn -> roots.add(mn));
368
}
369
} else {
370
// no resolution case
371
finder = systemModuleFinder;
372
roots = null;
373
}
374
375
Counters.add("jdk.module.boot.3.optionsAndRootsTime");
376
377
// Step 4: Resolve the root modules, with service binding, to create
378
// the configuration for the boot layer. If resolution is not needed
379
// then create the configuration for the boot layer from the
380
// readability graph created at link time.
381
382
Configuration cf;
383
if (needResolution) {
384
cf = Modules.newBootLayerConfiguration(finder, roots, traceOutput);
385
} else {
386
if (archivedModuleGraph != null) {
387
cf = archivedModuleGraph.configuration();
388
} else {
389
Map<String, Set<String>> map = systemModules.moduleReads();
390
cf = JLMA.newConfiguration(systemModuleFinder, map);
391
}
392
}
393
394
// check that modules specified to --patch-module are resolved
395
if (isPatched) {
396
patcher.patchedModules()
397
.stream()
398
.filter(mn -> !cf.findModule(mn).isPresent())
399
.forEach(mn -> warnUnknownModule(PATCH_MODULE, mn));
400
}
401
402
Counters.add("jdk.module.boot.4.resolveTime");
403
404
// Step 5: Map the modules in the configuration to class loaders.
405
// The static configuration provides the mapping of standard and JDK
406
// modules to the boot and platform loaders. All other modules (JDK
407
// tool modules, and both explicit and automatic modules on the
408
// application module path) are defined to the application class
409
// loader.
410
411
// mapping of modules to class loaders
412
Function<String, ClassLoader> clf;
413
if (archivedModuleGraph != null) {
414
clf = archivedModuleGraph.classLoaderFunction();
415
} else {
416
clf = ModuleLoaderMap.mappingFunction(cf);
417
}
418
419
// check that all modules to be mapped to the boot loader will be
420
// loaded from the runtime image
421
if (haveModulePath) {
422
for (ResolvedModule resolvedModule : cf.modules()) {
423
ModuleReference mref = resolvedModule.reference();
424
String name = mref.descriptor().name();
425
ClassLoader cl = clf.apply(name);
426
if (cl == null) {
427
if (upgradeModulePath != null
428
&& upgradeModulePath.find(name).isPresent())
429
fail(name + ": cannot be loaded from upgrade module path");
430
if (!systemModuleFinder.find(name).isPresent())
431
fail(name + ": cannot be loaded from application module path");
432
}
433
}
434
}
435
436
// check for split packages in the modules mapped to the built-in loaders
437
if (hasSplitPackages || isPatched || haveModulePath) {
438
checkSplitPackages(cf, clf);
439
}
440
441
// load/register the modules with the built-in class loaders
442
loadModules(cf, clf);
443
Counters.add("jdk.module.boot.5.loadModulesTime");
444
445
// Step 6: Define all modules to the VM
446
447
ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf);
448
Counters.add("jdk.module.boot.6.layerCreateTime");
449
450
// Step 7: Miscellaneous
451
452
// check incubating status
453
if (hasIncubatorModules || haveModulePath) {
454
checkIncubatingStatus(cf);
455
}
456
457
// --add-reads, --add-exports/--add-opens
458
addExtraReads(bootLayer);
459
boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer);
460
461
// add enable native access
462
addEnableNativeAccess(bootLayer);
463
464
Counters.add("jdk.module.boot.7.adjustModulesTime");
465
466
// save module finders for later use
467
if (savedModuleFinder != null) {
468
unlimitedFinder = new SafeModuleFinder(savedModuleFinder);
469
if (savedModuleFinder != finder)
470
limitedFinder = new SafeModuleFinder(finder);
471
}
472
473
// Archive module graph and boot layer can be archived at CDS dump time.
474
// Only allow the unnamed module case for now.
475
if (canArchive && (mainModule == null)) {
476
ArchivedModuleGraph.archive(hasSplitPackages,
477
hasIncubatorModules,
478
systemModuleFinder,
479
cf,
480
clf);
481
if (!hasSplitPackages && !hasIncubatorModules) {
482
ArchivedBootLayer.archive(bootLayer);
483
}
484
}
485
486
return bootLayer;
487
}
488
489
/**
490
* Load/register the modules to the built-in class loaders.
491
*/
492
private static void loadModules(Configuration cf,
493
Function<String, ClassLoader> clf) {
494
for (ResolvedModule resolvedModule : cf.modules()) {
495
ModuleReference mref = resolvedModule.reference();
496
String name = resolvedModule.name();
497
ClassLoader loader = clf.apply(name);
498
if (loader == null) {
499
// skip java.base as it is already loaded
500
if (!name.equals(JAVA_BASE)) {
501
BootLoader.loadModule(mref);
502
}
503
} else if (loader instanceof BuiltinClassLoader) {
504
((BuiltinClassLoader) loader).loadModule(mref);
505
}
506
}
507
}
508
509
/**
510
* Checks for split packages between modules defined to the built-in class
511
* loaders.
512
*/
513
private static void checkSplitPackages(Configuration cf,
514
Function<String, ClassLoader> clf) {
515
Map<String, String> packageToModule = new HashMap<>();
516
for (ResolvedModule resolvedModule : cf.modules()) {
517
ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
518
String name = descriptor.name();
519
ClassLoader loader = clf.apply(name);
520
if (loader == null || loader instanceof BuiltinClassLoader) {
521
for (String p : descriptor.packages()) {
522
String other = packageToModule.putIfAbsent(p, name);
523
if (other != null) {
524
String msg = "Package " + p + " in both module "
525
+ name + " and module " + other;
526
throw new LayerInstantiationException(msg);
527
}
528
}
529
}
530
}
531
}
532
533
/**
534
* Returns a ModuleFinder that limits observability to the given root
535
* modules, their transitive dependences, plus a set of other modules.
536
*/
537
private static ModuleFinder limitFinder(ModuleFinder finder,
538
Set<String> roots,
539
Set<String> otherMods)
540
{
541
// resolve all root modules
542
Configuration cf = Configuration.empty().resolve(finder,
543
ModuleFinder.of(),
544
roots);
545
546
// module name -> reference
547
Map<String, ModuleReference> map = new HashMap<>();
548
549
// root modules and their transitive dependences
550
cf.modules().stream()
551
.map(ResolvedModule::reference)
552
.forEach(mref -> map.put(mref.descriptor().name(), mref));
553
554
// additional modules
555
otherMods.stream()
556
.map(finder::find)
557
.flatMap(Optional::stream)
558
.forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));
559
560
// set of modules that are observable
561
Set<ModuleReference> mrefs = new HashSet<>(map.values());
562
563
return new ModuleFinder() {
564
@Override
565
public Optional<ModuleReference> find(String name) {
566
return Optional.ofNullable(map.get(name));
567
}
568
@Override
569
public Set<ModuleReference> findAll() {
570
return mrefs;
571
}
572
};
573
}
574
575
/**
576
* Creates a finder from the module path that is the value of the given
577
* system property and optionally patched by --patch-module
578
*/
579
private static ModuleFinder finderFor(String prop) {
580
String s = System.getProperty(prop);
581
if (s == null) {
582
return null;
583
} else {
584
String[] dirs = s.split(File.pathSeparator);
585
Path[] paths = new Path[dirs.length];
586
int i = 0;
587
for (String dir: dirs) {
588
paths[i++] = Path.of(dir);
589
}
590
return ModulePath.of(patcher, paths);
591
}
592
}
593
594
/**
595
* Initialize the module patcher for the initial configuration passed on the
596
* value of the --patch-module options.
597
*/
598
private static ModulePatcher initModulePatcher() {
599
Map<String, List<String>> map = decode("jdk.module.patch.",
600
File.pathSeparator,
601
false);
602
return new ModulePatcher(map);
603
}
604
605
/**
606
* Returns the set of module names specified by --add-module options.
607
*/
608
private static Set<String> addModules() {
609
String prefix = "jdk.module.addmods.";
610
int index = 0;
611
// the system property is removed after decoding
612
String value = getAndRemoveProperty(prefix + index);
613
if (value == null) {
614
return Set.of();
615
} else {
616
Set<String> modules = new HashSet<>();
617
while (value != null) {
618
for (String s : value.split(",")) {
619
if (!s.isEmpty())
620
modules.add(s);
621
}
622
index++;
623
value = getAndRemoveProperty(prefix + index);
624
}
625
return modules;
626
}
627
}
628
629
/**
630
* Returns the set of module names specified by --limit-modules.
631
*/
632
private static Set<String> limitModules() {
633
String value = getAndRemoveProperty("jdk.module.limitmods");
634
if (value == null) {
635
return Set.of();
636
} else {
637
Set<String> names = new HashSet<>();
638
for (String name : value.split(",")) {
639
if (name.length() > 0) names.add(name);
640
}
641
return names;
642
}
643
}
644
645
/**
646
* Process the --add-reads options to add any additional read edges that
647
* are specified on the command-line.
648
*/
649
private static void addExtraReads(ModuleLayer bootLayer) {
650
651
// decode the command line options
652
Map<String, List<String>> map = decode("jdk.module.addreads.");
653
if (map.isEmpty())
654
return;
655
656
for (Map.Entry<String, List<String>> e : map.entrySet()) {
657
658
// the key is $MODULE
659
String mn = e.getKey();
660
Optional<Module> om = bootLayer.findModule(mn);
661
if (!om.isPresent()) {
662
warnUnknownModule(ADD_READS, mn);
663
continue;
664
}
665
Module m = om.get();
666
667
// the value is the set of other modules (by name)
668
for (String name : e.getValue()) {
669
if (ALL_UNNAMED.equals(name)) {
670
Modules.addReadsAllUnnamed(m);
671
} else {
672
om = bootLayer.findModule(name);
673
if (om.isPresent()) {
674
Modules.addReads(m, om.get());
675
} else {
676
warnUnknownModule(ADD_READS, name);
677
}
678
}
679
}
680
}
681
}
682
683
/**
684
* Process the --add-exports and --add-opens options to export/open
685
* additional packages specified on the command-line.
686
*/
687
private static boolean addExtraExportsAndOpens(ModuleLayer bootLayer) {
688
boolean extraExportsOrOpens = false;
689
690
// --add-exports
691
String prefix = "jdk.module.addexports.";
692
Map<String, List<String>> extraExports = decode(prefix);
693
if (!extraExports.isEmpty()) {
694
addExtraExportsOrOpens(bootLayer, extraExports, false);
695
extraExportsOrOpens = true;
696
}
697
698
699
// --add-opens
700
prefix = "jdk.module.addopens.";
701
Map<String, List<String>> extraOpens = decode(prefix);
702
if (!extraOpens.isEmpty()) {
703
addExtraExportsOrOpens(bootLayer, extraOpens, true);
704
extraExportsOrOpens = true;
705
}
706
707
return extraExportsOrOpens;
708
}
709
710
private static void addExtraExportsOrOpens(ModuleLayer bootLayer,
711
Map<String, List<String>> map,
712
boolean opens)
713
{
714
String option = opens ? ADD_OPENS : ADD_EXPORTS;
715
for (Map.Entry<String, List<String>> e : map.entrySet()) {
716
717
// the key is $MODULE/$PACKAGE
718
String key = e.getKey();
719
String[] s = key.split("/");
720
if (s.length != 2)
721
fail(unableToParse(option, "<module>/<package>", key));
722
723
String mn = s[0];
724
String pn = s[1];
725
if (mn.isEmpty() || pn.isEmpty())
726
fail(unableToParse(option, "<module>/<package>", key));
727
728
// The exporting module is in the boot layer
729
Module m;
730
Optional<Module> om = bootLayer.findModule(mn);
731
if (!om.isPresent()) {
732
warnUnknownModule(option, mn);
733
continue;
734
}
735
736
m = om.get();
737
738
if (!m.getDescriptor().packages().contains(pn)) {
739
warn("package " + pn + " not in " + mn);
740
continue;
741
}
742
743
// the value is the set of modules to export to (by name)
744
for (String name : e.getValue()) {
745
boolean allUnnamed = false;
746
Module other = null;
747
if (ALL_UNNAMED.equals(name)) {
748
allUnnamed = true;
749
} else {
750
om = bootLayer.findModule(name);
751
if (om.isPresent()) {
752
other = om.get();
753
} else {
754
warnUnknownModule(option, name);
755
continue;
756
}
757
}
758
if (allUnnamed) {
759
if (opens) {
760
Modules.addOpensToAllUnnamed(m, pn);
761
} else {
762
Modules.addExportsToAllUnnamed(m, pn);
763
}
764
} else {
765
if (opens) {
766
Modules.addOpens(m, pn, other);
767
} else {
768
Modules.addExports(m, pn, other);
769
}
770
}
771
}
772
}
773
}
774
775
/**
776
* Process the --enable-native-access option to grant access to restricted methods to selected modules.
777
*/
778
private static void addEnableNativeAccess(ModuleLayer layer) {
779
for (String name : decodeEnableNativeAccess()) {
780
if (name.equals("ALL-UNNAMED")) {
781
JLA.addEnableNativeAccessAllUnnamed();
782
} else {
783
Optional<Module> module = layer.findModule(name);
784
if (module.isPresent()) {
785
JLA.addEnableNativeAccess(module.get());
786
} else {
787
warnUnknownModule(ENABLE_NATIVE_ACCESS, name);
788
}
789
}
790
}
791
}
792
793
/**
794
* Returns the set of module names specified by --enable-native-access options.
795
*/
796
private static Set<String> decodeEnableNativeAccess() {
797
String prefix = "jdk.module.enable.native.access.";
798
int index = 0;
799
// the system property is removed after decoding
800
String value = getAndRemoveProperty(prefix + index);
801
Set<String> modules = new HashSet<>();
802
if (value == null) {
803
return modules;
804
}
805
while (value != null) {
806
for (String s : value.split(",")) {
807
if (!s.isEmpty())
808
modules.add(s);
809
}
810
index++;
811
value = getAndRemoveProperty(prefix + index);
812
}
813
return modules;
814
}
815
816
/**
817
* Decodes the values of --add-reads, -add-exports, --add-opens or
818
* --patch-modules options that are encoded in system properties.
819
*
820
* @param prefix the system property prefix
821
* @praam regex the regex for splitting the RHS of the option value
822
*/
823
private static Map<String, List<String>> decode(String prefix,
824
String regex,
825
boolean allowDuplicates) {
826
int index = 0;
827
// the system property is removed after decoding
828
String value = getAndRemoveProperty(prefix + index);
829
if (value == null)
830
return Map.of();
831
832
Map<String, List<String>> map = new HashMap<>();
833
834
while (value != null) {
835
836
int pos = value.indexOf('=');
837
if (pos == -1)
838
fail(unableToParse(option(prefix), "<module>=<value>", value));
839
if (pos == 0)
840
fail(unableToParse(option(prefix), "<module>=<value>", value));
841
842
// key is <module> or <module>/<package>
843
String key = value.substring(0, pos);
844
845
String rhs = value.substring(pos+1);
846
if (rhs.isEmpty())
847
fail(unableToParse(option(prefix), "<module>=<value>", value));
848
849
// value is <module>(,<module>)* or <file>(<pathsep><file>)*
850
if (!allowDuplicates && map.containsKey(key))
851
fail(key + " specified more than once to " + option(prefix));
852
List<String> values = map.computeIfAbsent(key, k -> new ArrayList<>());
853
int ntargets = 0;
854
for (String s : rhs.split(regex)) {
855
if (!s.isEmpty()) {
856
values.add(s);
857
ntargets++;
858
}
859
}
860
if (ntargets == 0)
861
fail("Target must be specified: " + option(prefix) + " " + value);
862
863
index++;
864
value = getAndRemoveProperty(prefix + index);
865
}
866
867
return map;
868
}
869
870
/**
871
* Decodes the values of --add-reads, -add-exports or --add-opens
872
* which use the "," to separate the RHS of the option value.
873
*/
874
private static Map<String, List<String>> decode(String prefix) {
875
return decode(prefix, ",", true);
876
}
877
878
879
/**
880
* Gets the named system property
881
*/
882
private static String getProperty(String key) {
883
return System.getProperty(key);
884
}
885
886
/**
887
* Gets and remove the named system property
888
*/
889
private static String getAndRemoveProperty(String key) {
890
return (String) System.getProperties().remove(key);
891
}
892
893
/**
894
* Checks incubating status of modules in the configuration
895
*/
896
private static void checkIncubatingStatus(Configuration cf) {
897
String incubating = null;
898
for (ResolvedModule resolvedModule : cf.modules()) {
899
ModuleReference mref = resolvedModule.reference();
900
901
// emit warning if the WARN_INCUBATING module resolution bit set
902
if (ModuleResolution.hasIncubatingWarning(mref)) {
903
String mn = mref.descriptor().name();
904
if (incubating == null) {
905
incubating = mn;
906
} else {
907
incubating += ", " + mn;
908
}
909
}
910
}
911
if (incubating != null)
912
warn("Using incubator modules: " + incubating);
913
}
914
915
/**
916
* Throws a RuntimeException with the given message
917
*/
918
static void fail(String m) {
919
throw new RuntimeException(m);
920
}
921
922
static void warn(String m) {
923
System.err.println("WARNING: " + m);
924
}
925
926
static void warnUnknownModule(String option, String mn) {
927
warn("Unknown module: " + mn + " specified to " + option);
928
}
929
930
static String unableToParse(String option, String text, String value) {
931
return "Unable to parse " + option + " " + text + ": " + value;
932
}
933
934
private static final String ADD_MODULES = "--add-modules";
935
private static final String ADD_EXPORTS = "--add-exports";
936
private static final String ADD_OPENS = "--add-opens";
937
private static final String ADD_READS = "--add-reads";
938
private static final String PATCH_MODULE = "--patch-module";
939
private static final String ENABLE_NATIVE_ACCESS = "--enable-native-access";
940
941
/*
942
* Returns the command-line option name corresponds to the specified
943
* system property prefix.
944
*/
945
static String option(String prefix) {
946
switch (prefix) {
947
case "jdk.module.addexports.":
948
return ADD_EXPORTS;
949
case "jdk.module.addopens.":
950
return ADD_OPENS;
951
case "jdk.module.addreads.":
952
return ADD_READS;
953
case "jdk.module.patch.":
954
return PATCH_MODULE;
955
case "jdk.module.addmods.":
956
return ADD_MODULES;
957
default:
958
throw new IllegalArgumentException(prefix);
959
}
960
}
961
962
/**
963
* Wraps a (potentially not thread safe) ModuleFinder created during startup
964
* for use after startup.
965
*/
966
static class SafeModuleFinder implements ModuleFinder {
967
private final Set<ModuleReference> mrefs;
968
private volatile Map<String, ModuleReference> nameToModule;
969
970
SafeModuleFinder(ModuleFinder finder) {
971
this.mrefs = Collections.unmodifiableSet(finder.findAll());
972
}
973
@Override
974
public Optional<ModuleReference> find(String name) {
975
Objects.requireNonNull(name);
976
Map<String, ModuleReference> nameToModule = this.nameToModule;
977
if (nameToModule == null) {
978
this.nameToModule = nameToModule = mrefs.stream()
979
.collect(Collectors.toMap(m -> m.descriptor().name(),
980
Function.identity()));
981
}
982
return Optional.ofNullable(nameToModule.get(name));
983
}
984
@Override
985
public Set<ModuleReference> findAll() {
986
return mrefs;
987
}
988
}
989
990
/**
991
* Counters for startup performance analysis.
992
*/
993
static class Counters {
994
private static final boolean PUBLISH_COUNTERS;
995
private static final boolean PRINT_COUNTERS;
996
private static Map<String, Long> counters;
997
private static long startTime;
998
private static long previousTime;
999
1000
static {
1001
String s = System.getProperty("jdk.module.boot.usePerfData");
1002
if (s == null) {
1003
PUBLISH_COUNTERS = false;
1004
PRINT_COUNTERS = false;
1005
} else {
1006
PUBLISH_COUNTERS = true;
1007
PRINT_COUNTERS = s.equals("debug");
1008
counters = new LinkedHashMap<>(); // preserve insert order
1009
}
1010
}
1011
1012
/**
1013
* Start counting time.
1014
*/
1015
static void start() {
1016
if (PUBLISH_COUNTERS) {
1017
startTime = previousTime = System.nanoTime();
1018
}
1019
}
1020
1021
/**
1022
* Add a counter - storing the time difference between now and the
1023
* previous add or the start.
1024
*/
1025
static void add(String name) {
1026
if (PUBLISH_COUNTERS) {
1027
long current = System.nanoTime();
1028
long elapsed = current - previousTime;
1029
previousTime = current;
1030
counters.put(name, elapsed);
1031
}
1032
}
1033
1034
/**
1035
* Publish the counters to the instrumentation buffer or stdout.
1036
*/
1037
static void publish(String totalTimeName) {
1038
if (PUBLISH_COUNTERS) {
1039
long currentTime = System.nanoTime();
1040
for (Map.Entry<String, Long> e : counters.entrySet()) {
1041
String name = e.getKey();
1042
long value = e.getValue();
1043
PerfCounter.newPerfCounter(name).set(value);
1044
if (PRINT_COUNTERS)
1045
System.out.println(name + " = " + value);
1046
}
1047
long elapsedTotal = currentTime - startTime;
1048
PerfCounter.newPerfCounter(totalTimeName).set(elapsedTotal);
1049
if (PRINT_COUNTERS)
1050
System.out.println(totalTimeName + " = " + elapsedTotal);
1051
}
1052
}
1053
}
1054
}
1055
1056