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