Path: blob/master/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
41159 views
/*1* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.module;2627import java.io.File;28import java.io.PrintStream;29import java.lang.module.Configuration;30import java.lang.module.ModuleDescriptor;31import java.lang.module.ModuleFinder;32import java.lang.module.ModuleReference;33import java.lang.module.ResolvedModule;34import java.net.URI;35import java.nio.file.Path;36import java.util.ArrayList;37import java.util.Collections;38import java.util.HashMap;39import java.util.HashSet;40import java.util.Iterator;41import java.util.LinkedHashMap;42import java.util.List;43import java.util.Map;44import java.util.Objects;45import java.util.Optional;46import java.util.Set;47import java.util.function.Function;48import java.util.stream.Collectors;4950import jdk.internal.access.JavaLangAccess;51import jdk.internal.access.JavaLangModuleAccess;52import jdk.internal.access.SharedSecrets;53import jdk.internal.loader.BootLoader;54import jdk.internal.loader.BuiltinClassLoader;55import jdk.internal.loader.ClassLoaders;56import jdk.internal.misc.CDS;57import jdk.internal.perf.PerfCounter;5859/**60* Initializes/boots the module system.61*62* The {@link #boot() boot} method is called early in the startup to initialize63* the module system. In summary, the boot method creates a Configuration by64* 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 that66* is constructed from the upgrade module path, system modules, and application67* module path. The Configuration is instantiated as the boot layer with each68* module in the configuration defined to a class loader.69*/7071public final class ModuleBootstrap {72private ModuleBootstrap() { }7374private static final String JAVA_BASE = "java.base";7576// the token for "all default modules"77private static final String ALL_DEFAULT = "ALL-DEFAULT";7879// the token for "all unnamed modules"80private static final String ALL_UNNAMED = "ALL-UNNAMED";8182// the token for "all system modules"83private static final String ALL_SYSTEM = "ALL-SYSTEM";8485// the token for "all modules on the module path"86private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";8788// access to java.lang/module89private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();90private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();9192// The ModulePatcher for the initial configuration93private static final ModulePatcher patcher = initModulePatcher();9495/**96* Returns the ModulePatcher for the initial configuration.97*/98public static ModulePatcher patcher() {99return patcher;100}101102// ModuleFinders for the initial configuration103private static volatile ModuleFinder unlimitedFinder;104private static volatile ModuleFinder limitedFinder;105106/**107* Returns the ModuleFinder for the initial configuration before108* observability is limited by the --limit-modules command line option.109*110* @apiNote Used to support locating modules {@code java.instrument} and111* {@code jdk.management.agent} modules when they are loaded dynamically.112*/113public static ModuleFinder unlimitedFinder() {114ModuleFinder finder = unlimitedFinder;115if (finder == null) {116return ModuleFinder.ofSystem();117} else {118return finder;119}120}121122/**123* Returns the ModuleFinder for the initial configuration.124*125* @apiNote Used to support "{@code java --list-modules}".126*/127public static ModuleFinder limitedFinder() {128ModuleFinder finder = limitedFinder;129if (finder == null) {130return unlimitedFinder();131} else {132return finder;133}134}135136/**137* Returns true if the archived boot layer can be used. The system properties138* are checked in the order that they are used by boot2.139*/140private static boolean canUseArchivedBootLayer() {141return getProperty("jdk.module.upgrade.path") == null &&142getProperty("jdk.module.path") == null &&143getProperty("jdk.module.patch.0") == null && // --patch-module144getProperty("jdk.module.main") == null && // --module145getProperty("jdk.module.addmods.0") == null && // --add-modules146getProperty("jdk.module.limitmods") == null && // --limit-modules147getProperty("jdk.module.addreads.0") == null && // --add-reads148getProperty("jdk.module.addexports.0") == null && // --add-exports149getProperty("jdk.module.addopens.0") == null; // --add-opens150}151152/**153* Initialize the module system, returning the boot layer. The boot layer154* is obtained from the CDS archive if possible, otherwise it is generated155* from the module graph.156*157* @see java.lang.System#initPhase2(boolean, boolean)158*/159public static ModuleLayer boot() {160Counters.start();161162ModuleLayer bootLayer;163ArchivedBootLayer archivedBootLayer = ArchivedBootLayer.get();164if (archivedBootLayer != null) {165assert canUseArchivedBootLayer();166bootLayer = archivedBootLayer.bootLayer();167BootLoader.getUnnamedModule(); // trigger <clinit> of BootLoader.168CDS.defineArchivedModules(ClassLoaders.platformClassLoader(), ClassLoaders.appClassLoader());169170// assume boot layer has at least one module providing a service171// that is mapped to the application class loader.172JLA.bindToLoader(bootLayer, ClassLoaders.appClassLoader());173} else {174bootLayer = boot2();175}176177Counters.publish("jdk.module.boot.totalTime");178return bootLayer;179}180181private static ModuleLayer boot2() {182// Step 0: Command line options183184ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path");185ModuleFinder appModulePath = finderFor("jdk.module.path");186boolean isPatched = patcher.hasPatches();187String mainModule = System.getProperty("jdk.module.main");188Set<String> addModules = addModules();189Set<String> limitModules = limitModules();190191PrintStream traceOutput = null;192String trace = getAndRemoveProperty("jdk.module.showModuleResolution");193if (trace != null && Boolean.parseBoolean(trace))194traceOutput = System.out;195196Counters.add("jdk.module.boot.0.commandLineTime");197198// Step 1: The observable system modules, either all system modules199// or the system modules pre-generated for the initial module (the200// initial module may be the unnamed module). If the system modules201// are pre-generated for the initial module then resolution can be202// skipped.203204SystemModules systemModules = null;205ModuleFinder systemModuleFinder;206207boolean haveModulePath = (appModulePath != null || upgradeModulePath != null);208boolean needResolution = true;209boolean canArchive = false;210boolean hasSplitPackages;211boolean hasIncubatorModules;212213// If the java heap was archived at CDS dump time and the environment214// at dump time matches the current environment then use the archived215// system modules and finder.216ArchivedModuleGraph archivedModuleGraph = ArchivedModuleGraph.get(mainModule);217if (archivedModuleGraph != null218&& !haveModulePath219&& addModules.isEmpty()220&& limitModules.isEmpty()221&& !isPatched) {222systemModuleFinder = archivedModuleGraph.finder();223hasSplitPackages = archivedModuleGraph.hasSplitPackages();224hasIncubatorModules = archivedModuleGraph.hasIncubatorModules();225needResolution = (traceOutput != null);226} else {227if (!haveModulePath && addModules.isEmpty() && limitModules.isEmpty()) {228systemModules = SystemModuleFinders.systemModules(mainModule);229if (systemModules != null && !isPatched) {230needResolution = (traceOutput != null);231canArchive = true;232}233}234if (systemModules == null) {235// all system modules are observable236systemModules = SystemModuleFinders.allSystemModules();237}238if (systemModules != null) {239// images build240systemModuleFinder = SystemModuleFinders.of(systemModules);241} else {242// exploded build or testing243systemModules = new ExplodedSystemModules();244systemModuleFinder = SystemModuleFinders.ofSystem();245}246247hasSplitPackages = systemModules.hasSplitPackages();248hasIncubatorModules = systemModules.hasIncubatorModules();249// not using the archived module graph - avoid accidental use250archivedModuleGraph = null;251}252253Counters.add("jdk.module.boot.1.systemModulesTime");254255// Step 2: Define and load java.base. This patches all classes loaded256// to date so that they are members of java.base. Once java.base is257// loaded then resources in java.base are available for error messages258// needed from here on.259260ModuleReference base = systemModuleFinder.find(JAVA_BASE).orElse(null);261if (base == null)262throw new InternalError(JAVA_BASE + " not found");263URI baseUri = base.location().orElse(null);264if (baseUri == null)265throw new InternalError(JAVA_BASE + " does not have a location");266BootLoader.loadModule(base);267268Module baseModule = Modules.defineModule(null, base.descriptor(), baseUri);269JLA.addEnableNativeAccess(baseModule);270271// Step 2a: Scan all modules when --validate-modules specified272273if (getAndRemoveProperty("jdk.module.validation") != null) {274int errors = ModulePathValidator.scanAllModules(System.out);275if (errors > 0) {276fail("Validation of module path failed");277}278}279280Counters.add("jdk.module.boot.2.defineBaseTime");281282// Step 3: If resolution is needed then create the module finder and283// the set of root modules to resolve.284285ModuleFinder savedModuleFinder = null;286ModuleFinder finder;287Set<String> roots;288if (needResolution) {289290// upgraded modules override the modules in the run-time image291if (upgradeModulePath != null)292systemModuleFinder = ModuleFinder.compose(upgradeModulePath,293systemModuleFinder);294295// The module finder: [--upgrade-module-path] system [--module-path]296if (appModulePath != null) {297finder = ModuleFinder.compose(systemModuleFinder, appModulePath);298} else {299finder = systemModuleFinder;300}301302// The root modules to resolve303roots = new HashSet<>();304305// launcher -m option to specify the main/initial module306if (mainModule != null)307roots.add(mainModule);308309// additional module(s) specified by --add-modules310boolean addAllDefaultModules = false;311boolean addAllSystemModules = false;312boolean addAllApplicationModules = false;313for (String mod : addModules) {314switch (mod) {315case ALL_DEFAULT:316addAllDefaultModules = true;317break;318case ALL_SYSTEM:319addAllSystemModules = true;320break;321case ALL_MODULE_PATH:322addAllApplicationModules = true;323break;324default:325roots.add(mod);326}327}328329// --limit-modules330savedModuleFinder = finder;331if (!limitModules.isEmpty()) {332finder = limitFinder(finder, limitModules, roots);333}334335// If there is no initial module specified then assume that the initial336// module is the unnamed module of the application class loader. This337// is implemented by resolving all observable modules that export an338// API. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT bit set in339// their ModuleResolution attribute flags are excluded from the340// default set of roots.341if (mainModule == null || addAllDefaultModules) {342roots.addAll(DefaultRoots.compute(systemModuleFinder, finder));343}344345// If `--add-modules ALL-SYSTEM` is specified then all observable system346// modules will be resolved.347if (addAllSystemModules) {348ModuleFinder f = finder; // observable modules349systemModuleFinder.findAll()350.stream()351.map(ModuleReference::descriptor)352.map(ModuleDescriptor::name)353.filter(mn -> f.find(mn).isPresent()) // observable354.forEach(mn -> roots.add(mn));355}356357// If `--add-modules ALL-MODULE-PATH` is specified then all observable358// modules on the application module path will be resolved.359if (appModulePath != null && addAllApplicationModules) {360ModuleFinder f = finder; // observable modules361appModulePath.findAll()362.stream()363.map(ModuleReference::descriptor)364.map(ModuleDescriptor::name)365.filter(mn -> f.find(mn).isPresent()) // observable366.forEach(mn -> roots.add(mn));367}368} else {369// no resolution case370finder = systemModuleFinder;371roots = null;372}373374Counters.add("jdk.module.boot.3.optionsAndRootsTime");375376// Step 4: Resolve the root modules, with service binding, to create377// the configuration for the boot layer. If resolution is not needed378// then create the configuration for the boot layer from the379// readability graph created at link time.380381Configuration cf;382if (needResolution) {383cf = Modules.newBootLayerConfiguration(finder, roots, traceOutput);384} else {385if (archivedModuleGraph != null) {386cf = archivedModuleGraph.configuration();387} else {388Map<String, Set<String>> map = systemModules.moduleReads();389cf = JLMA.newConfiguration(systemModuleFinder, map);390}391}392393// check that modules specified to --patch-module are resolved394if (isPatched) {395patcher.patchedModules()396.stream()397.filter(mn -> !cf.findModule(mn).isPresent())398.forEach(mn -> warnUnknownModule(PATCH_MODULE, mn));399}400401Counters.add("jdk.module.boot.4.resolveTime");402403// Step 5: Map the modules in the configuration to class loaders.404// The static configuration provides the mapping of standard and JDK405// modules to the boot and platform loaders. All other modules (JDK406// tool modules, and both explicit and automatic modules on the407// application module path) are defined to the application class408// loader.409410// mapping of modules to class loaders411Function<String, ClassLoader> clf;412if (archivedModuleGraph != null) {413clf = archivedModuleGraph.classLoaderFunction();414} else {415clf = ModuleLoaderMap.mappingFunction(cf);416}417418// check that all modules to be mapped to the boot loader will be419// loaded from the runtime image420if (haveModulePath) {421for (ResolvedModule resolvedModule : cf.modules()) {422ModuleReference mref = resolvedModule.reference();423String name = mref.descriptor().name();424ClassLoader cl = clf.apply(name);425if (cl == null) {426if (upgradeModulePath != null427&& upgradeModulePath.find(name).isPresent())428fail(name + ": cannot be loaded from upgrade module path");429if (!systemModuleFinder.find(name).isPresent())430fail(name + ": cannot be loaded from application module path");431}432}433}434435// check for split packages in the modules mapped to the built-in loaders436if (hasSplitPackages || isPatched || haveModulePath) {437checkSplitPackages(cf, clf);438}439440// load/register the modules with the built-in class loaders441loadModules(cf, clf);442Counters.add("jdk.module.boot.5.loadModulesTime");443444// Step 6: Define all modules to the VM445446ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf);447Counters.add("jdk.module.boot.6.layerCreateTime");448449// Step 7: Miscellaneous450451// check incubating status452if (hasIncubatorModules || haveModulePath) {453checkIncubatingStatus(cf);454}455456// --add-reads, --add-exports/--add-opens457addExtraReads(bootLayer);458boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer);459460// add enable native access461addEnableNativeAccess(bootLayer);462463Counters.add("jdk.module.boot.7.adjustModulesTime");464465// save module finders for later use466if (savedModuleFinder != null) {467unlimitedFinder = new SafeModuleFinder(savedModuleFinder);468if (savedModuleFinder != finder)469limitedFinder = new SafeModuleFinder(finder);470}471472// Archive module graph and boot layer can be archived at CDS dump time.473// Only allow the unnamed module case for now.474if (canArchive && (mainModule == null)) {475ArchivedModuleGraph.archive(hasSplitPackages,476hasIncubatorModules,477systemModuleFinder,478cf,479clf);480if (!hasSplitPackages && !hasIncubatorModules) {481ArchivedBootLayer.archive(bootLayer);482}483}484485return bootLayer;486}487488/**489* Load/register the modules to the built-in class loaders.490*/491private static void loadModules(Configuration cf,492Function<String, ClassLoader> clf) {493for (ResolvedModule resolvedModule : cf.modules()) {494ModuleReference mref = resolvedModule.reference();495String name = resolvedModule.name();496ClassLoader loader = clf.apply(name);497if (loader == null) {498// skip java.base as it is already loaded499if (!name.equals(JAVA_BASE)) {500BootLoader.loadModule(mref);501}502} else if (loader instanceof BuiltinClassLoader) {503((BuiltinClassLoader) loader).loadModule(mref);504}505}506}507508/**509* Checks for split packages between modules defined to the built-in class510* loaders.511*/512private static void checkSplitPackages(Configuration cf,513Function<String, ClassLoader> clf) {514Map<String, String> packageToModule = new HashMap<>();515for (ResolvedModule resolvedModule : cf.modules()) {516ModuleDescriptor descriptor = resolvedModule.reference().descriptor();517String name = descriptor.name();518ClassLoader loader = clf.apply(name);519if (loader == null || loader instanceof BuiltinClassLoader) {520for (String p : descriptor.packages()) {521String other = packageToModule.putIfAbsent(p, name);522if (other != null) {523String msg = "Package " + p + " in both module "524+ name + " and module " + other;525throw new LayerInstantiationException(msg);526}527}528}529}530}531532/**533* Returns a ModuleFinder that limits observability to the given root534* modules, their transitive dependences, plus a set of other modules.535*/536private static ModuleFinder limitFinder(ModuleFinder finder,537Set<String> roots,538Set<String> otherMods)539{540// resolve all root modules541Configuration cf = Configuration.empty().resolve(finder,542ModuleFinder.of(),543roots);544545// module name -> reference546Map<String, ModuleReference> map = new HashMap<>();547548// root modules and their transitive dependences549cf.modules().stream()550.map(ResolvedModule::reference)551.forEach(mref -> map.put(mref.descriptor().name(), mref));552553// additional modules554otherMods.stream()555.map(finder::find)556.flatMap(Optional::stream)557.forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));558559// set of modules that are observable560Set<ModuleReference> mrefs = new HashSet<>(map.values());561562return new ModuleFinder() {563@Override564public Optional<ModuleReference> find(String name) {565return Optional.ofNullable(map.get(name));566}567@Override568public Set<ModuleReference> findAll() {569return mrefs;570}571};572}573574/**575* Creates a finder from the module path that is the value of the given576* system property and optionally patched by --patch-module577*/578private static ModuleFinder finderFor(String prop) {579String s = System.getProperty(prop);580if (s == null) {581return null;582} else {583String[] dirs = s.split(File.pathSeparator);584Path[] paths = new Path[dirs.length];585int i = 0;586for (String dir: dirs) {587paths[i++] = Path.of(dir);588}589return ModulePath.of(patcher, paths);590}591}592593/**594* Initialize the module patcher for the initial configuration passed on the595* value of the --patch-module options.596*/597private static ModulePatcher initModulePatcher() {598Map<String, List<String>> map = decode("jdk.module.patch.",599File.pathSeparator,600false);601return new ModulePatcher(map);602}603604/**605* Returns the set of module names specified by --add-module options.606*/607private static Set<String> addModules() {608String prefix = "jdk.module.addmods.";609int index = 0;610// the system property is removed after decoding611String value = getAndRemoveProperty(prefix + index);612if (value == null) {613return Set.of();614} else {615Set<String> modules = new HashSet<>();616while (value != null) {617for (String s : value.split(",")) {618if (!s.isEmpty())619modules.add(s);620}621index++;622value = getAndRemoveProperty(prefix + index);623}624return modules;625}626}627628/**629* Returns the set of module names specified by --limit-modules.630*/631private static Set<String> limitModules() {632String value = getAndRemoveProperty("jdk.module.limitmods");633if (value == null) {634return Set.of();635} else {636Set<String> names = new HashSet<>();637for (String name : value.split(",")) {638if (name.length() > 0) names.add(name);639}640return names;641}642}643644/**645* Process the --add-reads options to add any additional read edges that646* are specified on the command-line.647*/648private static void addExtraReads(ModuleLayer bootLayer) {649650// decode the command line options651Map<String, List<String>> map = decode("jdk.module.addreads.");652if (map.isEmpty())653return;654655for (Map.Entry<String, List<String>> e : map.entrySet()) {656657// the key is $MODULE658String mn = e.getKey();659Optional<Module> om = bootLayer.findModule(mn);660if (!om.isPresent()) {661warnUnknownModule(ADD_READS, mn);662continue;663}664Module m = om.get();665666// the value is the set of other modules (by name)667for (String name : e.getValue()) {668if (ALL_UNNAMED.equals(name)) {669Modules.addReadsAllUnnamed(m);670} else {671om = bootLayer.findModule(name);672if (om.isPresent()) {673Modules.addReads(m, om.get());674} else {675warnUnknownModule(ADD_READS, name);676}677}678}679}680}681682/**683* Process the --add-exports and --add-opens options to export/open684* additional packages specified on the command-line.685*/686private static boolean addExtraExportsAndOpens(ModuleLayer bootLayer) {687boolean extraExportsOrOpens = false;688689// --add-exports690String prefix = "jdk.module.addexports.";691Map<String, List<String>> extraExports = decode(prefix);692if (!extraExports.isEmpty()) {693addExtraExportsOrOpens(bootLayer, extraExports, false);694extraExportsOrOpens = true;695}696697698// --add-opens699prefix = "jdk.module.addopens.";700Map<String, List<String>> extraOpens = decode(prefix);701if (!extraOpens.isEmpty()) {702addExtraExportsOrOpens(bootLayer, extraOpens, true);703extraExportsOrOpens = true;704}705706return extraExportsOrOpens;707}708709private static void addExtraExportsOrOpens(ModuleLayer bootLayer,710Map<String, List<String>> map,711boolean opens)712{713String option = opens ? ADD_OPENS : ADD_EXPORTS;714for (Map.Entry<String, List<String>> e : map.entrySet()) {715716// the key is $MODULE/$PACKAGE717String key = e.getKey();718String[] s = key.split("/");719if (s.length != 2)720fail(unableToParse(option, "<module>/<package>", key));721722String mn = s[0];723String pn = s[1];724if (mn.isEmpty() || pn.isEmpty())725fail(unableToParse(option, "<module>/<package>", key));726727// The exporting module is in the boot layer728Module m;729Optional<Module> om = bootLayer.findModule(mn);730if (!om.isPresent()) {731warnUnknownModule(option, mn);732continue;733}734735m = om.get();736737if (!m.getDescriptor().packages().contains(pn)) {738warn("package " + pn + " not in " + mn);739continue;740}741742// the value is the set of modules to export to (by name)743for (String name : e.getValue()) {744boolean allUnnamed = false;745Module other = null;746if (ALL_UNNAMED.equals(name)) {747allUnnamed = true;748} else {749om = bootLayer.findModule(name);750if (om.isPresent()) {751other = om.get();752} else {753warnUnknownModule(option, name);754continue;755}756}757if (allUnnamed) {758if (opens) {759Modules.addOpensToAllUnnamed(m, pn);760} else {761Modules.addExportsToAllUnnamed(m, pn);762}763} else {764if (opens) {765Modules.addOpens(m, pn, other);766} else {767Modules.addExports(m, pn, other);768}769}770}771}772}773774/**775* Process the --enable-native-access option to grant access to restricted methods to selected modules.776*/777private static void addEnableNativeAccess(ModuleLayer layer) {778for (String name : decodeEnableNativeAccess()) {779if (name.equals("ALL-UNNAMED")) {780JLA.addEnableNativeAccessAllUnnamed();781} else {782Optional<Module> module = layer.findModule(name);783if (module.isPresent()) {784JLA.addEnableNativeAccess(module.get());785} else {786warnUnknownModule(ENABLE_NATIVE_ACCESS, name);787}788}789}790}791792/**793* Returns the set of module names specified by --enable-native-access options.794*/795private static Set<String> decodeEnableNativeAccess() {796String prefix = "jdk.module.enable.native.access.";797int index = 0;798// the system property is removed after decoding799String value = getAndRemoveProperty(prefix + index);800Set<String> modules = new HashSet<>();801if (value == null) {802return modules;803}804while (value != null) {805for (String s : value.split(",")) {806if (!s.isEmpty())807modules.add(s);808}809index++;810value = getAndRemoveProperty(prefix + index);811}812return modules;813}814815/**816* Decodes the values of --add-reads, -add-exports, --add-opens or817* --patch-modules options that are encoded in system properties.818*819* @param prefix the system property prefix820* @praam regex the regex for splitting the RHS of the option value821*/822private static Map<String, List<String>> decode(String prefix,823String regex,824boolean allowDuplicates) {825int index = 0;826// the system property is removed after decoding827String value = getAndRemoveProperty(prefix + index);828if (value == null)829return Map.of();830831Map<String, List<String>> map = new HashMap<>();832833while (value != null) {834835int pos = value.indexOf('=');836if (pos == -1)837fail(unableToParse(option(prefix), "<module>=<value>", value));838if (pos == 0)839fail(unableToParse(option(prefix), "<module>=<value>", value));840841// key is <module> or <module>/<package>842String key = value.substring(0, pos);843844String rhs = value.substring(pos+1);845if (rhs.isEmpty())846fail(unableToParse(option(prefix), "<module>=<value>", value));847848// value is <module>(,<module>)* or <file>(<pathsep><file>)*849if (!allowDuplicates && map.containsKey(key))850fail(key + " specified more than once to " + option(prefix));851List<String> values = map.computeIfAbsent(key, k -> new ArrayList<>());852int ntargets = 0;853for (String s : rhs.split(regex)) {854if (!s.isEmpty()) {855values.add(s);856ntargets++;857}858}859if (ntargets == 0)860fail("Target must be specified: " + option(prefix) + " " + value);861862index++;863value = getAndRemoveProperty(prefix + index);864}865866return map;867}868869/**870* Decodes the values of --add-reads, -add-exports or --add-opens871* which use the "," to separate the RHS of the option value.872*/873private static Map<String, List<String>> decode(String prefix) {874return decode(prefix, ",", true);875}876877878/**879* Gets the named system property880*/881private static String getProperty(String key) {882return System.getProperty(key);883}884885/**886* Gets and remove the named system property887*/888private static String getAndRemoveProperty(String key) {889return (String) System.getProperties().remove(key);890}891892/**893* Checks incubating status of modules in the configuration894*/895private static void checkIncubatingStatus(Configuration cf) {896String incubating = null;897for (ResolvedModule resolvedModule : cf.modules()) {898ModuleReference mref = resolvedModule.reference();899900// emit warning if the WARN_INCUBATING module resolution bit set901if (ModuleResolution.hasIncubatingWarning(mref)) {902String mn = mref.descriptor().name();903if (incubating == null) {904incubating = mn;905} else {906incubating += ", " + mn;907}908}909}910if (incubating != null)911warn("Using incubator modules: " + incubating);912}913914/**915* Throws a RuntimeException with the given message916*/917static void fail(String m) {918throw new RuntimeException(m);919}920921static void warn(String m) {922System.err.println("WARNING: " + m);923}924925static void warnUnknownModule(String option, String mn) {926warn("Unknown module: " + mn + " specified to " + option);927}928929static String unableToParse(String option, String text, String value) {930return "Unable to parse " + option + " " + text + ": " + value;931}932933private static final String ADD_MODULES = "--add-modules";934private static final String ADD_EXPORTS = "--add-exports";935private static final String ADD_OPENS = "--add-opens";936private static final String ADD_READS = "--add-reads";937private static final String PATCH_MODULE = "--patch-module";938private static final String ENABLE_NATIVE_ACCESS = "--enable-native-access";939940/*941* Returns the command-line option name corresponds to the specified942* system property prefix.943*/944static String option(String prefix) {945switch (prefix) {946case "jdk.module.addexports.":947return ADD_EXPORTS;948case "jdk.module.addopens.":949return ADD_OPENS;950case "jdk.module.addreads.":951return ADD_READS;952case "jdk.module.patch.":953return PATCH_MODULE;954case "jdk.module.addmods.":955return ADD_MODULES;956default:957throw new IllegalArgumentException(prefix);958}959}960961/**962* Wraps a (potentially not thread safe) ModuleFinder created during startup963* for use after startup.964*/965static class SafeModuleFinder implements ModuleFinder {966private final Set<ModuleReference> mrefs;967private volatile Map<String, ModuleReference> nameToModule;968969SafeModuleFinder(ModuleFinder finder) {970this.mrefs = Collections.unmodifiableSet(finder.findAll());971}972@Override973public Optional<ModuleReference> find(String name) {974Objects.requireNonNull(name);975Map<String, ModuleReference> nameToModule = this.nameToModule;976if (nameToModule == null) {977this.nameToModule = nameToModule = mrefs.stream()978.collect(Collectors.toMap(m -> m.descriptor().name(),979Function.identity()));980}981return Optional.ofNullable(nameToModule.get(name));982}983@Override984public Set<ModuleReference> findAll() {985return mrefs;986}987}988989/**990* Counters for startup performance analysis.991*/992static class Counters {993private static final boolean PUBLISH_COUNTERS;994private static final boolean PRINT_COUNTERS;995private static Map<String, Long> counters;996private static long startTime;997private static long previousTime;998999static {1000String s = System.getProperty("jdk.module.boot.usePerfData");1001if (s == null) {1002PUBLISH_COUNTERS = false;1003PRINT_COUNTERS = false;1004} else {1005PUBLISH_COUNTERS = true;1006PRINT_COUNTERS = s.equals("debug");1007counters = new LinkedHashMap<>(); // preserve insert order1008}1009}10101011/**1012* Start counting time.1013*/1014static void start() {1015if (PUBLISH_COUNTERS) {1016startTime = previousTime = System.nanoTime();1017}1018}10191020/**1021* Add a counter - storing the time difference between now and the1022* previous add or the start.1023*/1024static void add(String name) {1025if (PUBLISH_COUNTERS) {1026long current = System.nanoTime();1027long elapsed = current - previousTime;1028previousTime = current;1029counters.put(name, elapsed);1030}1031}10321033/**1034* Publish the counters to the instrumentation buffer or stdout.1035*/1036static void publish(String totalTimeName) {1037if (PUBLISH_COUNTERS) {1038long currentTime = System.nanoTime();1039for (Map.Entry<String, Long> e : counters.entrySet()) {1040String name = e.getKey();1041long value = e.getValue();1042PerfCounter.newPerfCounter(name).set(value);1043if (PRINT_COUNTERS)1044System.out.println(name + " = " + value);1045}1046long elapsedTotal = currentTime - startTime;1047PerfCounter.newPerfCounter(totalTimeName).set(elapsedTotal);1048if (PRINT_COUNTERS)1049System.out.println(totalTimeName + " = " + elapsedTotal);1050}1051}1052}1053}105410551056