/*
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.module;
import java.io.File;
import java.io.PrintStream;
import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import jdk.internal.loader.BootLoader;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.perf.PerfCounter;
Initializes/boots the module system. The boot
method is called early in the startup to initialize the module system. In summary, the boot method creates a Configuration by resolving a set of module names specified via the launcher (or equivalent) -m and --add-modules options. The modules are located on a module path that is constructed from the upgrade module path, system modules, and application module path. The Configuration is instantiated as the boot layer with each module in the the configuration defined to a class loader. /**
* Initializes/boots the module system.
*
* The {@link #boot() boot} method is called early in the startup to initialize
* the module system. In summary, the boot method creates a Configuration by
* resolving a set of module names specified via the launcher (or equivalent)
* -m and --add-modules options. The modules are located on a module path that
* is constructed from the upgrade module path, system modules, and application
* module path. The Configuration is instantiated as the boot layer with each
* module in the the configuration defined to a class loader.
*/
public final class ModuleBootstrap {
private ModuleBootstrap() { }
private static final String JAVA_BASE = "java.base";
private static final String JAVA_SE = "java.se";
// the token for "all default modules"
private static final String ALL_DEFAULT = "ALL-DEFAULT";
// the token for "all unnamed modules"
private static final String ALL_UNNAMED = "ALL-UNNAMED";
// the token for "all system modules"
private static final String ALL_SYSTEM = "ALL-SYSTEM";
// the token for "all modules on the module path"
private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
// The ModulePatcher for the initial configuration
private static final ModulePatcher patcher = initModulePatcher();
// ModuleFinders for the initial configuration
private static ModuleFinder unlimitedFinder;
private static ModuleFinder limitedFinder;
Returns the ModulePatcher for the initial configuration.
/**
* Returns the ModulePatcher for the initial configuration.
*/
public static ModulePatcher patcher() {
return patcher;
}
Returns the ModuleFinder for the initial configuration before observability
is limited by the --limit-modules command line option.
/**
* Returns the ModuleFinder for the initial configuration before observability
* is limited by the --limit-modules command line option.
*/
public static ModuleFinder unlimitedFinder() {
assert unlimitedFinder != null;
return unlimitedFinder;
}
Returns the ModuleFinder for the initial configuration.
/**
* Returns the ModuleFinder for the initial configuration.
*/
public static ModuleFinder limitedFinder() {
assert limitedFinder != null;
return limitedFinder;
}
Initialize the module system, returning the boot layer.
See Also: - initPhase2.initPhase2()
/**
* Initialize the module system, returning the boot layer.
*
* @see java.lang.System#initPhase2()
*/
public static ModuleLayer boot() {
// Step 1: Locate system modules (may be patched)
long t1 = System.nanoTime();
ModuleFinder systemModules = ModuleFinder.ofSystem();
PerfCounters.systemModulesTime.addElapsedTimeFrom(t1);
// Step 2: Define and load java.base. This patches all classes loaded
// to date so that they are members of java.base. Once java.base is
// loaded then resources in java.base are available for error messages
// needed from here on.
long t2 = System.nanoTime();
ModuleReference base = systemModules.find(JAVA_BASE).orElse(null);
if (base == null)
throw new InternalError(JAVA_BASE + " not found");
URI baseUri = base.location().orElse(null);
if (baseUri == null)
throw new InternalError(JAVA_BASE + " does not have a location");
BootLoader.loadModule(base);
Modules.defineModule(null, base.descriptor(), baseUri);
PerfCounters.defineBaseTime.addElapsedTimeFrom(t2);
// Step 2a: If --validate-modules is specified then the VM needs to
// start with only java.base, all other options are ignored.
String propValue = getAndRemoveProperty("jdk.module.minimumBoot");
if (propValue != null) {
return createMinimalBootLayer();
}
// Step 3: Construct the module path and the set of root modules to
// resolve. If --limit-modules is specified then it limits the set
// modules that are observable.
long t3 = System.nanoTime();
// --upgrade-module-path option specified to launcher
ModuleFinder upgradeModulePath
= createModulePathFinder("jdk.module.upgrade.path");
if (upgradeModulePath != null)
systemModules = ModuleFinder.compose(upgradeModulePath, systemModules);
// --module-path option specified to the launcher
ModuleFinder appModulePath = createModulePathFinder("jdk.module.path");
// The module finder: [--upgrade-module-path] system [--module-path]
ModuleFinder finder = systemModules;
if (appModulePath != null)
finder = ModuleFinder.compose(finder, appModulePath);
// The root modules to resolve
Set<String> roots = new HashSet<>();
// launcher -m option to specify the main/initial module
String mainModule = System.getProperty("jdk.module.main");
if (mainModule != null)
roots.add(mainModule);
// additional module(s) specified by --add-modules
boolean addAllDefaultModules = false;
boolean addAllSystemModules = false;
boolean addAllApplicationModules = false;
for (String mod: getExtraAddModules()) {
switch (mod) {
case ALL_DEFAULT:
addAllDefaultModules = true;
break;
case ALL_SYSTEM:
addAllSystemModules = true;
break;
case ALL_MODULE_PATH:
addAllApplicationModules = true;
break;
default :
roots.add(mod);
}
}
// --limit-modules
unlimitedFinder = finder;
propValue = getAndRemoveProperty("jdk.module.limitmods");
if (propValue != null) {
Set<String> mods = new HashSet<>();
for (String mod: propValue.split(",")) {
mods.add(mod);
}
finder = limitFinder(finder, mods, roots);
}
limitedFinder = finder;
// If there is no initial module specified then assume that the initial
// module is the unnamed module of the application class loader. This
// is implemented by resolving "java.se" and all (non-java.*) modules
// that export an API. If "java.se" is not observable then all java.*
// modules are resolved. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT
// bit set in their ModuleResolution attribute flags are excluded from
// the default set of roots.
if (mainModule == null || addAllDefaultModules) {
boolean hasJava = false;
if (systemModules.find(JAVA_SE).isPresent()) {
// java.se is a system module
if (finder == systemModules || finder.find(JAVA_SE).isPresent()) {
// java.se is observable
hasJava = true;
roots.add(JAVA_SE);
}
}
for (ModuleReference mref : systemModules.findAll()) {
String mn = mref.descriptor().name();
if (hasJava && mn.startsWith("java."))
continue;
if (ModuleResolution.doNotResolveByDefault(mref))
continue;
// add as root if observable and exports at least one package
if ((finder == systemModules || finder.find(mn).isPresent())) {
ModuleDescriptor descriptor = mref.descriptor();
for (ModuleDescriptor.Exports e : descriptor.exports()) {
if (!e.isQualified()) {
roots.add(mn);
break;
}
}
}
}
}
// If `--add-modules ALL-SYSTEM` is specified then all observable system
// modules will be resolved.
if (addAllSystemModules) {
ModuleFinder f = finder; // observable modules
systemModules.findAll()
.stream()
.map(ModuleReference::descriptor)
.map(ModuleDescriptor::name)
.filter(mn -> f.find(mn).isPresent()) // observable
.forEach(mn -> roots.add(mn));
}
// If `--add-modules ALL-MODULE-PATH` is specified then all observable
// modules on the application module path will be resolved.
if (appModulePath != null && addAllApplicationModules) {
ModuleFinder f = finder; // observable modules
appModulePath.findAll()
.stream()
.map(ModuleReference::descriptor)
.map(ModuleDescriptor::name)
.filter(mn -> f.find(mn).isPresent()) // observable
.forEach(mn -> roots.add(mn));
}
PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t3);
// Step 4: Resolve the root modules, with service binding, to create
// the configuration for the boot layer.
long t4 = System.nanoTime();
// determine if post resolution checks are needed
boolean needPostResolutionChecks = true;
if (baseUri.getScheme().equals("jrt") // toLowerCase not needed here
&& (upgradeModulePath == null)
&& (appModulePath == null)
&& (patcher.isEmpty())) {
needPostResolutionChecks = false;
}
PrintStream traceOutput = null;
propValue = getAndRemoveProperty("jdk.module.showModuleResolution");
if (propValue != null && Boolean.parseBoolean(propValue))
traceOutput = System.out;
// run the resolver to create the configuration
Configuration cf = SharedSecrets.getJavaLangModuleAccess()
.resolveAndBind(finder,
roots,
needPostResolutionChecks,
traceOutput);
PerfCounters.resolveTime.addElapsedTimeFrom(t4);
// Step 5: Map the modules in the configuration to class loaders.
// The static configuration provides the mapping of standard and JDK
// modules to the boot and platform loaders. All other modules (JDK
// tool modules, and both explicit and automatic modules on the
// application module path) are defined to the application class
// loader.
long t5 = System.nanoTime();
// mapping of modules to class loaders
Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
// check that all modules to be mapped to the boot loader will be
// loaded from the runtime image
if (needPostResolutionChecks) {
for (ResolvedModule resolvedModule : cf.modules()) {
ModuleReference mref = resolvedModule.reference();
String name = mref.descriptor().name();
ClassLoader cl = clf.apply(name);
if (cl == null) {
if (upgradeModulePath != null
&& upgradeModulePath.find(name).isPresent())
fail(name + ": cannot be loaded from upgrade module path");
if (!systemModules.find(name).isPresent())
fail(name + ": cannot be loaded from application module path");
}
}
// check if module specified in --patch-module is present
for (String mn: patcher.patchedModules()) {
if (!cf.findModule(mn).isPresent()) {
warnUnknownModule(PATCH_MODULE, mn);
}
}
}
// check for split packages in the modules mapped to the built-in loaders
if (SystemModules.hasSplitPackages() || needPostResolutionChecks) {
checkSplitPackages(cf, clf);
}
// load/register the modules with the built-in class loaders
loadModules(cf, clf);
PerfCounters.loadModulesTime.addElapsedTimeFrom(t5);
// Step 6: Define all modules to the VM
long t6 = System.nanoTime();
ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf);
PerfCounters.layerCreateTime.addElapsedTimeFrom(t6);
// Step 7: Miscellaneous
// check incubating status
checkIncubatingStatus(cf);
// --add-reads, --add-exports/--add-opens, and -illegal-access
long t7 = System.nanoTime();
addExtraReads(bootLayer);
boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer);
addIllegalAccess(bootLayer, upgradeModulePath, extraExportsOrOpens);
PerfCounters.adjustModulesTime.addElapsedTimeFrom(t7);
// total time to initialize
PerfCounters.bootstrapTime.addElapsedTimeFrom(t1);
return bootLayer;
}
Create a "minimal" boot module layer that only contains java.base.
/**
* Create a "minimal" boot module layer that only contains java.base.
*/
private static ModuleLayer createMinimalBootLayer() {
Configuration cf = SharedSecrets.getJavaLangModuleAccess()
.resolveAndBind(ModuleFinder.ofSystem(),
Set.of(JAVA_BASE),
false,
null);
Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
return ModuleLayer.empty().defineModules(cf, clf);
}
Load/register the modules to the built-in class loaders.
/**
* Load/register the modules to the built-in class loaders.
*/
private static void loadModules(Configuration cf,
Function<String, ClassLoader> clf) {
for (ResolvedModule resolvedModule : cf.modules()) {
ModuleReference mref = resolvedModule.reference();
String name = resolvedModule.name();
ClassLoader loader = clf.apply(name);
if (loader == null) {
// skip java.base as it is already loaded
if (!name.equals(JAVA_BASE)) {
BootLoader.loadModule(mref);
}
} else if (loader instanceof BuiltinClassLoader) {
((BuiltinClassLoader) loader).loadModule(mref);
}
}
}
Checks for split packages between modules defined to the built-in class
loaders.
/**
* Checks for split packages between modules defined to the built-in class
* loaders.
*/
private static void checkSplitPackages(Configuration cf,
Function<String, ClassLoader> clf) {
Map<String, String> packageToModule = new HashMap<>();
for (ResolvedModule resolvedModule : cf.modules()) {
ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
String name = descriptor.name();
ClassLoader loader = clf.apply(name);
if (loader == null || loader instanceof BuiltinClassLoader) {
for (String p : descriptor.packages()) {
String other = packageToModule.putIfAbsent(p, name);
if (other != null) {
String msg = "Package " + p + " in both module "
+ name + " and module " + other;
throw new LayerInstantiationException(msg);
}
}
}
}
}
Returns a ModuleFinder that limits observability to the given root
modules, their transitive dependences, plus a set of other modules.
/**
* Returns a ModuleFinder that limits observability to the given root
* modules, their transitive dependences, plus a set of other modules.
*/
private static ModuleFinder limitFinder(ModuleFinder finder,
Set<String> roots,
Set<String> otherMods)
{
// resolve all root modules
Configuration cf = Configuration.empty().resolve(finder,
ModuleFinder.of(),
roots);
// module name -> reference
Map<String, ModuleReference> map = new HashMap<>();
// root modules and their transitive dependences
cf.modules().stream()
.map(ResolvedModule::reference)
.forEach(mref -> map.put(mref.descriptor().name(), mref));
// additional modules
otherMods.stream()
.map(finder::find)
.flatMap(Optional::stream)
.forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));
// set of modules that are observable
Set<ModuleReference> mrefs = new HashSet<>(map.values());
return new ModuleFinder() {
@Override
public Optional<ModuleReference> find(String name) {
return Optional.ofNullable(map.get(name));
}
@Override
public Set<ModuleReference> findAll() {
return mrefs;
}
};
}
Creates a finder from the module path that is the value of the given
system property and optionally patched by --patch-module
/**
* Creates a finder from the module path that is the value of the given
* system property and optionally patched by --patch-module
*/
private static ModuleFinder createModulePathFinder(String prop) {
String s = System.getProperty(prop);
if (s == null) {
return null;
} else {
String[] dirs = s.split(File.pathSeparator);
Path[] paths = new Path[dirs.length];
int i = 0;
for (String dir: dirs) {
paths[i++] = Paths.get(dir);
}
return ModulePath.of(patcher, paths);
}
}
Initialize the module patcher for the initial configuration passed on the
value of the --patch-module options.
/**
* Initialize the module patcher for the initial configuration passed on the
* value of the --patch-module options.
*/
private static ModulePatcher initModulePatcher() {
Map<String, List<String>> map = decode("jdk.module.patch.",
File.pathSeparator,
false);
return new ModulePatcher(map);
}
Returns the set of module names specified via --add-modules options
on the command line
/**
* Returns the set of module names specified via --add-modules options
* on the command line
*/
private static Set<String> getExtraAddModules() {
String prefix = "jdk.module.addmods.";
int index = 0;
// the system property is removed after decoding
String value = getAndRemoveProperty(prefix + index);
if (value == null) {
return Collections.emptySet();
}
Set<String> modules = new HashSet<>();
while (value != null) {
for (String s : value.split(",")) {
if (s.length() > 0) modules.add(s);
}
index++;
value = getAndRemoveProperty(prefix + index);
}
return modules;
}
Process the --add-reads options to add any additional read edges that
are specified on the command-line.
/**
* Process the --add-reads options to add any additional read edges that
* are specified on the command-line.
*/
private static void addExtraReads(ModuleLayer bootLayer) {
// decode the command line options
Map<String, List<String>> map = decode("jdk.module.addreads.");
if (map.isEmpty())
return;
for (Map.Entry<String, List<String>> e : map.entrySet()) {
// the key is $MODULE
String mn = e.getKey();
Optional<Module> om = bootLayer.findModule(mn);
if (!om.isPresent()) {
warnUnknownModule(ADD_READS, mn);
continue;
}
Module m = om.get();
// the value is the set of other modules (by name)
for (String name : e.getValue()) {
if (ALL_UNNAMED.equals(name)) {
Modules.addReadsAllUnnamed(m);
} else {
om = bootLayer.findModule(name);
if (om.isPresent()) {
Modules.addReads(m, om.get());
} else {
warnUnknownModule(ADD_READS, name);
}
}
}
}
}
Process the --add-exports and --add-opens options to export/open
additional packages specified on the command-line.
/**
* Process the --add-exports and --add-opens options to export/open
* additional packages specified on the command-line.
*/
private static boolean addExtraExportsAndOpens(ModuleLayer bootLayer) {
boolean extraExportsOrOpens = false;
// --add-exports
String prefix = "jdk.module.addexports.";
Map<String, List<String>> extraExports = decode(prefix);
if (!extraExports.isEmpty()) {
addExtraExportsOrOpens(bootLayer, extraExports, false);
extraExportsOrOpens = true;
}
// --add-opens
prefix = "jdk.module.addopens.";
Map<String, List<String>> extraOpens = decode(prefix);
if (!extraOpens.isEmpty()) {
addExtraExportsOrOpens(bootLayer, extraOpens, true);
extraExportsOrOpens = true;
}
return extraExportsOrOpens;
}
private static void addExtraExportsOrOpens(ModuleLayer bootLayer,
Map<String, List<String>> map,
boolean opens)
{
String option = opens ? ADD_OPENS : ADD_EXPORTS;
for (Map.Entry<String, List<String>> e : map.entrySet()) {
// the key is $MODULE/$PACKAGE
String key = e.getKey();
String[] s = key.split("/");
if (s.length != 2)
fail(unableToParse(option, "<module>/<package>", key));
String mn = s[0];
String pn = s[1];
if (mn.isEmpty() || pn.isEmpty())
fail(unableToParse(option, "<module>/<package>", key));
// The exporting module is in the boot layer
Module m;
Optional<Module> om = bootLayer.findModule(mn);
if (!om.isPresent()) {
warnUnknownModule(option, mn);
continue;
}
m = om.get();
if (!m.getDescriptor().packages().contains(pn)) {
warn("package " + pn + " not in " + mn);
continue;
}
// the value is the set of modules to export to (by name)
for (String name : e.getValue()) {
boolean allUnnamed = false;
Module other = null;
if (ALL_UNNAMED.equals(name)) {
allUnnamed = true;
} else {
om = bootLayer.findModule(name);
if (om.isPresent()) {
other = om.get();
} else {
warnUnknownModule(option, name);
continue;
}
}
if (allUnnamed) {
if (opens) {
Modules.addOpensToAllUnnamed(m, pn);
} else {
Modules.addExportsToAllUnnamed(m, pn);
}
} else {
if (opens) {
Modules.addOpens(m, pn, other);
} else {
Modules.addExports(m, pn, other);
}
}
}
}
}
Process the --illegal-access option (and its default) to open packages
of system modules in the boot layer to code in unnamed modules.
/**
* Process the --illegal-access option (and its default) to open packages
* of system modules in the boot layer to code in unnamed modules.
*/
private static void addIllegalAccess(ModuleLayer bootLayer,
ModuleFinder upgradeModulePath,
boolean extraExportsOrOpens) {
String value = getAndRemoveProperty("jdk.module.illegalAccess");
IllegalAccessLogger.Mode mode = IllegalAccessLogger.Mode.ONESHOT;
if (value != null) {
switch (value) {
case "deny":
return;
case "permit":
break;
case "warn":
mode = IllegalAccessLogger.Mode.WARN;
break;
case "debug":
mode = IllegalAccessLogger.Mode.DEBUG;
break;
default:
fail("Value specified to --illegal-access not recognized:"
+ " '" + value + "'");
return;
}
}
IllegalAccessLogger.Builder builder
= new IllegalAccessLogger.Builder(mode, System.err);
Map<String, Set<String>> map1 = SystemModules.concealedPackagesToOpen();
Map<String, Set<String>> map2 = SystemModules.exportedPackagesToOpen();
if (map1.isEmpty() && map2.isEmpty()) {
// need to generate maps when on exploded build
IllegalAccessMaps maps = IllegalAccessMaps.generate(limitedFinder());
map1 = maps.concealedPackagesToOpen();
map2 = maps.exportedPackagesToOpen();
}
// open specific packages in the system modules
for (Module m : bootLayer.modules()) {
ModuleDescriptor descriptor = m.getDescriptor();
String name = m.getName();
// skip open modules
if (descriptor.isOpen()) {
continue;
}
// skip modules loaded from the upgrade module path
if (upgradeModulePath != null
&& upgradeModulePath.find(name).isPresent()) {
continue;
}
Set<String> concealedPackages = map1.getOrDefault(name, Set.of());
Set<String> exportedPackages = map2.getOrDefault(name, Set.of());
// refresh the set of concealed and exported packages if needed
if (extraExportsOrOpens) {
concealedPackages = new HashSet<>(concealedPackages);
exportedPackages = new HashSet<>(exportedPackages);
Iterator<String> iterator = concealedPackages.iterator();
while (iterator.hasNext()) {
String pn = iterator.next();
if (m.isExported(pn, BootLoader.getUnnamedModule())) {
// concealed package is exported to ALL-UNNAMED
iterator.remove();
exportedPackages.add(pn);
}
}
iterator = exportedPackages.iterator();
while (iterator.hasNext()) {
String pn = iterator.next();
if (m.isOpen(pn, BootLoader.getUnnamedModule())) {
// exported package is opened to ALL-UNNAMED
iterator.remove();
}
}
}
// log reflective access to all types in concealed packages
builder.logAccessToConcealedPackages(m, concealedPackages);
// log reflective access to non-public members/types in exported packages
builder.logAccessToExportedPackages(m, exportedPackages);
// open the packages to unnamed modules
JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
jla.addOpensToAllUnnamed(m, concat(concealedPackages.iterator(),
exportedPackages.iterator()));
}
builder.complete();
}
Decodes the values of --add-reads, -add-exports, --add-opens or
--patch-modules options that are encoded in system properties.
Params: - prefix – the system property prefix
@praam regex the regex for splitting the RHS of the option value
/**
* Decodes the values of --add-reads, -add-exports, --add-opens or
* --patch-modules options that are encoded in system properties.
*
* @param prefix the system property prefix
* @praam regex the regex for splitting the RHS of the option value
*/
private static Map<String, List<String>> decode(String prefix,
String regex,
boolean allowDuplicates) {
int index = 0;
// the system property is removed after decoding
String value = getAndRemoveProperty(prefix + index);
if (value == null)
return Collections.emptyMap();
Map<String, List<String>> map = new HashMap<>();
while (value != null) {
int pos = value.indexOf('=');
if (pos == -1)
fail(unableToParse(option(prefix), "<module>=<value>", value));
if (pos == 0)
fail(unableToParse(option(prefix), "<module>=<value>", value));
// key is <module> or <module>/<package>
String key = value.substring(0, pos);
String rhs = value.substring(pos+1);
if (rhs.isEmpty())
fail(unableToParse(option(prefix), "<module>=<value>", value));
// value is <module>(,<module>)* or <file>(<pathsep><file>)*
if (!allowDuplicates && map.containsKey(key))
fail(key + " specified more than once to " + option(prefix));
List<String> values = map.computeIfAbsent(key, k -> new ArrayList<>());
int ntargets = 0;
for (String s : rhs.split(regex)) {
if (s.length() > 0) {
values.add(s);
ntargets++;
}
}
if (ntargets == 0)
fail("Target must be specified: " + option(prefix) + " " + value);
index++;
value = getAndRemoveProperty(prefix + index);
}
return map;
}
Decodes the values of --add-reads, -add-exports or --add-opens
which use the "," to separate the RHS of the option value.
/**
* Decodes the values of --add-reads, -add-exports or --add-opens
* which use the "," to separate the RHS of the option value.
*/
private static Map<String, List<String>> decode(String prefix) {
return decode(prefix, ",", true);
}
Gets and remove the named system property
/**
* Gets and remove the named system property
*/
private static String getAndRemoveProperty(String key) {
return (String)System.getProperties().remove(key);
}
Checks incubating status of modules in the configuration
/**
* Checks incubating status of modules in the configuration
*/
private static void checkIncubatingStatus(Configuration cf) {
String incubating = null;
for (ResolvedModule resolvedModule : cf.modules()) {
ModuleReference mref = resolvedModule.reference();
// emit warning if the WARN_INCUBATING module resolution bit set
if (ModuleResolution.hasIncubatingWarning(mref)) {
String mn = mref.descriptor().name();
if (incubating == null) {
incubating = mn;
} else {
incubating += ", " + mn;
}
}
}
if (incubating != null)
warn("Using incubator modules: " + incubating);
}
Throws a RuntimeException with the given message
/**
* Throws a RuntimeException with the given message
*/
static void fail(String m) {
throw new RuntimeException(m);
}
static void warn(String m) {
System.err.println("WARNING: " + m);
}
static void warnUnknownModule(String option, String mn) {
warn("Unknown module: " + mn + " specified to " + option);
}
static String unableToParse(String option, String text, String value) {
return "Unable to parse " + option + " " + text + ": " + value;
}
private static final String ADD_MODULES = "--add-modules";
private static final String ADD_EXPORTS = "--add-exports";
private static final String ADD_OPENS = "--add-opens";
private static final String ADD_READS = "--add-reads";
private static final String PATCH_MODULE = "--patch-module";
/*
* Returns the command-line option name corresponds to the specified
* system property prefix.
*/
static String option(String prefix) {
switch (prefix) {
case "jdk.module.addexports.":
return ADD_EXPORTS;
case "jdk.module.addopens.":
return ADD_OPENS;
case "jdk.module.addreads.":
return ADD_READS;
case "jdk.module.patch.":
return PATCH_MODULE;
case "jdk.module.addmods.":
return ADD_MODULES;
default:
throw new IllegalArgumentException(prefix);
}
}
static <T> Iterator<T> concat(Iterator<T> iterator1, Iterator<T> iterator2) {
return new Iterator<T>() {
@Override
public boolean hasNext() {
return iterator1.hasNext() || iterator2.hasNext();
}
@Override
public T next() {
if (iterator1.hasNext()) return iterator1.next();
if (iterator2.hasNext()) return iterator2.next();
throw new NoSuchElementException();
}
};
}
static class PerfCounters {
static PerfCounter systemModulesTime
= PerfCounter.newPerfCounter("jdk.module.bootstrap.systemModulesTime");
static PerfCounter defineBaseTime
= PerfCounter.newPerfCounter("jdk.module.bootstrap.defineBaseTime");
static PerfCounter optionsAndRootsTime
= PerfCounter.newPerfCounter("jdk.module.bootstrap.optionsAndRootsTime");
static PerfCounter resolveTime
= PerfCounter.newPerfCounter("jdk.module.bootstrap.resolveTime");
static PerfCounter layerCreateTime
= PerfCounter.newPerfCounter("jdk.module.bootstrap.layerCreateTime");
static PerfCounter loadModulesTime
= PerfCounter.newPerfCounter("jdk.module.bootstrap.loadModulesTime");
static PerfCounter adjustModulesTime
= PerfCounter.newPerfCounter("jdk.module.bootstrap.adjustModulesTime");
static PerfCounter bootstrapTime
= PerfCounter.newPerfCounter("jdk.module.bootstrap.totalTime");
}
}