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 26package jdk.internal.module; 27 28import java.io.File; 29import java.io.PrintStream; 30import java.lang.module.Configuration; 31import java.lang.module.ModuleDescriptor; 32import java.lang.module.ModuleFinder; 33import java.lang.module.ModuleReference; 34import java.lang.module.ResolvedModule; 35import java.net.URI; 36import java.nio.file.Path; 37import java.nio.file.Paths; 38import java.util.ArrayList; 39import java.util.Collections; 40import java.util.HashMap; 41import java.util.HashSet; 42import java.util.Iterator; 43import java.util.List; 44import java.util.Map; 45import java.util.NoSuchElementException; 46import java.util.Optional; 47import java.util.Set; 48import java.util.function.Function; 49 50import jdk.internal.loader.BootLoader; 51import jdk.internal.loader.BuiltinClassLoader; 52import jdk.internal.misc.JavaLangAccess; 53import jdk.internal.misc.SharedSecrets; 54import 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 a class loader. 66 */ 67 68public 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 // ModuleFinders for the initial configuration 91 private static ModuleFinder unlimitedFinder; 92 private static ModuleFinder limitedFinder; 93 94 /** 95 * Returns the ModulePatcher for the initial configuration. 96 */ 97 public static ModulePatcher patcher() { 98 return patcher; 99 } 100 101 /** 102 * Returns the ModuleFinder for the initial configuration before observability 103 * is limited by the --limit-modules command line option. 104 */ 105 public static ModuleFinder unlimitedFinder() { 106 assert unlimitedFinder != null; 107 return unlimitedFinder; 108 } 109 110 /** 111 * Returns the ModuleFinder for the initial configuration. 112 */ 113 public static ModuleFinder limitedFinder() { 114 assert limitedFinder != null; 115 return limitedFinder; 116 } 117 118 /** 119 * Initialize the module system, returning the boot layer. 120 * 121 * @see java.lang.System#initPhase2() 122 */ 123 public static ModuleLayer boot() { 124 125 // Step 1: Locate system modules (may be patched) 126 127 long t1 = System.nanoTime(); 128 ModuleFinder systemModules = ModuleFinder.ofSystem(); 129 PerfCounters.systemModulesTime.addElapsedTimeFrom(t1); 130 131 132 // Step 2: Define and load java.base. This patches all classes loaded 133 // to date so that they are members of java.base. Once java.base is 134 // loaded then resources in java.base are available for error messages 135 // needed from here on. 136 137 long t2 = System.nanoTime(); 138 139 ModuleReference base = systemModules.find(JAVA_BASE).orElse(null); 140 if (base == null) 141 throw new InternalError(JAVA_BASE + " not found"); 142 URI baseUri = base.location().orElse(null); 143 if (baseUri == null) 144 throw new InternalError(JAVA_BASE + " does not have a location"); 145 BootLoader.loadModule(base); 146 Modules.defineModule(null, base.descriptor(), baseUri); 147 148 PerfCounters.defineBaseTime.addElapsedTimeFrom(t2); 149 150 151 // Step 2a: If --validate-modules is specified then the VM needs to 152 // start with only java.base, all other options are ignored. 153 154 String propValue = getAndRemoveProperty("jdk.module.minimumBoot"); 155 if (propValue != null) { 156 return createMinimalBootLayer(); 157 } 158 159 160 // Step 3: Construct the module path and the set of root modules to 161 // resolve. If --limit-modules is specified then it limits the set 162 // modules that are observable. 163 164 long t3 = System.nanoTime(); 165 166 // --upgrade-module-path option specified to launcher 167 ModuleFinder upgradeModulePath 168 = createModulePathFinder("jdk.module.upgrade.path"); 169 if (upgradeModulePath != null) 170 systemModules = ModuleFinder.compose(upgradeModulePath, systemModules); 171 172 // --module-path option specified to the launcher 173 ModuleFinder appModulePath = createModulePathFinder("jdk.module.path"); 174 175 // The module finder: [--upgrade-module-path] system [--module-path] 176 ModuleFinder finder = systemModules; 177 if (appModulePath != null) 178 finder = ModuleFinder.compose(finder, appModulePath); 179 180 // The root modules to resolve 181 Set<String> roots = new HashSet<>(); 182 183 // launcher -m option to specify the main/initial module 184 String mainModule = System.getProperty("jdk.module.main"); 185 if (mainModule != null) 186 roots.add(mainModule); 187 188 // additional module(s) specified by --add-modules 189 boolean addAllDefaultModules = false; 190 boolean addAllSystemModules = false; 191 boolean addAllApplicationModules = false; 192 for (String mod: getExtraAddModules()) { 193 switch (mod) { 194 case ALL_DEFAULT: 195 addAllDefaultModules = true; 196 break; 197 case ALL_SYSTEM: 198 addAllSystemModules = true; 199 break; 200 case ALL_MODULE_PATH: 201 addAllApplicationModules = true; 202 break; 203 default : 204 roots.add(mod); 205 } 206 } 207 208 // --limit-modules 209 unlimitedFinder = finder; 210 propValue = getAndRemoveProperty("jdk.module.limitmods"); 211 if (propValue != null) { 212 Set<String> mods = new HashSet<>(); 213 for (String mod: propValue.split(",")) { 214 mods.add(mod); 215 } 216 finder = limitFinder(finder, mods, roots); 217 } 218 limitedFinder = finder; 219 220 // If there is no initial module specified then assume that the initial 221 // module is the unnamed module of the application class loader. This 222 // is implemented by resolving "java.se" and all (non-java.*) modules 223 // that export an API. If "java.se" is not observable then all java.* 224 // modules are resolved. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT 225 // bit set in their ModuleResolution attribute flags are excluded from 226 // the default set of roots. 227 if (mainModule == null || addAllDefaultModules) { 228 boolean hasJava = false; 229 if (systemModules.find(JAVA_SE).isPresent()) { 230 // java.se is a system module 231 if (finder == systemModules || finder.find(JAVA_SE).isPresent()) { 232 // java.se is observable 233 hasJava = true; 234 roots.add(JAVA_SE); 235 } 236 } 237 238 for (ModuleReference mref : systemModules.findAll()) { 239 String mn = mref.descriptor().name(); 240 if (hasJava && mn.startsWith("java.")) 241 continue; 242 243 if (ModuleResolution.doNotResolveByDefault(mref)) 244 continue; 245 246 // add as root if observable and exports at least one package 247 if ((finder == systemModules || finder.find(mn).isPresent())) { 248 ModuleDescriptor descriptor = mref.descriptor(); 249 for (ModuleDescriptor.Exports e : descriptor.exports()) { 250 if (!e.isQualified()) { 251 roots.add(mn); 252 break; 253 } 254 } 255 } 256 } 257 } 258 259 // If `--add-modules ALL-SYSTEM` is specified then all observable system 260 // modules will be resolved. 261 if (addAllSystemModules) { 262 ModuleFinder f = finder; // observable modules 263 systemModules.findAll() 264 .stream() 265 .map(ModuleReference::descriptor) 266 .map(ModuleDescriptor::name) 267 .filter(mn -> f.find(mn).isPresent()) // observable 268 .forEach(mn -> roots.add(mn)); 269 } 270 271 // If `--add-modules ALL-MODULE-PATH` is specified then all observable 272 // modules on the application module path will be resolved. 273 if (appModulePath != null && addAllApplicationModules) { 274 ModuleFinder f = finder; // observable modules 275 appModulePath.findAll() 276 .stream() 277 .map(ModuleReference::descriptor) 278 .map(ModuleDescriptor::name) 279 .filter(mn -> f.find(mn).isPresent()) // observable 280 .forEach(mn -> roots.add(mn)); 281 } 282 283 PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t3); 284 285 286 // Step 4: Resolve the root modules, with service binding, to create 287 // the configuration for the boot layer. 288 289 long t4 = System.nanoTime(); 290 291 // determine if post resolution checks are needed 292 boolean needPostResolutionChecks = true; 293 if (baseUri.getScheme().equals("jrt") // toLowerCase not needed here 294 && (upgradeModulePath == null) 295 && (appModulePath == null) 296 && (patcher.isEmpty())) { 297 needPostResolutionChecks = false; 298 } 299 300 PrintStream traceOutput = null; 301 propValue = getAndRemoveProperty("jdk.module.showModuleResolution"); 302 if (propValue != null && Boolean.parseBoolean(propValue)) 303 traceOutput = System.out; 304 305 // run the resolver to create the configuration 306 Configuration cf = SharedSecrets.getJavaLangModuleAccess() 307 .resolveAndBind(finder, 308 roots, 309 needPostResolutionChecks, 310 traceOutput); 311 312 PerfCounters.resolveTime.addElapsedTimeFrom(t4); 313 314 315 // Step 5: Map the modules in the configuration to class loaders. 316 // The static configuration provides the mapping of standard and JDK 317 // modules to the boot and platform loaders. All other modules (JDK 318 // tool modules, and both explicit and automatic modules on the 319 // application module path) are defined to the application class 320 // loader. 321 322 long t5 = System.nanoTime(); 323 324 // mapping of modules to class loaders 325 Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf); 326 327 // check that all modules to be mapped to the boot loader will be 328 // loaded from the runtime image 329 if (needPostResolutionChecks) { 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 (upgradeModulePath != null 336 && upgradeModulePath.find(name).isPresent()) 337 fail(name + ": cannot be loaded from upgrade module path"); 338 if (!systemModules.find(name).isPresent()) 339 fail(name + ": cannot be loaded from application module path"); 340 } 341 } 342 343 // check if module specified in --patch-module is present 344 for (String mn: patcher.patchedModules()) { 345 if (!cf.findModule(mn).isPresent()) { 346 warnUnknownModule(PATCH_MODULE, mn); 347 } 348 } 349 } 350 351 // check for split packages in the modules mapped to the built-in loaders 352 if (SystemModules.hasSplitPackages() || needPostResolutionChecks) { 353 checkSplitPackages(cf, clf); 354 } 355 356 // load/register the modules with the built-in class loaders 357 loadModules(cf, clf); 358 359 PerfCounters.loadModulesTime.addElapsedTimeFrom(t5); 360 361 362 // Step 6: Define all modules to the VM 363 364 long t6 = System.nanoTime(); 365 ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf); 366 PerfCounters.layerCreateTime.addElapsedTimeFrom(t6); 367 368 369 // Step 7: Miscellaneous 370 371 // check incubating status 372 checkIncubatingStatus(cf); 373 374 // --add-reads, --add-exports/--add-opens, and -illegal-access 375 long t7 = System.nanoTime(); 376 addExtraReads(bootLayer); 377 boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer); 378 addIllegalAccess(bootLayer, upgradeModulePath, extraExportsOrOpens); 379 PerfCounters.adjustModulesTime.addElapsedTimeFrom(t7); 380 381 // total time to initialize 382 PerfCounters.bootstrapTime.addElapsedTimeFrom(t1); 383 384 return bootLayer; 385 } 386 387 /** 388 * Create a "minimal" boot module layer that only contains java.base. 389 */ 390 private static ModuleLayer createMinimalBootLayer() { 391 Configuration cf = SharedSecrets.getJavaLangModuleAccess() 392 .resolveAndBind(ModuleFinder.ofSystem(), 393 Set.of(JAVA_BASE), 394 false, 395 null); 396 397 Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf); 398 return ModuleLayer.empty().defineModules(cf, clf); 399 } 400 401 /** 402 * Load/register the modules to the built-in class loaders. 403 */ 404 private static void loadModules(Configuration cf, 405 Function<String, ClassLoader> clf) { 406 for (ResolvedModule resolvedModule : cf.modules()) { 407 ModuleReference mref = resolvedModule.reference(); 408 String name = resolvedModule.name(); 409 ClassLoader loader = clf.apply(name); 410 if (loader == null) { 411 // skip java.base as it is already loaded 412 if (!name.equals(JAVA_BASE)) { 413 BootLoader.loadModule(mref); 414 } 415 } else if (loader instanceof BuiltinClassLoader) { 416 ((BuiltinClassLoader) loader).loadModule(mref); 417 } 418 } 419 } 420 421 /** 422 * Checks for split packages between modules defined to the built-in class 423 * loaders. 424 */ 425 private static void checkSplitPackages(Configuration cf, 426 Function<String, ClassLoader> clf) { 427 Map<String, String> packageToModule = new HashMap<>(); 428 for (ResolvedModule resolvedModule : cf.modules()) { 429 ModuleDescriptor descriptor = resolvedModule.reference().descriptor(); 430 String name = descriptor.name(); 431 ClassLoader loader = clf.apply(name); 432 if (loader == null || loader instanceof BuiltinClassLoader) { 433 for (String p : descriptor.packages()) { 434 String other = packageToModule.putIfAbsent(p, name); 435 if (other != null) { 436 String msg = "Package " + p + " in both module " 437 + name + " and module " + other; 438 throw new LayerInstantiationException(msg); 439 } 440 } 441 } 442 443 } 444 } 445 446 /** 447 * Returns a ModuleFinder that limits observability to the given root 448 * modules, their transitive dependences, plus a set of other modules. 449 */ 450 private static ModuleFinder limitFinder(ModuleFinder finder, 451 Set<String> roots, 452 Set<String> otherMods) 453 { 454 // resolve all root modules 455 Configuration cf = Configuration.empty().resolve(finder, 456 ModuleFinder.of(), 457 roots); 458 459 // module name -> reference 460 Map<String, ModuleReference> map = new HashMap<>(); 461 462 // root modules and their transitive dependences 463 cf.modules().stream() 464 .map(ResolvedModule::reference) 465 .forEach(mref -> map.put(mref.descriptor().name(), mref)); 466 467 // additional modules 468 otherMods.stream() 469 .map(finder::find) 470 .flatMap(Optional::stream) 471 .forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref)); 472 473 // set of modules that are observable 474 Set<ModuleReference> mrefs = new HashSet<>(map.values()); 475 476 return new ModuleFinder() { 477 @Override 478 public Optional<ModuleReference> find(String name) { 479 return Optional.ofNullable(map.get(name)); 480 } 481 @Override 482 public Set<ModuleReference> findAll() { 483 return mrefs; 484 } 485 }; 486 } 487 488 /** 489 * Creates a finder from the module path that is the value of the given 490 * system property and optionally patched by --patch-module 491 */ 492 private static ModuleFinder createModulePathFinder(String prop) { 493 String s = System.getProperty(prop); 494 if (s == null) { 495 return null; 496 } else { 497 String[] dirs = s.split(File.pathSeparator); 498 Path[] paths = new Path[dirs.length]; 499 int i = 0; 500 for (String dir: dirs) { 501 paths[i++] = Paths.get(dir); 502 } 503 return ModulePath.of(patcher, paths); 504 } 505 } 506 507 /** 508 * Initialize the module patcher for the initial configuration passed on the 509 * value of the --patch-module options. 510 */ 511 private static ModulePatcher initModulePatcher() { 512 Map<String, List<String>> map = decode("jdk.module.patch.", 513 File.pathSeparator, 514 false); 515 return new ModulePatcher(map); 516 } 517 518 /** 519 * Returns the set of module names specified via --add-modules options 520 * on the command line 521 */ 522 private static Set<String> getExtraAddModules() { 523 String prefix = "jdk.module.addmods."; 524 int index = 0; 525 526 // the system property is removed after decoding 527 String value = getAndRemoveProperty(prefix + index); 528 if (value == null) { 529 return Collections.emptySet(); 530 } 531 532 Set<String> modules = new HashSet<>(); 533 while (value != null) { 534 for (String s : value.split(",")) { 535 if (s.length() > 0) modules.add(s); 536 } 537 index++; 538 value = getAndRemoveProperty(prefix + index); 539 } 540 541 return modules; 542 } 543 544 /** 545 * Process the --add-reads options to add any additional read edges that 546 * are specified on the command-line. 547 */ 548 private static void addExtraReads(ModuleLayer bootLayer) { 549 550 // decode the command line options 551 Map<String, List<String>> map = decode("jdk.module.addreads."); 552 if (map.isEmpty()) 553 return; 554 555 for (Map.Entry<String, List<String>> e : map.entrySet()) { 556 557 // the key is $MODULE 558 String mn = e.getKey(); 559 Optional<Module> om = bootLayer.findModule(mn); 560 if (!om.isPresent()) { 561 warnUnknownModule(ADD_READS, mn); 562 continue; 563 } 564 Module m = om.get(); 565 566 // the value is the set of other modules (by name) 567 for (String name : e.getValue()) { 568 if (ALL_UNNAMED.equals(name)) { 569 Modules.addReadsAllUnnamed(m); 570 } else { 571 om = bootLayer.findModule(name); 572 if (om.isPresent()) { 573 Modules.addReads(m, om.get()); 574 } else { 575 warnUnknownModule(ADD_READS, name); 576 } 577 } 578 } 579 } 580 } 581 582 /** 583 * Process the --add-exports and --add-opens options to export/open 584 * additional packages specified on the command-line. 585 */ 586 private static boolean addExtraExportsAndOpens(ModuleLayer bootLayer) { 587 boolean extraExportsOrOpens = false; 588 589 // --add-exports 590 String prefix = "jdk.module.addexports."; 591 Map<String, List<String>> extraExports = decode(prefix); 592 if (!extraExports.isEmpty()) { 593 addExtraExportsOrOpens(bootLayer, extraExports, false); 594 extraExportsOrOpens = true; 595 } 596 597 598 // --add-opens 599 prefix = "jdk.module.addopens."; 600 Map<String, List<String>> extraOpens = decode(prefix); 601 if (!extraOpens.isEmpty()) { 602 addExtraExportsOrOpens(bootLayer, extraOpens, true); 603 extraExportsOrOpens = true; 604 } 605 606 return extraExportsOrOpens; 607 } 608 609 private static void addExtraExportsOrOpens(ModuleLayer bootLayer, 610 Map<String, List<String>> map, 611 boolean opens) 612 { 613 String option = opens ? ADD_OPENS : ADD_EXPORTS; 614 for (Map.Entry<String, List<String>> e : map.entrySet()) { 615 616 // the key is $MODULE/$PACKAGE 617 String key = e.getKey(); 618 String[] s = key.split("/"); 619 if (s.length != 2) 620 fail(unableToParse(option, "<module>/<package>", key)); 621 622 String mn = s[0]; 623 String pn = s[1]; 624 if (mn.isEmpty() || pn.isEmpty()) 625 fail(unableToParse(option, "<module>/<package>", key)); 626 627 // The exporting module is in the boot layer 628 Module m; 629 Optional<Module> om = bootLayer.findModule(mn); 630 if (!om.isPresent()) { 631 warnUnknownModule(option, mn); 632 continue; 633 } 634 635 m = om.get(); 636 637 if (!m.getDescriptor().packages().contains(pn)) { 638 warn("package " + pn + " not in " + mn); 639 continue; 640 } 641 642 // the value is the set of modules to export to (by name) 643 for (String name : e.getValue()) { 644 boolean allUnnamed = false; 645 Module other = null; 646 if (ALL_UNNAMED.equals(name)) { 647 allUnnamed = true; 648 } else { 649 om = bootLayer.findModule(name); 650 if (om.isPresent()) { 651 other = om.get(); 652 } else { 653 warnUnknownModule(option, name); 654 continue; 655 } 656 } 657 if (allUnnamed) { 658 if (opens) { 659 Modules.addOpensToAllUnnamed(m, pn); 660 } else { 661 Modules.addExportsToAllUnnamed(m, pn); 662 } 663 } else { 664 if (opens) { 665 Modules.addOpens(m, pn, other); 666 } else { 667 Modules.addExports(m, pn, other); 668 } 669 } 670 671 } 672 } 673 } 674 675 /** 676 * Process the --illegal-access option (and its default) to open packages 677 * of system modules in the boot layer to code in unnamed modules. 678 */ 679 private static void addIllegalAccess(ModuleLayer bootLayer, 680 ModuleFinder upgradeModulePath, 681 boolean extraExportsOrOpens) { 682 String value = getAndRemoveProperty("jdk.module.illegalAccess"); 683 IllegalAccessLogger.Mode mode = IllegalAccessLogger.Mode.ONESHOT; 684 if (value != null) { 685 switch (value) { 686 case "deny": 687 return; 688 case "permit": 689 break; 690 case "warn": 691 mode = IllegalAccessLogger.Mode.WARN; 692 break; 693 case "debug": 694 mode = IllegalAccessLogger.Mode.DEBUG; 695 break; 696 default: 697 fail("Value specified to --illegal-access not recognized:" 698 + " '" + value + "'"); 699 return; 700 } 701 } 702 IllegalAccessLogger.Builder builder 703 = new IllegalAccessLogger.Builder(mode, System.err); 704 705 Map<String, Set<String>> map1 = SystemModules.concealedPackagesToOpen(); 706 Map<String, Set<String>> map2 = SystemModules.exportedPackagesToOpen(); 707 if (map1.isEmpty() && map2.isEmpty()) { 708 // need to generate maps when on exploded build 709 IllegalAccessMaps maps = IllegalAccessMaps.generate(limitedFinder()); 710 map1 = maps.concealedPackagesToOpen(); 711 map2 = maps.exportedPackagesToOpen(); 712 } 713 714 // open specific packages in the system modules 715 for (Module m : bootLayer.modules()) { 716 ModuleDescriptor descriptor = m.getDescriptor(); 717 String name = m.getName(); 718 719 // skip open modules 720 if (descriptor.isOpen()) { 721 continue; 722 } 723 724 // skip modules loaded from the upgrade module path 725 if (upgradeModulePath != null 726 && upgradeModulePath.find(name).isPresent()) { 727 continue; 728 } 729 730 Set<String> concealedPackages = map1.getOrDefault(name, Set.of()); 731 Set<String> exportedPackages = map2.getOrDefault(name, Set.of()); 732 733 // refresh the set of concealed and exported packages if needed 734 if (extraExportsOrOpens) { 735 concealedPackages = new HashSet<>(concealedPackages); 736 exportedPackages = new HashSet<>(exportedPackages); 737 Iterator<String> iterator = concealedPackages.iterator(); 738 while (iterator.hasNext()) { 739 String pn = iterator.next(); 740 if (m.isExported(pn, BootLoader.getUnnamedModule())) { 741 // concealed package is exported to ALL-UNNAMED 742 iterator.remove(); 743 exportedPackages.add(pn); 744 } 745 } 746 iterator = exportedPackages.iterator(); 747 while (iterator.hasNext()) { 748 String pn = iterator.next(); 749 if (m.isOpen(pn, BootLoader.getUnnamedModule())) { 750 // exported package is opened to ALL-UNNAMED 751 iterator.remove(); 752 } 753 } 754 } 755 756 // log reflective access to all types in concealed packages 757 builder.logAccessToConcealedPackages(m, concealedPackages); 758 759 // log reflective access to non-public members/types in exported packages 760 builder.logAccessToExportedPackages(m, exportedPackages); 761 762 // open the packages to unnamed modules 763 JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 764 jla.addOpensToAllUnnamed(m, concat(concealedPackages.iterator(), 765 exportedPackages.iterator())); 766 } 767 768 builder.complete(); 769 } 770 771 /** 772 * Decodes the values of --add-reads, -add-exports, --add-opens or 773 * --patch-modules options that are encoded in system properties. 774 * 775 * @param prefix the system property prefix 776 * @praam regex the regex for splitting the RHS of the option value 777 */ 778 private static Map<String, List<String>> decode(String prefix, 779 String regex, 780 boolean allowDuplicates) { 781 int index = 0; 782 // the system property is removed after decoding 783 String value = getAndRemoveProperty(prefix + index); 784 if (value == null) 785 return Collections.emptyMap(); 786 787 Map<String, List<String>> map = new HashMap<>(); 788 789 while (value != null) { 790 791 int pos = value.indexOf('='); 792 if (pos == -1) 793 fail(unableToParse(option(prefix), "<module>=<value>", value)); 794 if (pos == 0) 795 fail(unableToParse(option(prefix), "<module>=<value>", value)); 796 797 // key is <module> or <module>/<package> 798 String key = value.substring(0, pos); 799 800 String rhs = value.substring(pos+1); 801 if (rhs.isEmpty()) 802 fail(unableToParse(option(prefix), "<module>=<value>", value)); 803 804 // value is <module>(,<module>)* or <file>(<pathsep><file>)* 805 if (!allowDuplicates && map.containsKey(key)) 806 fail(key + " specified more than once to " + option(prefix)); 807 List<String> values = map.computeIfAbsent(key, k -> new ArrayList<>()); 808 int ntargets = 0; 809 for (String s : rhs.split(regex)) { 810 if (s.length() > 0) { 811 values.add(s); 812 ntargets++; 813 } 814 } 815 if (ntargets == 0) 816 fail("Target must be specified: " + option(prefix) + " " + value); 817 818 index++; 819 value = getAndRemoveProperty(prefix + index); 820 } 821 822 return map; 823 } 824 825 /** 826 * Decodes the values of --add-reads, -add-exports or --add-opens 827 * which use the "," to separate the RHS of the option value. 828 */ 829 private static Map<String, List<String>> decode(String prefix) { 830 return decode(prefix, ",", true); 831 } 832 833 /** 834 * Gets and remove the named system property 835 */ 836 private static String getAndRemoveProperty(String key) { 837 return (String)System.getProperties().remove(key); 838 } 839 840 /** 841 * Checks incubating status of modules in the configuration 842 */ 843 private static void checkIncubatingStatus(Configuration cf) { 844 String incubating = null; 845 for (ResolvedModule resolvedModule : cf.modules()) { 846 ModuleReference mref = resolvedModule.reference(); 847 848 // emit warning if the WARN_INCUBATING module resolution bit set 849 if (ModuleResolution.hasIncubatingWarning(mref)) { 850 String mn = mref.descriptor().name(); 851 if (incubating == null) { 852 incubating = mn; 853 } else { 854 incubating += ", " + mn; 855 } 856 } 857 } 858 if (incubating != null) 859 warn("Using incubator modules: " + incubating); 860 } 861 862 /** 863 * Throws a RuntimeException with the given message 864 */ 865 static void fail(String m) { 866 throw new RuntimeException(m); 867 } 868 869 static void warn(String m) { 870 System.err.println("WARNING: " + m); 871 } 872 873 static void warnUnknownModule(String option, String mn) { 874 warn("Unknown module: " + mn + " specified to " + option); 875 } 876 877 static String unableToParse(String option, String text, String value) { 878 return "Unable to parse " + option + " " + text + ": " + value; 879 } 880 881 private static final String ADD_MODULES = "--add-modules"; 882 private static final String ADD_EXPORTS = "--add-exports"; 883 private static final String ADD_OPENS = "--add-opens"; 884 private static final String ADD_READS = "--add-reads"; 885 private static final String PATCH_MODULE = "--patch-module"; 886 887 888 /* 889 * Returns the command-line option name corresponds to the specified 890 * system property prefix. 891 */ 892 static String option(String prefix) { 893 switch (prefix) { 894 case "jdk.module.addexports.": 895 return ADD_EXPORTS; 896 case "jdk.module.addopens.": 897 return ADD_OPENS; 898 case "jdk.module.addreads.": 899 return ADD_READS; 900 case "jdk.module.patch.": 901 return PATCH_MODULE; 902 case "jdk.module.addmods.": 903 return ADD_MODULES; 904 default: 905 throw new IllegalArgumentException(prefix); 906 } 907 } 908 909 static <T> Iterator<T> concat(Iterator<T> iterator1, Iterator<T> iterator2) { 910 return new Iterator<T>() { 911 @Override 912 public boolean hasNext() { 913 return iterator1.hasNext() || iterator2.hasNext(); 914 } 915 @Override 916 public T next() { 917 if (iterator1.hasNext()) return iterator1.next(); 918 if (iterator2.hasNext()) return iterator2.next(); 919 throw new NoSuchElementException(); 920 } 921 }; 922 } 923 924 static class PerfCounters { 925 926 static PerfCounter systemModulesTime 927 = PerfCounter.newPerfCounter("jdk.module.bootstrap.systemModulesTime"); 928 static PerfCounter defineBaseTime 929 = PerfCounter.newPerfCounter("jdk.module.bootstrap.defineBaseTime"); 930 static PerfCounter optionsAndRootsTime 931 = PerfCounter.newPerfCounter("jdk.module.bootstrap.optionsAndRootsTime"); 932 static PerfCounter resolveTime 933 = PerfCounter.newPerfCounter("jdk.module.bootstrap.resolveTime"); 934 static PerfCounter layerCreateTime 935 = PerfCounter.newPerfCounter("jdk.module.bootstrap.layerCreateTime"); 936 static PerfCounter loadModulesTime 937 = PerfCounter.newPerfCounter("jdk.module.bootstrap.loadModulesTime"); 938 static PerfCounter adjustModulesTime 939 = PerfCounter.newPerfCounter("jdk.module.bootstrap.adjustModulesTime"); 940 static PerfCounter bootstrapTime 941 = PerfCounter.newPerfCounter("jdk.module.bootstrap.totalTime"); 942 } 943} 944