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.NoSuchElementException;
46 import java.util.Objects;
47 import java.util.Optional;
48 import java.util.Set;
49 import java.util.function.Function;
50 import java.util.stream.Collectors;
51
52 import jdk.internal.loader.BootLoader;
53 import jdk.internal.loader.BuiltinClassLoader;
54 import jdk.internal.access.JavaLangAccess;
55 import jdk.internal.access.JavaLangModuleAccess;
56 import jdk.internal.access.SharedSecrets;
57 import jdk.internal.perf.PerfCounter;
58
59 /**
60 * Initializes/boots the module system.
61 *
62 * The {@link #boot() boot} method is called early in the startup to initialize
63 * the module system. In summary, the boot method creates a Configuration by
64 * resolving a set of module names specified via the launcher (or equivalent)
65 * -m and --add-modules options. The modules are located on a module path that
66 * is constructed from the upgrade module path, system modules, and application
67 * module path. The Configuration is instantiated as the boot layer with each
68 * module in the configuration defined to a class loader.
69 */
70
71 public final class ModuleBootstrap {
72 private ModuleBootstrap() { }
73
116 return ModuleFinder.ofSystem();
117 } else {
118 return finder;
119 }
120 }
121
122 /**
123 * Returns the ModuleFinder for the initial configuration.
124 *
125 * @apiNote Used to support "{@code java --list-modules}".
126 */
127 public static ModuleFinder limitedFinder() {
128 ModuleFinder finder = limitedFinder;
129 if (finder == null) {
130 return unlimitedFinder();
131 } else {
132 return finder;
133 }
134 }
135
136 /**
137 * Initialize the module system, returning the boot layer.
138 *
139 * @see java.lang.System#initPhase2(boolean, boolean)
140 */
141 public static ModuleLayer boot() throws Exception {
142
143 Counters.start();
144
145 // Step 0: Command line options
146
147 ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path");
148 ModuleFinder appModulePath = finderFor("jdk.module.path");
149 boolean isPatched = patcher.hasPatches();
150
151 String mainModule = System.getProperty("jdk.module.main");
152 Set<String> addModules = addModules();
153 Set<String> limitModules = limitModules();
154
155 PrintStream traceOutput = null;
156 String trace = getAndRemoveProperty("jdk.module.showModuleResolution");
157 if (trace != null && Boolean.parseBoolean(trace))
158 traceOutput = System.out;
159
160 Counters.add("jdk.module.boot.0.commandLineTime");
161
162 // Step 1: The observable system modules, either all system modules
163 // or the system modules pre-generated for the initial module (the
388 if (upgradeModulePath != null
389 && upgradeModulePath.find(name).isPresent())
390 fail(name + ": cannot be loaded from upgrade module path");
391 if (!systemModuleFinder.find(name).isPresent())
392 fail(name + ": cannot be loaded from application module path");
393 }
394 }
395 }
396
397 // check for split packages in the modules mapped to the built-in loaders
398 if (hasSplitPackages || isPatched || haveModulePath) {
399 checkSplitPackages(cf, clf);
400 }
401
402 // load/register the modules with the built-in class loaders
403 loadModules(cf, clf);
404 Counters.add("jdk.module.boot.5.loadModulesTime");
405
406 // Step 6: Define all modules to the VM
407
408 ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf);
409 Counters.add("jdk.module.boot.6.layerCreateTime");
410
411 // Step 7: Miscellaneous
412
413 // check incubating status
414 if (hasIncubatorModules || haveModulePath) {
415 checkIncubatingStatus(cf);
416 }
417
418 // --add-reads, --add-exports/--add-opens, and --illegal-access
419 addExtraReads(bootLayer);
420 boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer);
421
422 Map<String, Set<String>> concealedPackagesToOpen;
423 Map<String, Set<String>> exportedPackagesToOpen;
424 if (archivedModuleGraph != null) {
425 concealedPackagesToOpen = archivedModuleGraph.concealedPackagesToOpen();
426 exportedPackagesToOpen = archivedModuleGraph.exportedPackagesToOpen();
427 } else {
428 concealedPackagesToOpen = systemModules.concealedPackagesToOpen();
429 exportedPackagesToOpen = systemModules.exportedPackagesToOpen();
430 }
431 addIllegalAccess(upgradeModulePath,
432 concealedPackagesToOpen,
433 exportedPackagesToOpen,
434 bootLayer,
435 extraExportsOrOpens);
436 Counters.add("jdk.module.boot.7.adjustModulesTime");
437
438 // save module finders for later use
439 if (savedModuleFinder != null) {
440 unlimitedFinder = new SafeModuleFinder(savedModuleFinder);
441 if (savedModuleFinder != finder)
442 limitedFinder = new SafeModuleFinder(finder);
443 }
444
445 // Module graph can be archived at CDS dump time. Only allow the
446 // unnamed module case for now.
447 if (canArchive && (mainModule == null)) {
448 ArchivedModuleGraph.archive(
449 new ArchivedModuleGraph(hasSplitPackages,
450 hasIncubatorModules,
451 systemModuleFinder,
452 cf,
453 clf,
454 concealedPackagesToOpen,
455 exportedPackagesToOpen));
456 }
457
458 // total time to initialize
459 Counters.publish("jdk.module.boot.totalTime");
460
461 return bootLayer;
462 }
463
464 /**
465 * Load/register the modules to the built-in class loaders.
466 */
467 private static void loadModules(Configuration cf,
468 Function<String, ClassLoader> clf) {
469 for (ResolvedModule resolvedModule : cf.modules()) {
470 ModuleReference mref = resolvedModule.reference();
471 String name = resolvedModule.name();
472 ClassLoader loader = clf.apply(name);
473 if (loader == null) {
474 // skip java.base as it is already loaded
475 if (!name.equals(JAVA_BASE)) {
734 if (opens) {
735 Modules.addOpensToAllUnnamed(m, pn);
736 } else {
737 Modules.addExportsToAllUnnamed(m, pn);
738 }
739 } else {
740 if (opens) {
741 Modules.addOpens(m, pn, other);
742 } else {
743 Modules.addExports(m, pn, other);
744 }
745 }
746 }
747 }
748 }
749
750 /**
751 * Process the --illegal-access option (and its default) to open packages
752 * of system modules in the boot layer to code in unnamed modules.
753 */
754 private static void addIllegalAccess(ModuleFinder upgradeModulePath,
755 Map<String, Set<String>> concealedPackagesToOpen,
756 Map<String, Set<String>> exportedPackagesToOpen,
757 ModuleLayer bootLayer,
758 boolean extraExportsOrOpens) {
759 String value = getAndRemoveProperty("jdk.module.illegalAccess");
760 IllegalAccessLogger.Mode mode = IllegalAccessLogger.Mode.ONESHOT;
761 if (value != null) {
762 switch (value) {
763 case "deny":
764 return;
765 case "permit":
766 break;
767 case "warn":
768 mode = IllegalAccessLogger.Mode.WARN;
769 break;
770 case "debug":
771 mode = IllegalAccessLogger.Mode.DEBUG;
772 break;
773 default:
774 fail("Value specified to --illegal-access not recognized:"
775 + " '" + value + "'");
776 return;
777 }
778 }
779 IllegalAccessLogger.Builder builder
780 = new IllegalAccessLogger.Builder(mode, System.err);
781
782 if (concealedPackagesToOpen.isEmpty() && exportedPackagesToOpen.isEmpty()) {
783 // need to generate (exploded build)
784 IllegalAccessMaps maps = IllegalAccessMaps.generate(limitedFinder());
785 concealedPackagesToOpen = maps.concealedPackagesToOpen();
786 exportedPackagesToOpen = maps.exportedPackagesToOpen();
787 }
788
789 // open specific packages in the system modules
790 Set<String> emptySet = Set.of();
791 for (Module m : bootLayer.modules()) {
792 ModuleDescriptor descriptor = m.getDescriptor();
793 String name = m.getName();
794
795 // skip open modules
796 if (descriptor.isOpen()) {
824 String pn = iterator.next();
825 if (m.isOpen(pn, BootLoader.getUnnamedModule())) {
826 // exported package is opened to ALL-UNNAMED
827 iterator.remove();
828 }
829 }
830 }
831
832 // log reflective access to all types in concealed packages
833 builder.logAccessToConcealedPackages(m, concealedPackages);
834
835 // log reflective access to non-public members/types in exported packages
836 builder.logAccessToExportedPackages(m, exportedPackages);
837
838 // open the packages to unnamed modules
839 JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
840 jla.addOpensToAllUnnamed(m, concealedPackages, exportedPackages);
841 }
842
843 builder.complete();
844 }
845
846 /**
847 * Decodes the values of --add-reads, -add-exports, --add-opens or
848 * --patch-modules options that are encoded in system properties.
849 *
850 * @param prefix the system property prefix
851 * @praam regex the regex for splitting the RHS of the option value
852 */
853 private static Map<String, List<String>> decode(String prefix,
854 String regex,
855 boolean allowDuplicates) {
856 int index = 0;
857 // the system property is removed after decoding
858 String value = getAndRemoveProperty(prefix + index);
859 if (value == null)
860 return Map.of();
861
862 Map<String, List<String>> map = new HashMap<>();
863
|
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.NoSuchElementException;
46 import java.util.Objects;
47 import java.util.Optional;
48 import java.util.Set;
49 import java.util.function.Function;
50 import java.util.stream.Collectors;
51
52 import jdk.internal.loader.BootLoader;
53 import jdk.internal.loader.BuiltinClassLoader;
54 import jdk.internal.loader.ClassLoaders;
55 import jdk.internal.misc.VM;
56 import jdk.internal.access.JavaLangAccess;
57 import jdk.internal.access.JavaLangModuleAccess;
58 import jdk.internal.access.SharedSecrets;
59 import jdk.internal.perf.PerfCounter;
60
61 /**
62 * Initializes/boots the module system.
63 *
64 * The {@link #boot() boot} method is called early in the startup to initialize
65 * the module system. In summary, the boot method creates a Configuration by
66 * resolving a set of module names specified via the launcher (or equivalent)
67 * -m and --add-modules options. The modules are located on a module path that
68 * is constructed from the upgrade module path, system modules, and application
69 * module path. The Configuration is instantiated as the boot layer with each
70 * module in the configuration defined to a class loader.
71 */
72
73 public final class ModuleBootstrap {
74 private ModuleBootstrap() { }
75
118 return ModuleFinder.ofSystem();
119 } else {
120 return finder;
121 }
122 }
123
124 /**
125 * Returns the ModuleFinder for the initial configuration.
126 *
127 * @apiNote Used to support "{@code java --list-modules}".
128 */
129 public static ModuleFinder limitedFinder() {
130 ModuleFinder finder = limitedFinder;
131 if (finder == null) {
132 return unlimitedFinder();
133 } else {
134 return finder;
135 }
136 }
137
138 private static class ArchivedBootLayer {
139 private static ArchivedBootLayer archivedBootLayer;
140
141 private final ModuleLayer bootLayer;
142 private final ModuleFinder limitedFinder;
143 private final IllegalAccessLogger.Builder builder;
144 private final ModuleFinder unlimitedFinder;
145 private final ServicesCatalog platformCatalog;
146 private final ServicesCatalog appCatalog;
147
148 public ArchivedBootLayer(ModuleLayer bootLayer,
149 ModuleFinder limitedFinder,
150 ModuleFinder unlimitedFinder,
151 IllegalAccessLogger.Builder builder) {
152 this.bootLayer = bootLayer;
153 this.limitedFinder = limitedFinder;
154 this.unlimitedFinder = unlimitedFinder;
155 this.builder = builder;
156
157 this.platformCatalog = ServicesCatalog.getServicesCatalog(ClassLoaders.platformClassLoader());
158 this.appCatalog = ServicesCatalog.getServicesCatalog(ClassLoaders.appClassLoader());
159 }
160
161 static ArchivedBootLayer get() {
162 // The VM will initialize archivedBootLayer only if MetaspaceShared::use_full_module_graph() is true.
163 return archivedBootLayer;
164 }
165
166 static void archive(ArchivedBootLayer layer) {
167 archivedBootLayer = layer;
168 }
169
170 static {
171 VM.initializeFromArchive(ArchivedBootLayer.class);
172 }
173 }
174
175 private static boolean hasProperty(String key) {
176 return System.getProperty(key) != null;
177 }
178
179 private static boolean mayUseArchivedBootLayer() {
180 // If these properties are set, we cannot use the archived boot layer.
181 // The VM should have already checked this before initializing
182 // ArchivedBootLayer::archivedBootLayer.
183 if (hasProperty("jdk.module.upgrade.path") ||
184 hasProperty("jdk.module.main") ||
185 hasProperty("jdk.module.limitmods") ||
186 hasProperty("jdk.module.validation") ||
187 hasProperty("jdk.module.showModuleResolution") ||
188 hasProperty("jdk.module.illegalAccess") ||
189 hasProperty("java.system.class.loader") ||
190 hasProperty("jdk.module.addexports.0") ||
191 hasProperty("jdk.module.addopens.0") ||
192 hasProperty("jdk.module.addreads.0") ||
193 hasProperty("jdk.module.patch.0") ||
194 hasProperty("jdk.module.addmods.0")) {
195 return false;
196 } else {
197 return true;
198 }
199 }
200
201 private static ModuleLayer getArchivedBootLayer() {
202 ArchivedBootLayer archivedBootLayer = ArchivedBootLayer.get();
203 if (archivedBootLayer != null) {
204 assert mayUseArchivedBootLayer();
205 Counters.add("jdk.module.boot.0.archivedBootLayer");
206 limitedFinder = archivedBootLayer.limitedFinder;
207 unlimitedFinder = archivedBootLayer.unlimitedFinder;
208 ModuleLayer bootLayer = archivedBootLayer.bootLayer;
209
210 // Trigger BootLoader.<clinit>
211 BootLoader.getUnnamedModule();
212
213 // BootLoader.SERVICES_CATALOG is saved/restored separately in BootLoader.java
214 ServicesCatalog.setServicesCatalog(ClassLoaders.platformClassLoader(),
215 archivedBootLayer.platformCatalog);
216 ServicesCatalog.setServicesCatalog(ClassLoaders.appClassLoader(),
217 archivedBootLayer.appCatalog);
218
219 JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
220 jla.bindToLoader(bootLayer, ClassLoaders.appClassLoader());
221
222 IllegalAccessLogger.Builder builder = archivedBootLayer.builder;
223 if (builder != null) {
224 builder.complete();
225 }
226
227 Counters.publish("jdk.module.boot.totalTime");
228 return bootLayer;
229 }
230
231 return null;
232 }
233
234
235 /**
236 * Initialize the module system, returning the boot layer.
237 *
238 * @see java.lang.System#initPhase2(boolean, boolean)
239 */
240 public static ModuleLayer boot() throws Exception {
241
242 Counters.start();
243 ModuleLayer bootLayer = getArchivedBootLayer();
244 if (bootLayer != null) {
245 return bootLayer;
246 }
247
248 // Step 0: Command line options
249
250 ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path");
251 ModuleFinder appModulePath = finderFor("jdk.module.path");
252 boolean isPatched = patcher.hasPatches();
253
254 String mainModule = System.getProperty("jdk.module.main");
255 Set<String> addModules = addModules();
256 Set<String> limitModules = limitModules();
257
258 PrintStream traceOutput = null;
259 String trace = getAndRemoveProperty("jdk.module.showModuleResolution");
260 if (trace != null && Boolean.parseBoolean(trace))
261 traceOutput = System.out;
262
263 Counters.add("jdk.module.boot.0.commandLineTime");
264
265 // Step 1: The observable system modules, either all system modules
266 // or the system modules pre-generated for the initial module (the
491 if (upgradeModulePath != null
492 && upgradeModulePath.find(name).isPresent())
493 fail(name + ": cannot be loaded from upgrade module path");
494 if (!systemModuleFinder.find(name).isPresent())
495 fail(name + ": cannot be loaded from application module path");
496 }
497 }
498 }
499
500 // check for split packages in the modules mapped to the built-in loaders
501 if (hasSplitPackages || isPatched || haveModulePath) {
502 checkSplitPackages(cf, clf);
503 }
504
505 // load/register the modules with the built-in class loaders
506 loadModules(cf, clf);
507 Counters.add("jdk.module.boot.5.loadModulesTime");
508
509 // Step 6: Define all modules to the VM
510
511 bootLayer = ModuleLayer.empty().defineModules(cf, clf);
512 Counters.add("jdk.module.boot.6.layerCreateTime");
513
514 // Step 7: Miscellaneous
515
516 // check incubating status
517 if (hasIncubatorModules || haveModulePath) {
518 checkIncubatingStatus(cf);
519 }
520
521 // --add-reads, --add-exports/--add-opens, and --illegal-access
522 addExtraReads(bootLayer);
523 boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer);
524
525 Map<String, Set<String>> concealedPackagesToOpen;
526 Map<String, Set<String>> exportedPackagesToOpen;
527 if (archivedModuleGraph != null) {
528 concealedPackagesToOpen = archivedModuleGraph.concealedPackagesToOpen();
529 exportedPackagesToOpen = archivedModuleGraph.exportedPackagesToOpen();
530 } else {
531 concealedPackagesToOpen = systemModules.concealedPackagesToOpen();
532 exportedPackagesToOpen = systemModules.exportedPackagesToOpen();
533 }
534 IllegalAccessLogger.Builder builder =
535 addIllegalAccess(upgradeModulePath,
536 concealedPackagesToOpen,
537 exportedPackagesToOpen,
538 bootLayer,
539 extraExportsOrOpens);
540 Counters.add("jdk.module.boot.7.adjustModulesTime");
541
542 // save module finders for later use
543 if (savedModuleFinder != null) {
544 unlimitedFinder = new SafeModuleFinder(savedModuleFinder);
545 if (savedModuleFinder != finder)
546 limitedFinder = new SafeModuleFinder(finder);
547 }
548
549 // Module graph can be archived at CDS dump time. Only allow the
550 // unnamed module case for now.
551 if (canArchive && (mainModule == null)) {
552 ArchivedModuleGraph.archive(
553 new ArchivedModuleGraph(hasSplitPackages,
554 hasIncubatorModules,
555 systemModuleFinder,
556 cf,
557 clf,
558 concealedPackagesToOpen,
559 exportedPackagesToOpen));
560 ArchivedBootLayer.archive(
561 new ArchivedBootLayer(bootLayer,
562 limitedFinder,
563 unlimitedFinder,
564 builder));
565 }
566
567 // total time to initialize
568 Counters.publish("jdk.module.boot.totalTime");
569
570 return bootLayer;
571 }
572
573 /**
574 * Load/register the modules to the built-in class loaders.
575 */
576 private static void loadModules(Configuration cf,
577 Function<String, ClassLoader> clf) {
578 for (ResolvedModule resolvedModule : cf.modules()) {
579 ModuleReference mref = resolvedModule.reference();
580 String name = resolvedModule.name();
581 ClassLoader loader = clf.apply(name);
582 if (loader == null) {
583 // skip java.base as it is already loaded
584 if (!name.equals(JAVA_BASE)) {
843 if (opens) {
844 Modules.addOpensToAllUnnamed(m, pn);
845 } else {
846 Modules.addExportsToAllUnnamed(m, pn);
847 }
848 } else {
849 if (opens) {
850 Modules.addOpens(m, pn, other);
851 } else {
852 Modules.addExports(m, pn, other);
853 }
854 }
855 }
856 }
857 }
858
859 /**
860 * Process the --illegal-access option (and its default) to open packages
861 * of system modules in the boot layer to code in unnamed modules.
862 */
863 private static IllegalAccessLogger.Builder
864 addIllegalAccess(ModuleFinder upgradeModulePath,
865 Map<String, Set<String>> concealedPackagesToOpen,
866 Map<String, Set<String>> exportedPackagesToOpen,
867 ModuleLayer bootLayer,
868 boolean extraExportsOrOpens) {
869 String value = getAndRemoveProperty("jdk.module.illegalAccess");
870 IllegalAccessLogger.Mode mode = IllegalAccessLogger.Mode.ONESHOT;
871 if (value != null) {
872 switch (value) {
873 case "deny":
874 return null;
875 case "permit":
876 break;
877 case "warn":
878 mode = IllegalAccessLogger.Mode.WARN;
879 break;
880 case "debug":
881 mode = IllegalAccessLogger.Mode.DEBUG;
882 break;
883 default:
884 fail("Value specified to --illegal-access not recognized:"
885 + " '" + value + "'");
886 return null;
887 }
888 }
889 IllegalAccessLogger.Builder builder
890 = new IllegalAccessLogger.Builder(mode, System.err);
891
892 if (concealedPackagesToOpen.isEmpty() && exportedPackagesToOpen.isEmpty()) {
893 // need to generate (exploded build)
894 IllegalAccessMaps maps = IllegalAccessMaps.generate(limitedFinder());
895 concealedPackagesToOpen = maps.concealedPackagesToOpen();
896 exportedPackagesToOpen = maps.exportedPackagesToOpen();
897 }
898
899 // open specific packages in the system modules
900 Set<String> emptySet = Set.of();
901 for (Module m : bootLayer.modules()) {
902 ModuleDescriptor descriptor = m.getDescriptor();
903 String name = m.getName();
904
905 // skip open modules
906 if (descriptor.isOpen()) {
934 String pn = iterator.next();
935 if (m.isOpen(pn, BootLoader.getUnnamedModule())) {
936 // exported package is opened to ALL-UNNAMED
937 iterator.remove();
938 }
939 }
940 }
941
942 // log reflective access to all types in concealed packages
943 builder.logAccessToConcealedPackages(m, concealedPackages);
944
945 // log reflective access to non-public members/types in exported packages
946 builder.logAccessToExportedPackages(m, exportedPackages);
947
948 // open the packages to unnamed modules
949 JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
950 jla.addOpensToAllUnnamed(m, concealedPackages, exportedPackages);
951 }
952
953 builder.complete();
954 return builder;
955 }
956
957 /**
958 * Decodes the values of --add-reads, -add-exports, --add-opens or
959 * --patch-modules options that are encoded in system properties.
960 *
961 * @param prefix the system property prefix
962 * @praam regex the regex for splitting the RHS of the option value
963 */
964 private static Map<String, List<String>> decode(String prefix,
965 String regex,
966 boolean allowDuplicates) {
967 int index = 0;
968 // the system property is removed after decoding
969 String value = getAndRemoveProperty(prefix + index);
970 if (value == null)
971 return Map.of();
972
973 Map<String, List<String>> map = new HashMap<>();
974
|