jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
changeset 45652 33342314ce89
parent 45004 ea3137042a61
child 46096 62c77b334012
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Thu Jun 15 17:24:12 2017 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Fri Jun 16 09:20:39 2017 -0700
@@ -39,14 +39,17 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
 
 import jdk.internal.loader.BootLoader;
 import jdk.internal.loader.BuiltinClassLoader;
+import jdk.internal.misc.JavaLangAccess;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.perf.PerfCounter;
 
@@ -59,7 +62,7 @@
  * -m and --add-modules options. The modules are located on a module path that
  * is constructed from the upgrade module path, system modules, and application
  * module path. The Configuration is instantiated as the boot layer with each
- * module in the the configuration defined to one of the built-in class loaders.
+ * module in the the configuration defined to a class loader.
  */
 
 public final class ModuleBootstrap {
@@ -119,20 +122,20 @@
      */
     public static ModuleLayer boot() {
 
-        long t0 = System.nanoTime();
+        // Step 1: Locate system modules (may be patched)
 
-        // system modules (may be patched)
+        long t1 = System.nanoTime();
         ModuleFinder systemModules = ModuleFinder.ofSystem();
-
-        PerfCounters.systemModulesTime.addElapsedTimeFrom(t0);
+        PerfCounters.systemModulesTime.addElapsedTimeFrom(t1);
 
 
-        long t1 = System.nanoTime();
+        // Step 2: Define and load java.base. This patches all classes loaded
+        // to date so that they are members of java.base. Once java.base is
+        // loaded then resources in java.base are available for error messages
+        // needed from here on.
 
-        // Once we have the system modules then we define the base module to
-        // the VM. We do this here so that java.base is defined as early as
-        // possible and also that resources in the base module can be located
-        // for error messages that may happen from here on.
+        long t2 = System.nanoTime();
+
         ModuleReference base = systemModules.find(JAVA_BASE).orElse(null);
         if (base == null)
             throw new InternalError(JAVA_BASE + " not found");
@@ -142,15 +145,23 @@
         BootLoader.loadModule(base);
         Modules.defineModule(null, base.descriptor(), baseUri);
 
-        PerfCounters.defineBaseTime.addElapsedTimeFrom(t1);
+        PerfCounters.defineBaseTime.addElapsedTimeFrom(t2);
+
 
-        // special mode to boot with only java.base, ignores other options
+        // Step 2a: If --validate-modules is specified then the VM needs to
+        // start with only java.base, all other options are ignored.
+
         String propValue = getAndRemoveProperty("jdk.module.minimumBoot");
         if (propValue != null) {
             return createMinimalBootLayer();
         }
 
-        long t2 = System.nanoTime();
+
+        // Step 3: Construct the module path and the set of root modules to
+        // resolve. If --limit-modules is specified then it limits the set
+        // modules that are observable.
+
+        long t3 = System.nanoTime();
 
         // --upgrade-module-path option specified to launcher
         ModuleFinder upgradeModulePath
@@ -269,10 +280,13 @@
                 .forEach(mn -> roots.add(mn));
         }
 
-        PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t2);
+        PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t3);
 
 
-        long t3 = System.nanoTime();
+        // Step 4: Resolve the root modules, with service binding, to create
+        // the configuration for the boot layer.
+
+        long t4 = System.nanoTime();
 
         // determine if post resolution checks are needed
         boolean needPostResolutionChecks = true;
@@ -295,11 +309,17 @@
                                 needPostResolutionChecks,
                                 traceOutput);
 
-        // time to create configuration
-        PerfCounters.resolveTime.addElapsedTimeFrom(t3);
+        PerfCounters.resolveTime.addElapsedTimeFrom(t4);
+
 
-        // check module names and incubating status
-        checkModuleNamesAndStatus(cf);
+        // Step 5: Map the modules in the configuration to class loaders.
+        // The static configuration provides the mapping of standard and JDK
+        // modules to the boot and platform loaders. All other modules (JDK
+        // tool modules, and both explicit and automatic modules on the
+        // application module path) are defined to the application class
+        // loader.
+
+        long t5 = System.nanoTime();
 
         // mapping of modules to class loaders
         Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
@@ -312,11 +332,9 @@
                 String name = mref.descriptor().name();
                 ClassLoader cl = clf.apply(name);
                 if (cl == null) {
-
                     if (upgradeModulePath != null
                             && upgradeModulePath.find(name).isPresent())
                         fail(name + ": cannot be loaded from upgrade module path");
-
                     if (!systemModules.find(name).isPresent())
                         fail(name + ": cannot be loaded from application module path");
                 }
@@ -330,55 +348,38 @@
             }
         }
 
-        // if needed check that there are no split packages in the set of
-        // resolved modules for the boot layer
+        // check for split packages in the modules mapped to the built-in loaders
         if (SystemModules.hasSplitPackages() || needPostResolutionChecks) {
-            Map<String, String> packageToModule = new HashMap<>();
-            for (ResolvedModule resolvedModule : cf.modules()) {
-                ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
-                String name = descriptor.name();
-                for (String p : descriptor.packages()) {
-                    String other = packageToModule.putIfAbsent(p, name);
-                    if (other != null) {
-                        String msg = "Package " + p + " in both module "
-                                     + name + " and module " + other;
-                        throw new LayerInstantiationException(msg);
-                    }
-                }
-            }
+            checkSplitPackages(cf, clf);
         }
 
-        long t4 = System.nanoTime();
-
-        // define modules to VM/runtime
-        ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf);
-
-        PerfCounters.layerCreateTime.addElapsedTimeFrom(t4);
-
-
-        long t5 = System.nanoTime();
-
-        // define the module to its class loader, except java.base
-        for (ResolvedModule resolvedModule : cf.modules()) {
-            ModuleReference mref = resolvedModule.reference();
-            String name = mref.descriptor().name();
-            ClassLoader cl = clf.apply(name);
-            if (cl == null) {
-                if (!name.equals(JAVA_BASE)) BootLoader.loadModule(mref);
-            } else {
-                ((BuiltinClassLoader)cl).loadModule(mref);
-            }
-        }
+        // load/register the modules with the built-in class loaders
+        loadModules(cf, clf);
 
         PerfCounters.loadModulesTime.addElapsedTimeFrom(t5);
 
 
-        // --add-reads, --add-exports/--add-opens
+        // Step 6: Define all modules to the VM
+
+        long t6 = System.nanoTime();
+        ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf);
+        PerfCounters.layerCreateTime.addElapsedTimeFrom(t6);
+
+
+        // Step 7: Miscellaneous
+
+        // check incubating status
+        checkIncubatingStatus(cf);
+
+        // --add-reads, --add-exports/--add-opens, and -illegal-access
+        long t7 = System.nanoTime();
         addExtraReads(bootLayer);
-        addExtraExportsAndOpens(bootLayer);
+        boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer);
+        addIllegalAccess(bootLayer, upgradeModulePath, extraExportsOrOpens);
+        PerfCounters.adjustModulesTime.addElapsedTimeFrom(t7);
 
         // total time to initialize
-        PerfCounters.bootstrapTime.addElapsedTimeFrom(t0);
+        PerfCounters.bootstrapTime.addElapsedTimeFrom(t1);
 
         return bootLayer;
     }
@@ -398,6 +399,51 @@
     }
 
     /**
+     * Load/register the modules to the built-in class loaders.
+     */
+    private static void loadModules(Configuration cf,
+                                    Function<String, ClassLoader> clf) {
+        for (ResolvedModule resolvedModule : cf.modules()) {
+            ModuleReference mref = resolvedModule.reference();
+            String name = resolvedModule.name();
+            ClassLoader loader = clf.apply(name);
+            if (loader == null) {
+                // skip java.base as it is already loaded
+                if (!name.equals(JAVA_BASE)) {
+                    BootLoader.loadModule(mref);
+                }
+            } else if (loader instanceof BuiltinClassLoader) {
+                ((BuiltinClassLoader) loader).loadModule(mref);
+            }
+        }
+    }
+
+    /**
+     * Checks for split packages between modules defined to the built-in class
+     * loaders.
+     */
+    private static void checkSplitPackages(Configuration cf,
+                                           Function<String, ClassLoader> clf) {
+        Map<String, String> packageToModule = new HashMap<>();
+        for (ResolvedModule resolvedModule : cf.modules()) {
+            ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
+            String name = descriptor.name();
+            ClassLoader loader = clf.apply(name);
+            if (loader == null || loader instanceof BuiltinClassLoader) {
+                for (String p : descriptor.packages()) {
+                    String other = packageToModule.putIfAbsent(p, name);
+                    if (other != null) {
+                        String msg = "Package " + p + " in both module "
+                                     + name + " and module " + other;
+                        throw new LayerInstantiationException(msg);
+                    }
+                }
+            }
+
+        }
+    }
+
+    /**
      * Returns a ModuleFinder that limits observability to the given root
      * modules, their transitive dependences, plus a set of other modules.
      */
@@ -458,15 +504,14 @@
         }
     }
 
-
     /**
      * Initialize the module patcher for the initial configuration passed on the
      * value of the --patch-module options.
      */
     private static ModulePatcher initModulePatcher() {
         Map<String, List<String>> map = decode("jdk.module.patch.",
-                                               File.pathSeparator,
-                                               false);
+                File.pathSeparator,
+                false);
         return new ModulePatcher(map);
     }
 
@@ -538,38 +583,27 @@
      * Process the --add-exports and --add-opens options to export/open
      * additional packages specified on the command-line.
      */
-    private static void addExtraExportsAndOpens(ModuleLayer bootLayer) {
+    private static boolean addExtraExportsAndOpens(ModuleLayer bootLayer) {
+        boolean extraExportsOrOpens = false;
+
         // --add-exports
         String prefix = "jdk.module.addexports.";
         Map<String, List<String>> extraExports = decode(prefix);
         if (!extraExports.isEmpty()) {
             addExtraExportsOrOpens(bootLayer, extraExports, false);
+            extraExportsOrOpens = true;
         }
 
+
         // --add-opens
         prefix = "jdk.module.addopens.";
         Map<String, List<String>> extraOpens = decode(prefix);
         if (!extraOpens.isEmpty()) {
             addExtraExportsOrOpens(bootLayer, extraOpens, true);
+            extraExportsOrOpens = true;
         }
 
-        // --permit-illegal-access
-        if (getAndRemoveProperty("jdk.module.permitIllegalAccess") != null) {
-            warn("--permit-illegal-access will be removed in the next major release");
-            IllegalAccessLogger.Builder builder = new IllegalAccessLogger.Builder();
-            Module unnamed = BootLoader.getUnnamedModule();
-            bootLayer.modules().stream().forEach(m -> {
-                m.getDescriptor()
-                 .packages()
-                 .stream()
-                 .filter(pn -> !m.isOpen(pn, unnamed))  // skip if opened by --add-opens
-                 .forEach(pn -> {
-                     builder.logAccessToOpenPackage(m, pn, "--permit-illegal-access");
-                     Modules.addOpensToAllUnnamed(m, pn);
-                 });
-            });
-            IllegalAccessLogger.setIllegalAccessLogger(builder.build());
-        }
+        return extraExportsOrOpens;
     }
 
     private static void addExtraExportsOrOpens(ModuleLayer bootLayer,
@@ -639,6 +673,102 @@
     }
 
     /**
+     * Process the --illegal-access option (and its default) to open packages
+     * of system modules in the boot layer to code in unnamed modules.
+     */
+    private static void addIllegalAccess(ModuleLayer bootLayer,
+                                         ModuleFinder upgradeModulePath,
+                                         boolean extraExportsOrOpens) {
+        String value = getAndRemoveProperty("jdk.module.illegalAccess");
+        IllegalAccessLogger.Mode mode = IllegalAccessLogger.Mode.ONESHOT;
+        if (value != null) {
+            switch (value) {
+                case "deny":
+                    return;
+                case "permit":
+                    break;
+                case "warn":
+                    mode = IllegalAccessLogger.Mode.WARN;
+                    break;
+                case "debug":
+                    mode = IllegalAccessLogger.Mode.DEBUG;
+                    break;
+                default:
+                    fail("Value specified to --illegal-access not recognized:"
+                            + " '" + value + "'");
+                    return;
+            }
+        }
+        IllegalAccessLogger.Builder builder
+            = new IllegalAccessLogger.Builder(mode, System.err);
+
+        Map<String, Set<String>> map1 = SystemModules.concealedPackagesToOpen();
+        Map<String, Set<String>> map2 = SystemModules.exportedPackagesToOpen();
+        if (map1.isEmpty() && map2.isEmpty()) {
+            // need to generate maps when on exploded build
+            IllegalAccessMaps maps = IllegalAccessMaps.generate(limitedFinder());
+            map1 = maps.concealedPackagesToOpen();
+            map2 = maps.exportedPackagesToOpen();
+        }
+
+        // open specific packages in the system modules
+        for (Module m : bootLayer.modules()) {
+            ModuleDescriptor descriptor = m.getDescriptor();
+            String name = m.getName();
+
+            // skip open modules
+            if (descriptor.isOpen()) {
+                continue;
+            }
+
+            // skip modules loaded from the upgrade module path
+            if (upgradeModulePath != null
+                && upgradeModulePath.find(name).isPresent()) {
+                continue;
+            }
+
+            Set<String> concealedPackages = map1.getOrDefault(name, Set.of());
+            Set<String> exportedPackages = map2.getOrDefault(name, Set.of());
+
+            // refresh the set of concealed and exported packages if needed
+            if (extraExportsOrOpens) {
+                concealedPackages = new HashSet<>(concealedPackages);
+                exportedPackages = new HashSet<>(exportedPackages);
+                Iterator<String> iterator = concealedPackages.iterator();
+                while (iterator.hasNext()) {
+                    String pn = iterator.next();
+                    if (m.isExported(pn, BootLoader.getUnnamedModule())) {
+                        // concealed package is exported to ALL-UNNAMED
+                        iterator.remove();
+                        exportedPackages.add(pn);
+                    }
+                }
+                iterator = exportedPackages.iterator();
+                while (iterator.hasNext()) {
+                    String pn = iterator.next();
+                    if (m.isOpen(pn, BootLoader.getUnnamedModule())) {
+                        // exported package is opened to ALL-UNNAMED
+                        iterator.remove();
+                    }
+                }
+            }
+
+            // log reflective access to all types in concealed packages
+            builder.logAccessToConcealedPackages(m, concealedPackages);
+
+            // log reflective access to non-public members/types in exported packages
+            builder.logAccessToExportedPackages(m, exportedPackages);
+
+            // open the packages to unnamed modules
+            JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
+            jla.addOpensToAllUnnamed(m, concat(concealedPackages.iterator(),
+                                               exportedPackages.iterator()));
+        }
+
+        builder.complete();
+    }
+
+    /**
      * Decodes the values of --add-reads, -add-exports, --add-opens or
      * --patch-modules options that are encoded in system properties.
      *
@@ -708,17 +838,16 @@
     }
 
     /**
-     * Checks the names and resolution bit of each module in the configuration,
-     * emitting warnings if needed.
+     * Checks incubating status of modules in the configuration
      */
-    private static void checkModuleNamesAndStatus(Configuration cf) {
+    private static void checkIncubatingStatus(Configuration cf) {
         String incubating = null;
-        for (ResolvedModule rm : cf.modules()) {
-            ModuleReference mref = rm.reference();
-            String mn = mref.descriptor().name();
+        for (ResolvedModule resolvedModule : cf.modules()) {
+            ModuleReference mref = resolvedModule.reference();
 
             // emit warning if the WARN_INCUBATING module resolution bit set
             if (ModuleResolution.hasIncubatingWarning(mref)) {
+                String mn = mref.descriptor().name();
                 if (incubating == null) {
                     incubating = mn;
                 } else {
@@ -777,6 +906,21 @@
         }
     }
 
+    static <T> Iterator<T> concat(Iterator<T> iterator1, Iterator<T> iterator2) {
+        return new Iterator<T>() {
+            @Override
+            public boolean hasNext() {
+                return iterator1.hasNext() || iterator2.hasNext();
+            }
+            @Override
+            public T next() {
+                if (iterator1.hasNext()) return iterator1.next();
+                if (iterator2.hasNext()) return iterator2.next();
+                throw new NoSuchElementException();
+            }
+        };
+    }
+
     static class PerfCounters {
 
         static PerfCounter systemModulesTime
@@ -791,6 +935,8 @@
             = PerfCounter.newPerfCounter("jdk.module.bootstrap.layerCreateTime");
         static PerfCounter loadModulesTime
             = PerfCounter.newPerfCounter("jdk.module.bootstrap.loadModulesTime");
+        static PerfCounter adjustModulesTime
+            = PerfCounter.newPerfCounter("jdk.module.bootstrap.adjustModulesTime");
         static PerfCounter bootstrapTime
             = PerfCounter.newPerfCounter("jdk.module.bootstrap.totalTime");
     }