1 /*
   2  * Copyright (c) 2014, 2017, 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.lang.reflect.Layer;
  36 import java.lang.reflect.LayerInstantiationException;
  37 import java.lang.reflect.Module;
  38 import java.net.URI;
  39 import java.nio.file.Path;
  40 import java.nio.file.Paths;
  41 import java.util.ArrayList;
  42 import java.util.Collections;
  43 import java.util.HashMap;
  44 import java.util.HashSet;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.Optional;
  48 import java.util.Set;
  49 import java.util.function.Function;
  50 
  51 import jdk.internal.loader.BootLoader;
  52 import jdk.internal.loader.BuiltinClassLoader;
  53 import jdk.internal.misc.SharedSecrets;
  54 import jdk.internal.perf.PerfCounter;
  55 
  56 /**
  57  * Initializes/boots the module system.
  58  *
  59  * The {@link #boot() boot} method is called early in the startup to initialize
  60  * the module system. In summary, the boot method creates a Configuration by
  61  * resolving a set of module names specified via the launcher (or equivalent)
  62  * -m and --add-modules options. The modules are located on a module path that
  63  * is constructed from the upgrade module path, system modules, and application
  64  * module path. The Configuration is instantiated as the boot Layer with each
  65  * module in the the configuration defined to one of the built-in class loaders.
  66  */
  67 
  68 public final class ModuleBootstrap {
  69     private ModuleBootstrap() { }
  70 
  71     private static final String JAVA_BASE = "java.base";
  72 
  73     private static final String JAVA_SE = "java.se";
  74 
  75     // the token for "all default modules"
  76     private static final String ALL_DEFAULT = "ALL-DEFAULT";
  77 
  78     // the token for "all unnamed modules"
  79     private static final String ALL_UNNAMED = "ALL-UNNAMED";
  80 
  81     // the token for "all system modules"
  82     private static final String ALL_SYSTEM = "ALL-SYSTEM";
  83 
  84     // the token for "all modules on the module path"
  85     private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
  86 
  87     // The ModulePatcher for the initial configuration
  88     private static final ModulePatcher patcher = initModulePatcher();
  89 
  90     // ModuleFinder for the initial configuration
  91     private static ModuleFinder initialFinder;
  92 
  93     /**
  94      * Returns the ModulePatcher for the initial configuration.
  95      */
  96     public static ModulePatcher patcher() {
  97         return patcher;
  98     }
  99 
 100     /**
 101      * Returns the ModuleFinder for the initial configuration
 102      */
 103     public static ModuleFinder finder() {
 104         assert initialFinder != null;
 105         return initialFinder;
 106     }
 107 
 108     /**
 109      * Initialize the module system, returning the boot Layer.
 110      *
 111      * @see java.lang.System#initPhase2()
 112      */
 113     public static Layer boot() {
 114 
 115         long t0 = System.nanoTime();
 116 
 117         // system modules (may be patched)
 118         ModuleFinder systemModules = ModuleFinder.ofSystem();
 119 
 120         PerfCounters.systemModulesTime.addElapsedTimeFrom(t0);
 121 
 122 
 123         long t1 = System.nanoTime();
 124 
 125         // Once we have the system modules then we define the base module to
 126         // the VM. We do this here so that java.base is defined as early as
 127         // possible and also that resources in the base module can be located
 128         // for error messages that may happen from here on.
 129         ModuleReference base = systemModules.find(JAVA_BASE).orElse(null);
 130         if (base == null)
 131             throw new InternalError(JAVA_BASE + " not found");
 132         URI baseUri = base.location().orElse(null);
 133         if (baseUri == null)
 134             throw new InternalError(JAVA_BASE + " does not have a location");
 135         BootLoader.loadModule(base);
 136         Modules.defineModule(null, base.descriptor(), baseUri);
 137 
 138         PerfCounters.defineBaseTime.addElapsedTimeFrom(t1);
 139 
 140 
 141         long t2 = System.nanoTime();
 142 
 143         // --upgrade-module-path option specified to launcher
 144         ModuleFinder upgradeModulePath
 145             = createModulePathFinder("jdk.module.upgrade.path");
 146         if (upgradeModulePath != null)
 147             systemModules = ModuleFinder.compose(upgradeModulePath, systemModules);
 148 
 149         // --module-path option specified to the launcher
 150         ModuleFinder appModulePath = createModulePathFinder("jdk.module.path");
 151 
 152         // The module finder: [--upgrade-module-path] system [--module-path]
 153         ModuleFinder finder = systemModules;
 154         if (appModulePath != null)
 155             finder = ModuleFinder.compose(finder, appModulePath);
 156 
 157         // The root modules to resolve
 158         Set<String> roots = new HashSet<>();
 159 
 160         // launcher -m option to specify the main/initial module
 161         String mainModule = System.getProperty("jdk.module.main");
 162         if (mainModule != null)
 163             roots.add(mainModule);
 164 
 165         // additional module(s) specified by --add-modules
 166         boolean addAllDefaultModules = false;
 167         boolean addAllSystemModules = false;
 168         boolean addAllApplicationModules = false;
 169         for (String mod: getExtraAddModules()) {
 170             switch (mod) {
 171                 case ALL_DEFAULT:
 172                     addAllDefaultModules = true;
 173                     break;
 174                 case ALL_SYSTEM:
 175                     addAllSystemModules = true;
 176                     break;
 177                 case ALL_MODULE_PATH:
 178                     addAllApplicationModules = true;
 179                     break;
 180                 default :
 181                     roots.add(mod);
 182             }
 183         }
 184 
 185         // --limit-modules
 186         String propValue = getAndRemoveProperty("jdk.module.limitmods");
 187         if (propValue != null) {
 188             Set<String> mods = new HashSet<>();
 189             for (String mod: propValue.split(",")) {
 190                 mods.add(mod);
 191             }
 192             finder = limitFinder(finder, mods, roots);
 193         }
 194 
 195         // If there is no initial module specified then assume that the initial
 196         // module is the unnamed module of the application class loader. This
 197         // is implemented by resolving "java.se" and all (non-java.*) modules
 198         // that export an API. If "java.se" is not observable then all java.*
 199         // modules are resolved. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT
 200         // bit set in their ModuleResolution attribute flags are excluded from
 201         // the default set of roots.
 202         if (mainModule == null || addAllDefaultModules) {
 203             boolean hasJava = false;
 204             if (systemModules.find(JAVA_SE).isPresent()) {
 205                 // java.se is a system module
 206                 if (finder == systemModules || finder.find(JAVA_SE).isPresent()) {
 207                     // java.se is observable
 208                     hasJava = true;
 209                     roots.add(JAVA_SE);
 210                 }
 211             }
 212 
 213             for (ModuleReference mref : systemModules.findAll()) {
 214                 String mn = mref.descriptor().name();
 215                 if (hasJava && mn.startsWith("java."))
 216                     continue;
 217 
 218                 if (ModuleResolution.doNotResolveByDefault(mref))
 219                     continue;
 220 
 221                 // add as root if observable and exports at least one package
 222                 if ((finder == systemModules || finder.find(mn).isPresent())) {
 223                     ModuleDescriptor descriptor = mref.descriptor();
 224                     for (ModuleDescriptor.Exports e : descriptor.exports()) {
 225                         if (!e.isQualified()) {
 226                             roots.add(mn);
 227                             break;
 228                         }
 229                     }
 230                 }
 231             }
 232         }
 233 
 234         // If `--add-modules ALL-SYSTEM` is specified then all observable system
 235         // modules will be resolved.
 236         if (addAllSystemModules) {
 237             ModuleFinder f = finder;  // observable modules
 238             systemModules.findAll()
 239                 .stream()
 240                 .filter(mref -> !ModuleResolution.doNotResolveByDefault(mref))
 241                 .map(ModuleReference::descriptor)
 242                 .map(ModuleDescriptor::name)
 243                 .filter(mn -> f.find(mn).isPresent())  // observable
 244                 .forEach(mn -> roots.add(mn));
 245         }
 246 
 247         // If `--add-modules ALL-MODULE-PATH` is specified then all observable
 248         // modules on the application module path will be resolved.
 249         if (appModulePath != null && addAllApplicationModules) {
 250             ModuleFinder f = finder;  // observable modules
 251             appModulePath.findAll()
 252                 .stream()
 253                 .map(ModuleReference::descriptor)
 254                 .map(ModuleDescriptor::name)
 255                 .filter(mn -> f.find(mn).isPresent())  // observable
 256                 .forEach(mn -> roots.add(mn));
 257         }
 258 
 259         PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t2);
 260 
 261 
 262         long t3 = System.nanoTime();
 263 
 264         // determine if post resolution checks are needed
 265         boolean needPostResolutionChecks = true;
 266         if (baseUri.getScheme().equals("jrt")   // toLowerCase not needed here
 267                 && (upgradeModulePath == null)
 268                 && (appModulePath == null)
 269                 && (patcher.isEmpty())) {
 270             needPostResolutionChecks = false;
 271         }
 272 
 273         PrintStream traceOutput = null;
 274         if (Boolean.getBoolean("jdk.launcher.traceResolver"))
 275             traceOutput = System.out;
 276 
 277         // run the resolver to create the configuration
 278         Configuration cf = SharedSecrets.getJavaLangModuleAccess()
 279                 .resolveAndBind(finder,
 280                                 roots,
 281                                 needPostResolutionChecks,
 282                                 traceOutput);
 283 
 284         // time to create configuration
 285         PerfCounters.resolveTime.addElapsedTimeFrom(t3);
 286 
 287         // check module names and incubating status
 288         checkModuleNamesAndStatus(cf);
 289 
 290         // mapping of modules to class loaders
 291         Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
 292 
 293         // check that all modules to be mapped to the boot loader will be
 294         // loaded from the runtime image
 295         if (needPostResolutionChecks) {
 296             for (ResolvedModule resolvedModule : cf.modules()) {
 297                 ModuleReference mref = resolvedModule.reference();
 298                 String name = mref.descriptor().name();
 299                 ClassLoader cl = clf.apply(name);
 300                 if (cl == null) {
 301 
 302                     if (upgradeModulePath != null
 303                             && upgradeModulePath.find(name).isPresent())
 304                         fail(name + ": cannot be loaded from upgrade module path");
 305 
 306                     if (!systemModules.find(name).isPresent())
 307                         fail(name + ": cannot be loaded from application module path");
 308                 }
 309             }
 310 
 311             // check if module specified in --patch-module is present
 312             for (String mn: patcher.patchedModules()) {
 313                 if (!cf.findModule(mn).isPresent()) {
 314                     warnUnknownModule(PATCH_MODULE, mn);
 315                 }
 316             }
 317         }
 318 
 319         // if needed check that there are no split packages in the set of
 320         // resolved modules for the boot layer
 321         if (SystemModules.hasSplitPackages() || needPostResolutionChecks) {
 322             Map<String, String> packageToModule = new HashMap<>();
 323             for (ResolvedModule resolvedModule : cf.modules()) {
 324                 ModuleDescriptor descriptor =
 325                     resolvedModule.reference().descriptor();
 326                 String name = descriptor.name();
 327                 for (String p : descriptor.packages()) {
 328                     String other = packageToModule.putIfAbsent(p, name);
 329                     if (other != null) {
 330                         String msg = "Package " + p + " in both module "
 331                                      + name + " and module " + other;
 332                         throw new LayerInstantiationException(msg);
 333                     }
 334                 }
 335             }
 336         }
 337 
 338         long t4 = System.nanoTime();
 339 
 340         // define modules to VM/runtime
 341         Layer bootLayer = Layer.empty().defineModules(cf, clf);
 342 
 343         PerfCounters.layerCreateTime.addElapsedTimeFrom(t4);
 344 
 345 
 346         long t5 = System.nanoTime();
 347 
 348         // define the module to its class loader, except java.base
 349         for (ResolvedModule resolvedModule : cf.modules()) {
 350             ModuleReference mref = resolvedModule.reference();
 351             String name = mref.descriptor().name();
 352             ClassLoader cl = clf.apply(name);
 353             if (cl == null) {
 354                 if (!name.equals(JAVA_BASE)) BootLoader.loadModule(mref);
 355             } else {
 356                 ((BuiltinClassLoader)cl).loadModule(mref);
 357             }
 358         }
 359 
 360         PerfCounters.loadModulesTime.addElapsedTimeFrom(t5);
 361 
 362 
 363         // --add-reads, --add-exports/--add-opens
 364         addExtraReads(bootLayer);
 365         addExtraExportsAndOpens(bootLayer);
 366 
 367         // total time to initialize
 368         PerfCounters.bootstrapTime.addElapsedTimeFrom(t0);
 369 
 370         // remember the ModuleFinder
 371         initialFinder = finder;
 372 
 373         return bootLayer;
 374     }
 375 
 376     /**
 377      * Returns a ModuleFinder that limits observability to the given root
 378      * modules, their transitive dependences, plus a set of other modules.
 379      */
 380     private static ModuleFinder limitFinder(ModuleFinder finder,
 381                                             Set<String> roots,
 382                                             Set<String> otherMods)
 383     {
 384         // resolve all root modules
 385         Configuration cf = Configuration.empty().resolve(finder,
 386                                                          ModuleFinder.of(),
 387                                                          roots);
 388 
 389         // module name -> reference
 390         Map<String, ModuleReference> map = new HashMap<>();
 391 
 392         // root modules and their transitive dependences
 393         cf.modules().stream()
 394             .map(ResolvedModule::reference)
 395             .forEach(mref -> map.put(mref.descriptor().name(), mref));
 396 
 397         // additional modules
 398         otherMods.stream()
 399             .map(finder::find)
 400             .flatMap(Optional::stream)
 401             .forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));
 402 
 403         // set of modules that are observable
 404         Set<ModuleReference> mrefs = new HashSet<>(map.values());
 405 
 406         return new ModuleFinder() {
 407             @Override
 408             public Optional<ModuleReference> find(String name) {
 409                 return Optional.ofNullable(map.get(name));
 410             }
 411             @Override
 412             public Set<ModuleReference> findAll() {
 413                 return mrefs;
 414             }
 415         };
 416     }
 417 
 418     /**
 419      * Creates a finder from the module path that is the value of the given
 420      * system property and optionally patched by --patch-module
 421      */
 422     private static ModuleFinder createModulePathFinder(String prop) {
 423         String s = System.getProperty(prop);
 424         if (s == null) {
 425             return null;
 426         } else {
 427             String[] dirs = s.split(File.pathSeparator);
 428             Path[] paths = new Path[dirs.length];
 429             int i = 0;
 430             for (String dir: dirs) {
 431                 paths[i++] = Paths.get(dir);
 432             }
 433             return ModulePath.of(patcher, paths);
 434         }
 435     }
 436 
 437 
 438     /**
 439      * Initialize the module patcher for the initial configuration passed on the
 440      * value of the --patch-module options.
 441      */
 442     private static ModulePatcher initModulePatcher() {
 443         Map<String, List<String>> map = decode("jdk.module.patch.",
 444                                                File.pathSeparator,
 445                                                false);
 446         return new ModulePatcher(map);
 447     }
 448 
 449     /**
 450      * Returns the set of module names specified via --add-modules options
 451      * on the command line
 452      */
 453     private static Set<String> getExtraAddModules() {
 454         String prefix = "jdk.module.addmods.";
 455         int index = 0;
 456 
 457         // the system property is removed after decoding
 458         String value = getAndRemoveProperty(prefix + index);
 459         if (value == null) {
 460             return Collections.emptySet();
 461         }
 462 
 463         Set<String> modules = new HashSet<>();
 464         while (value != null) {
 465             for (String s : value.split(",")) {
 466                 if (s.length() > 0) modules.add(s);
 467             }
 468             index++;
 469             value = getAndRemoveProperty(prefix + index);
 470         }
 471 
 472         return modules;
 473     }
 474 
 475     /**
 476      * Process the --add-reads options to add any additional read edges that
 477      * are specified on the command-line.
 478      */
 479     private static void addExtraReads(Layer bootLayer) {
 480 
 481         // decode the command line options
 482         Map<String, List<String>> map = decode("jdk.module.addreads.");
 483         if (map.isEmpty())
 484             return;
 485 
 486         for (Map.Entry<String, List<String>> e : map.entrySet()) {
 487 
 488             // the key is $MODULE
 489             String mn = e.getKey();
 490             Optional<Module> om = bootLayer.findModule(mn);
 491             if (!om.isPresent()) {
 492                 warnUnknownModule(ADD_READS, mn);
 493                 continue;
 494             }
 495             Module m = om.get();
 496 
 497             // the value is the set of other modules (by name)
 498             for (String name : e.getValue()) {
 499                 if (ALL_UNNAMED.equals(name)) {
 500                     Modules.addReadsAllUnnamed(m);
 501                 } else {
 502                     om = bootLayer.findModule(name);
 503                     if (om.isPresent()) {
 504                         Modules.addReads(m, om.get());
 505                     } else {
 506                         warnUnknownModule(ADD_READS, name);
 507                     }
 508                 }
 509             }
 510         }
 511     }
 512 
 513     /**
 514      * Process the --add-exports and --add-opens options to export/open
 515      * additional packages specified on the command-line.
 516      */
 517     private static void addExtraExportsAndOpens(Layer bootLayer) {
 518         IllegalAccessLogger.Builder builder = new IllegalAccessLogger.Builder();
 519 
 520         // --add-exports
 521         String prefix = "jdk.module.addexports.";
 522         Map<String, List<String>> extraExports = decode(prefix);
 523         if (!extraExports.isEmpty()) {
 524             addExtraExportsOrOpens(bootLayer, extraExports, false, builder);
 525         }
 526 
 527         // --add-opens
 528         prefix = "jdk.module.addopens.";
 529         Map<String, List<String>> extraOpens = decode(prefix);
 530         if (!extraOpens.isEmpty()) {
 531             addExtraExportsOrOpens(bootLayer, extraOpens, true, builder);
 532         }
 533 
 534         // --permit-illegal-access
 535         if (getAndRemoveProperty("jdk.module.permitIllegalAccess") != null) {
 536             warn("--permit-illegal-access will be removed in the next major release");
 537             bootLayer.modules().stream().forEach(m -> {
 538                 m.getDescriptor()
 539                  .packages()
 540                  .stream()
 541                  .filter(pn -> !m.isOpen(pn))
 542                  .forEach(pn -> {
 543                      builder.logAccessToOpenPackage(m, pn, "--permit-illegal-access");
 544                      Modules.addOpensToAllUnnamed(m, pn);
 545                  });
 546             });
 547         }
 548 
 549         IllegalAccessLogger.setIllegalAccessLogger(builder.build());
 550     }
 551 
 552     private static void addExtraExportsOrOpens(Layer bootLayer,
 553                                                Map<String, List<String>> map,
 554                                                boolean opens,
 555                                                IllegalAccessLogger.Builder builder)
 556     {
 557         String option = opens ? ADD_OPENS : ADD_EXPORTS;
 558         for (Map.Entry<String, List<String>> e : map.entrySet()) {
 559 
 560             // the key is $MODULE/$PACKAGE
 561             String key = e.getKey();
 562             String[] s = key.split("/");
 563             if (s.length != 2)
 564                 fail(unableToParse(option, "<module>/<package>", key));
 565 
 566             String mn = s[0];
 567             String pn = s[1];
 568             if (mn.isEmpty() || pn.isEmpty())
 569                 fail(unableToParse(option, "<module>/<package>", key));
 570 
 571             // The exporting module is in the boot layer
 572             Module m;
 573             Optional<Module> om = bootLayer.findModule(mn);
 574             if (!om.isPresent()) {
 575                 warnUnknownModule(option, mn);
 576                 continue;
 577             }
 578 
 579             m = om.get();
 580 
 581             if (!m.getDescriptor().packages().contains(pn)) {
 582                 warn("package " + pn + " not in " + mn);
 583                 continue;
 584             }
 585 
 586             // the value is the set of modules to export to (by name)
 587             for (String name : e.getValue()) {
 588                 boolean allUnnamed = false;
 589                 Module other = null;
 590                 if (ALL_UNNAMED.equals(name)) {
 591                     allUnnamed = true;
 592                 } else {
 593                     om = bootLayer.findModule(name);
 594                     if (om.isPresent()) {
 595                         other = om.get();
 596                     } else {
 597                         warnUnknownModule(option, name);
 598                         continue;
 599                     }
 600                 }
 601                 if (allUnnamed) {
 602                     if (opens) {
 603                         builder.logAccessToOpenPackage(m, pn, option);
 604                         Modules.addOpensToAllUnnamed(m, pn);
 605                     } else {
 606                         builder.logAccessToExportedPackage(m, pn, option);
 607                         Modules.addExportsToAllUnnamed(m, pn);
 608                     }
 609                 } else {
 610                     if (opens) {
 611                         Modules.addOpens(m, pn, other);
 612                     } else {
 613                         Modules.addExports(m, pn, other);
 614                     }
 615                 }
 616 
 617             }
 618         }
 619     }
 620 
 621     /**
 622      * Decodes the values of --add-reads, -add-exports, --add-opens or
 623      * --patch-modules options that are encoded in system properties.
 624      *
 625      * @param prefix the system property prefix
 626      * @praam regex the regex for splitting the RHS of the option value
 627      */
 628     private static Map<String, List<String>> decode(String prefix,
 629                                                     String regex,
 630                                                     boolean allowDuplicates) {
 631         int index = 0;
 632         // the system property is removed after decoding
 633         String value = getAndRemoveProperty(prefix + index);
 634         if (value == null)
 635             return Collections.emptyMap();
 636 
 637         Map<String, List<String>> map = new HashMap<>();
 638 
 639         while (value != null) {
 640 
 641             int pos = value.indexOf('=');
 642             if (pos == -1)
 643                 fail(unableToParse(option(prefix), "<module>=<value>", value));
 644             if (pos == 0)
 645                 fail(unableToParse(option(prefix), "<module>=<value>", value));
 646 
 647             // key is <module> or <module>/<package>
 648             String key = value.substring(0, pos);
 649 
 650             String rhs = value.substring(pos+1);
 651             if (rhs.isEmpty())
 652                 fail(unableToParse(option(prefix), "<module>=<value>", value));
 653 
 654             // value is <module>(,<module>)* or <file>(<pathsep><file>)*
 655             if (!allowDuplicates && map.containsKey(key))
 656                 fail(key + " specified more than once to " + option(prefix));
 657             List<String> values = map.computeIfAbsent(key, k -> new ArrayList<>());
 658             int ntargets = 0;
 659             for (String s : rhs.split(regex)) {
 660                 if (s.length() > 0) {
 661                     values.add(s);
 662                     ntargets++;
 663                 }
 664             }
 665             if (ntargets == 0)
 666                 fail("Target must be specified: " + option(prefix) + " " + value);
 667 
 668             index++;
 669             value = getAndRemoveProperty(prefix + index);
 670         }
 671 
 672         return map;
 673     }
 674 
 675     /**
 676      * Decodes the values of --add-reads, -add-exports or --add-opens
 677      * which use the "," to separate the RHS of the option value.
 678      */
 679     private static Map<String, List<String>> decode(String prefix) {
 680         return decode(prefix, ",", true);
 681     }
 682 
 683     /**
 684      * Gets and remove the named system property
 685      */
 686     private static String getAndRemoveProperty(String key) {
 687         return (String)System.getProperties().remove(key);
 688     }
 689 
 690     /**
 691      * Checks the names and resolution bit of each module in the configuration,
 692      * emitting warnings if needed.
 693      */
 694     private static void checkModuleNamesAndStatus(Configuration cf) {
 695         String incubating = null;
 696         for (ResolvedModule rm : cf.modules()) {
 697             ModuleReference mref = rm.reference();
 698             String mn = mref.descriptor().name();
 699 
 700             // emit warning if the WARN_INCUBATING module resolution bit set
 701             if (ModuleResolution.hasIncubatingWarning(mref)) {
 702                 if (incubating == null) {
 703                     incubating = mn;
 704                 } else {
 705                     incubating += ", " + mn;
 706                 }
 707             }
 708         }
 709         if (incubating != null)
 710             warn("Using incubator modules: " + incubating);
 711     }
 712 
 713     /**
 714      * Throws a RuntimeException with the given message
 715      */
 716     static void fail(String m) {
 717         throw new RuntimeException(m);
 718     }
 719 
 720     static void warn(String m) {
 721         System.err.println("WARNING: " + m);
 722     }
 723 
 724     static void warnUnknownModule(String option, String mn) {
 725         warn("Unknown module: " + mn + " specified to " + option);
 726     }
 727 
 728     static String unableToParse(String option, String text, String value) {
 729         return "Unable to parse " +  option + " " + text + ": " + value;
 730     }
 731 
 732     private static final String ADD_MODULES  = "--add-modules";
 733     private static final String ADD_EXPORTS  = "--add-exports";
 734     private static final String ADD_OPENS    = "--add-opens";
 735     private static final String ADD_READS    = "--add-reads";
 736     private static final String PATCH_MODULE = "--patch-module";
 737 
 738 
 739     /*
 740      * Returns the command-line option name corresponds to the specified
 741      * system property prefix.
 742      */
 743     static String option(String prefix) {
 744         switch (prefix) {
 745             case "jdk.module.addexports.":
 746                 return ADD_EXPORTS;
 747             case "jdk.module.addopens.":
 748                 return ADD_OPENS;
 749             case "jdk.module.addreads.":
 750                 return ADD_READS;
 751             case "jdk.module.patch.":
 752                 return PATCH_MODULE;
 753             case "jdk.module.addmods.":
 754                 return ADD_MODULES;
 755             default:
 756                 throw new IllegalArgumentException(prefix);
 757         }
 758     }
 759 
 760     static class PerfCounters {
 761 
 762         static PerfCounter systemModulesTime
 763             = PerfCounter.newPerfCounter("jdk.module.bootstrap.systemModulesTime");
 764         static PerfCounter defineBaseTime
 765             = PerfCounter.newPerfCounter("jdk.module.bootstrap.defineBaseTime");
 766         static PerfCounter optionsAndRootsTime
 767             = PerfCounter.newPerfCounter("jdk.module.bootstrap.optionsAndRootsTime");
 768         static PerfCounter resolveTime
 769             = PerfCounter.newPerfCounter("jdk.module.bootstrap.resolveTime");
 770         static PerfCounter layerCreateTime
 771             = PerfCounter.newPerfCounter("jdk.module.bootstrap.layerCreateTime");
 772         static PerfCounter loadModulesTime
 773             = PerfCounter.newPerfCounter("jdk.module.bootstrap.loadModulesTime");
 774         static PerfCounter bootstrapTime
 775             = PerfCounter.newPerfCounter("jdk.module.bootstrap.totalTime");
 776     }
 777 }