jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
changeset 37779 7c84df693837
parent 36511 9d0388c6b336
child 38457 3d019217e322
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Tue May 03 09:09:57 2016 +0100
@@ -26,12 +26,15 @@
 package jdk.internal.module;
 
 import java.io.File;
+import java.io.PrintStream;
 import java.lang.module.Configuration;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
 import java.lang.module.ModuleReference;
-import java.lang.module.ModuleFinder;
 import java.lang.module.ResolvedModule;
 import java.lang.reflect.Layer;
 import java.lang.reflect.Module;
+import java.net.URI;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Collections;
@@ -41,10 +44,10 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
-import java.util.stream.Collectors;
 
 import jdk.internal.loader.BootLoader;
 import jdk.internal.loader.BuiltinClassLoader;
+import jdk.internal.misc.SharedSecrets;
 import jdk.internal.perf.PerfCounter;
 
 /**
@@ -54,10 +57,9 @@
  * the module system. In summary, the boot method creates a Configuration by
  * resolving a set of module names specified via the launcher (or equivalent)
  * -m and -addmods options. The modules are located on a module path that is
- * constructed from the upgrade, system and application module paths. The
- * Configuration is reified by creating the boot Layer with each module in the
- * the configuration defined to one of the built-in class loaders. The mapping
- * of modules to class loaders is statically mapped in a helper class.
+ * 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.
  */
 
 public final class ModuleBootstrap {
@@ -65,6 +67,11 @@
 
     private static final String JAVA_BASE = "java.base";
 
+    private static final String JAVA_SE = "java.se";
+
+    // the token for "all default modules"
+    private static final String ALL_DEFAULT = "ALL-DEFAULT";
+
     // the token for "all unnamed modules"
     private static final String ALL_UNNAMED = "ALL-UNNAMED";
 
@@ -94,47 +101,65 @@
 
         long t0 = System.nanoTime();
 
-        // system module path
-        ModuleFinder systemModulePath = ModuleFinder.ofSystem();
+        // system modules
+        ModuleFinder systemModules = ModuleFinder.ofSystem();
+
+        PerfCounters.systemModulesTime.addElapsedTimeFrom(t0);
 
-        // Once we have the system module path then we define the base module.
-        // We do this here so that java.base is defined to the VM as early as
+
+        long t1 = System.nanoTime();
+
+        // 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.
-        Optional<ModuleReference> obase = systemModulePath.find(JAVA_BASE);
-        if (!obase.isPresent())
+        ModuleReference base = systemModules.find(JAVA_BASE).orElse(null);
+        if (base == null)
             throw new InternalError(JAVA_BASE + " not found");
-        ModuleReference base = obase.get();
+        URI baseUri = base.location().orElse(null);
+        if (baseUri == null)
+            throw new InternalError(JAVA_BASE + " does not have a location");
         BootLoader.loadModule(base);
-        Modules.defineModule(null, base.descriptor(), base.location().orElse(null));
+        Modules.defineModule(null, base.descriptor(), baseUri);
 
+        PerfCounters.defineBaseTime.addElapsedTimeFrom(t1);
+
+
+        long t2 = System.nanoTime();
 
         // -upgrademodulepath option specified to launcher
         ModuleFinder upgradeModulePath
             = createModulePathFinder("jdk.upgrade.module.path");
+        if (upgradeModulePath != null)
+            systemModules = ModuleFinder.compose(upgradeModulePath, systemModules);
 
         // -modulepath option specified to the launcher
         ModuleFinder appModulePath = createModulePathFinder("jdk.module.path");
 
-        // The module finder: [-upgrademodulepath] system-module-path [-modulepath]
-        ModuleFinder finder = systemModulePath;
-        if (upgradeModulePath != null)
-            finder = ModuleFinder.compose(upgradeModulePath, finder);
+        // The module finder: [-upgrademodulepath] system [-modulepath]
+        ModuleFinder finder = systemModules;
         if (appModulePath != null)
             finder = ModuleFinder.compose(finder, appModulePath);
 
-        // launcher -m option to specify the initial module
+        // The root modules to resolve
+        Set<String> roots = new HashSet<>();
+
+        // launcher -m option to specify the main/initial module
         String mainModule = System.getProperty("jdk.module.main");
+        if (mainModule != null)
+            roots.add(mainModule);
 
         // additional module(s) specified by -addmods
+        boolean addAllDefaultModules = false;
         boolean addAllSystemModules = false;
         boolean addAllApplicationModules = false;
-        Set<String> addModules = null;
         String propValue = System.getProperty("jdk.launcher.addmods");
         if (propValue != null) {
-            addModules = new HashSet<>();
             for (String mod: propValue.split(",")) {
                 switch (mod) {
+                    case ALL_DEFAULT:
+                        addAllDefaultModules = true;
+                        break;
                     case ALL_SYSTEM:
                         addAllSystemModules = true;
                         break;
@@ -142,28 +167,12 @@
                         addAllApplicationModules = true;
                         break;
                     default :
-                        addModules.add(mod);
+                        roots.add(mod);
                 }
             }
         }
 
-        // The root modules to resolve
-        Set<String> roots = new HashSet<>();
-
-        // main/initial module
-        if (mainModule != null) {
-            roots.add(mainModule);
-            if (addAllApplicationModules)
-                fail(ALL_MODULE_PATH + " not allowed with initial module");
-        }
-
-        // If -addmods is specified then those modules need to be resolved
-        if (addModules != null)
-            roots.addAll(addModules);
-
-
         // -limitmods
-        boolean limitmods = false;
         propValue = System.getProperty("jdk.launcher.limitmods");
         if (propValue != null) {
             Set<String> mods = new HashSet<>();
@@ -171,62 +180,101 @@
                 mods.add(mod);
             }
             finder = limitFinder(finder, mods, roots);
-            limitmods = true;
         }
 
-
-        // If there is no initial module specified then assume that the
-        // initial module is the unnamed module of the application class
-        // loader. By convention, and for compatibility, this is
-        // implemented by putting the names of all modules on the system
-        // module path into the set of modules to resolve.
-        //
-        // If `-addmods ALL-SYSTEM` is used then all modules on the system
-        // module path will be resolved, irrespective of whether an initial
-        // module is specified.
-        //
-        // If `-addmods ALL-MODULE-PATH` is used, and no initial module is
-        // specified, then all modules on the application module path will
-        // be resolved.
-        //
-        if (mainModule == null || addAllSystemModules) {
-            Set<ModuleReference> mrefs;
-            if (addAllApplicationModules) {
-                assert mainModule == null;
-                mrefs = finder.findAll();
-            } else {
-                mrefs = systemModulePath.findAll();
-                if (limitmods) {
-                    ModuleFinder f = finder;
-                    mrefs = mrefs.stream()
-                        .filter(m -> f.find(m.descriptor().name()).isPresent())
-                        .collect(Collectors.toSet());
+        // If there is no initial module specified then assume that the initial
+        // module is the unnamed module of the application class loader. This
+        // is implemented by resolving "java.se" and all (non-java.*) modules
+        // that export an API. If "java.se" is not observable then all java.*
+        // modules are resolved.
+        if (mainModule == null || addAllDefaultModules) {
+            boolean hasJava = false;
+            if (systemModules.find(JAVA_SE).isPresent()) {
+                // java.se is a system module
+                if (finder == systemModules || finder.find(JAVA_SE).isPresent()) {
+                    // java.se is observable
+                    hasJava = true;
+                    roots.add(JAVA_SE);
                 }
             }
-            // map to module names
-            for (ModuleReference mref : mrefs) {
-                roots.add(mref.descriptor().name());
+
+            for (ModuleReference mref : systemModules.findAll()) {
+                String mn = mref.descriptor().name();
+                if (hasJava && mn.startsWith("java."))
+                    continue;
+
+                // add as root if observable and exports at least one package
+                if ((finder == systemModules || finder.find(mn).isPresent())) {
+                    ModuleDescriptor descriptor = mref.descriptor();
+                    for (ModuleDescriptor.Exports e : descriptor.exports()) {
+                        if (!e.isQualified()) {
+                            roots.add(mn);
+                            break;
+                        }
+                    }
+                }
             }
         }
 
-        long t1 = System.nanoTime();
+        // If `-addmods ALL-SYSTEM` is specified then all observable system
+        // modules will be resolved.
+        if (addAllSystemModules) {
+            ModuleFinder f = finder;  // observable modules
+            systemModules.findAll()
+                .stream()
+                .map(ModuleReference::descriptor)
+                .map(ModuleDescriptor::name)
+                .filter(mn -> f.find(mn).isPresent())  // observable
+                .forEach(mn -> roots.add(mn));
+        }
+
+        // If `-addmods ALL-MODULE-PATH` is specified then all observable
+        // modules on the application module path will be resolved.
+        if  (appModulePath != null && addAllApplicationModules) {
+            ModuleFinder f = finder;  // observable modules
+            appModulePath.findAll()
+                .stream()
+                .map(ModuleReference::descriptor)
+                .map(ModuleDescriptor::name)
+                .filter(mn -> f.find(mn).isPresent())  // observable
+                .forEach(mn -> roots.add(mn));
+        }
+
+        PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t2);
+
+
+        long t3 = System.nanoTime();
+
+        // determine if post resolution checks are needed
+        boolean needPostResolutionChecks = true;
+        if (baseUri.getScheme().equals("jrt")   // toLowerCase not needed here
+                && (upgradeModulePath == null)
+                && (appModulePath == null)
+                && (System.getProperty("jdk.launcher.patch.0") == null)) {
+            needPostResolutionChecks = false;
+        }
+
+        PrintStream traceOutput = null;
+        if (Boolean.getBoolean("jdk.launcher.traceResolver"))
+            traceOutput = System.out;
 
         // run the resolver to create the configuration
-
-        Configuration cf = Configuration.empty()
+        Configuration cf = SharedSecrets.getJavaLangModuleAccess()
                 .resolveRequiresAndUses(finder,
-                                        ModuleFinder.empty(),
-                                        roots);
+                                        roots,
+                                        needPostResolutionChecks,
+                                        traceOutput);
 
         // time to create configuration
-        PerfCounters.resolveTime.addElapsedTimeFrom(t1);
+        PerfCounters.resolveTime.addElapsedTimeFrom(t3);
+
 
         // mapping of modules to class loaders
         Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
 
         // check that all modules to be mapped to the boot loader will be
-        // loaded from the system module path
-        if (finder != systemModulePath) {
+        // loaded from the runtime image
+        if (needPostResolutionChecks) {
             for (ResolvedModule resolvedModule : cf.modules()) {
                 ModuleReference mref = resolvedModule.reference();
                 String name = mref.descriptor().name();
@@ -237,20 +285,22 @@
                             && upgradeModulePath.find(name).isPresent())
                         fail(name + ": cannot be loaded from upgrade module path");
 
-                    if (!systemModulePath.find(name).isPresent())
+                    if (!systemModules.find(name).isPresent())
                         fail(name + ": cannot be loaded from application module path");
                 }
             }
         }
 
-        long t2 = System.nanoTime();
+
+        long t4 = System.nanoTime();
 
         // define modules to VM/runtime
         Layer bootLayer = Layer.empty().defineModules(cf, clf);
 
-        PerfCounters.layerCreateTime.addElapsedTimeFrom(t2);
+        PerfCounters.layerCreateTime.addElapsedTimeFrom(t4);
 
-        long t3 = System.nanoTime();
+
+        long t5 = System.nanoTime();
 
         // define the module to its class loader, except java.base
         for (ResolvedModule resolvedModule : cf.modules()) {
@@ -264,7 +314,8 @@
             }
         }
 
-        PerfCounters.loadModulesTime.addElapsedTimeFrom(t3);
+        PerfCounters.loadModulesTime.addElapsedTimeFrom(t5);
+
 
         // -XaddReads and -XaddExports
         addExtraReads(bootLayer);
@@ -295,25 +346,21 @@
 
         // module name -> reference
         Map<String, ModuleReference> map = new HashMap<>();
+
+        // root modules and their transitive dependences
         cf.modules().stream()
             .map(ResolvedModule::reference)
             .forEach(mref -> map.put(mref.descriptor().name(), mref));
 
+        // additional modules
+        otherMods.stream()
+            .map(finder::find)
+            .flatMap(Optional::stream)
+            .forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));
+
         // set of modules that are observable
         Set<ModuleReference> mrefs = new HashSet<>(map.values());
 
-        // add the other modules
-        for (String mod : otherMods) {
-            Optional<ModuleReference> omref = finder.find(mod);
-            if (omref.isPresent()) {
-                ModuleReference mref = omref.get();
-                map.putIfAbsent(mod, mref);
-                mrefs.add(mref);
-            } else {
-                // no need to fail
-            }
-        }
-
         return new ModuleFinder() {
             @Override
             public Optional<ModuleReference> find(String name) {
@@ -369,15 +416,15 @@
 
                 Module other;
                 if (ALL_UNNAMED.equals(name)) {
-                    other = null;  // loose
+                    Modules.addReadsAllUnnamed(m);
                 } else {
                     om = bootLayer.findModule(name);
                     if (!om.isPresent())
                         fail("Unknown module: " + name);
                     other = om.get();
+                    Modules.addReads(m, other);
                 }
 
-                Modules.addReads(m, other);
             }
         }
     }
@@ -439,10 +486,6 @@
      * Decodes the values of -XaddReads or -XaddExports options
      *
      * The format of the options is: $KEY=$MODULE(,$MODULE)*
-     *
-     * For transition purposes, this method allows the first usage to be
-     *     $KEY=$MODULE(,$KEY=$MODULE)
-     * This format will eventually be removed.
      */
     private static Map<String, Set<String>> decode(String prefix) {
         int index = 0;
@@ -467,42 +510,15 @@
             if (rhs.isEmpty())
                 fail("Unable to parse: " + value);
 
-            // new format $MODULE(,$MODULE)* or old format $(MODULE)=...
-            pos = rhs.indexOf('=');
 
-            // old format only allowed in first -X option
-            if (pos >= 0 && index > 0)
-                fail("Unable to parse: " + value);
-
-            if (pos == -1) {
-
-                // new format: $KEY=$MODULE(,$MODULE)*
-
-                Set<String> values = map.get(key);
-                if (values != null)
-                    fail(key + " specified more than once");
+            // value is <module>(,<module>)*
+            if (map.containsKey(key))
+                fail(key + " specified more than once");
 
-                values = new HashSet<>();
-                map.put(key, values);
-                for (String s : rhs.split(",")) {
-                    if (s.length() > 0) values.add(s);
-                }
-
-            } else {
-
-                // old format: $KEY=$MODULE(,$KEY=$MODULE)*
-
-                assert index == 0;  // old format only allowed in first usage
-
-                for (String expr : value.split(",")) {
-                    if (expr.length() > 0) {
-                        String[] s = expr.split("=");
-                        if (s.length != 2)
-                            fail("Unable to parse: " + expr);
-
-                        map.computeIfAbsent(s[0], k -> new HashSet<>()).add(s[1]);
-                    }
-                }
+            Set<String> values = new HashSet<>();
+            map.put(key, values);
+            for (String s : rhs.split(",")) {
+                if (s.length() > 0) values.add(s);
             }
 
             index++;
@@ -521,6 +537,13 @@
     }
 
     static class PerfCounters {
+
+        static PerfCounter systemModulesTime
+            = PerfCounter.newPerfCounter("jdk.module.bootstrap.systemModulesTime");
+        static PerfCounter defineBaseTime
+            = PerfCounter.newPerfCounter("jdk.module.bootstrap.defineBaseTime");
+        static PerfCounter optionsAndRootsTime
+            = PerfCounter.newPerfCounter("jdk.module.bootstrap.optionsAndRootsTime");
         static PerfCounter resolveTime
             = PerfCounter.newPerfCounter("jdk.module.bootstrap.resolveTime");
         static PerfCounter layerCreateTime