Merge
authorduke
Thu, 24 Aug 2017 16:34:30 +0200
changeset 46102 81e69cdccc36
parent 46101 14287f4ad373 (current diff)
parent 46100 39106800b006 (diff)
child 46104 6ba0d3a79707
Merge
jdk/src/java.base/share/classes/jdk/internal/module/SystemModuleFinder.java
jdk/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModules.java
--- a/.hgtags-top-repo	Fri Aug 04 23:29:04 2017 +0000
+++ b/.hgtags-top-repo	Thu Aug 24 16:34:30 2017 +0200
@@ -440,3 +440,4 @@
 ec4159ebe7050fcc5dcee8a2d150cf948ecc97db jdk-9+178
 252475ccfd84cc249f8d6faf4b7806b5e2c384ce jdk-9+179
 a133a7d1007b1456bc62824382fd8ac93b45d329 jdk-10+17
+536b81db8075486ca0fe3225d8e59313df5b936c jdk-10+18
--- a/corba/.hgtags	Fri Aug 04 23:29:04 2017 +0000
+++ b/corba/.hgtags	Thu Aug 24 16:34:30 2017 +0200
@@ -440,3 +440,4 @@
 9c1e9712648921ae389d623042d22561fad82d75 jdk-9+178
 24390da83c5ee9e23ceafbcaff4460a01e37bb3a jdk-9+179
 50ff1fd66362f212a8db6de76089d9d0ffa4df0f jdk-10+17
+a923b3f30e7bddb4f960059ddfc7978fc63e2e6e jdk-10+18
--- a/hotspot/.hgtags	Fri Aug 04 23:29:04 2017 +0000
+++ b/hotspot/.hgtags	Thu Aug 24 16:34:30 2017 +0200
@@ -600,3 +600,4 @@
 9d032191f82fca5ba0aac98682f69c4ff0f1283d jdk-9+178
 d2661aa42bff322badbe6c1337fc638d2e0f5730 jdk-9+179
 73e2cb8700bfa51304bd4b02f224620859a3f600 jdk-10+17
+c9d3317623d48da3327232c81e3f8cfc0d29d888 jdk-10+18
--- a/jdk/.hgtags	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/.hgtags	Thu Aug 24 16:34:30 2017 +0200
@@ -440,3 +440,4 @@
 443025bee731eb2225371b92c1c74b519b7baf33 jdk-9+178
 06df1ce4b9b887d05ce6a13f4def3547e434dd1a jdk-9+179
 d93f2fd542b7d7855c2cd49ae15ebcc3d441a83b jdk-10+17
+c4b709bad6c5d29294124de5e74e1e2ac84fcf1f jdk-10+18
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/DESKey.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/DESKey.java	Thu Aug 24 16:34:30 2017 +0200
@@ -31,6 +31,8 @@
 import javax.crypto.SecretKey;
 import javax.crypto.spec.DESKeySpec;
 
+import jdk.internal.ref.CleanerFactory;
+
 /**
  * This class represents a DES key.
  *
@@ -74,6 +76,11 @@
         this.key = new byte[DESKeySpec.DES_KEY_LEN];
         System.arraycopy(key, offset, this.key, 0, DESKeySpec.DES_KEY_LEN);
         DESKeyGenerator.setParityBit(this.key, 0);
+
+        // Use the cleaner to zero the key when no longer referenced
+        final byte[] k = this.key;
+        CleanerFactory.cleaner().register(this,
+                () -> java.util.Arrays.fill(k, (byte)0x00));
     }
 
     public byte[] getEncoded() {
@@ -144,20 +151,4 @@
                         getFormat(),
                         getEncoded());
     }
-
-    /**
-     * Ensures that the bytes of this key are
-     * set to zero when there are no more references to it.
-     */
-    @SuppressWarnings("deprecation")
-    protected void finalize() throws Throwable {
-        try {
-            if (this.key != null) {
-                java.util.Arrays.fill(this.key, (byte)0x00);
-                this.key = null;
-            }
-        } finally {
-            super.finalize();
-        }
-    }
 }
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/DESedeKey.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/DESedeKey.java	Thu Aug 24 16:34:30 2017 +0200
@@ -31,6 +31,8 @@
 import javax.crypto.SecretKey;
 import javax.crypto.spec.DESedeKeySpec;
 
+import jdk.internal.ref.CleanerFactory;
+
 /**
  * This class represents a DES-EDE key.
  *
@@ -76,6 +78,11 @@
         DESKeyGenerator.setParityBit(this.key, 0);
         DESKeyGenerator.setParityBit(this.key, 8);
         DESKeyGenerator.setParityBit(this.key, 16);
+
+        // Use the cleaner to zero the key when no longer referenced
+        final byte[] k = this.key;
+        CleanerFactory.cleaner().register(this,
+                () -> java.util.Arrays.fill(k, (byte)0x00));
     }
 
     public byte[] getEncoded() {
@@ -145,20 +152,4 @@
                         getFormat(),
                         getEncoded());
     }
-
-    /**
-     * Ensures that the bytes of this key are
-     * set to zero when there are no more references to it.
-     */
-    @SuppressWarnings("deprecation")
-    protected void finalize() throws Throwable {
-        try {
-            if (this.key != null) {
-                java.util.Arrays.fill(this.key, (byte)0x00);
-                this.key = null;
-            }
-        } finally {
-            super.finalize();
-        }
-    }
 }
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java	Thu Aug 24 16:34:30 2017 +0200
@@ -32,6 +32,8 @@
 import javax.crypto.SecretKey;
 import javax.crypto.spec.PBEKeySpec;
 
+import jdk.internal.ref.CleanerFactory;
+
 /**
  * This class represents a PBE key.
  *
@@ -49,7 +51,7 @@
     /**
      * Creates a PBE key from a given PBE key specification.
      *
-     * @param key the given PBE key specification
+     * @param keytype the given PBE key specification
      */
     PBEKey(PBEKeySpec keySpec, String keytype) throws InvalidKeySpecException {
         char[] passwd = keySpec.getPassword();
@@ -70,6 +72,11 @@
             this.key[i] = (byte) (passwd[i] & 0x7f);
         java.util.Arrays.fill(passwd, ' ');
         type = keytype;
+
+        // Use the cleaner to zero the key when no longer referenced
+        final byte[] k = this.key;
+        CleanerFactory.cleaner().register(this,
+                () -> java.util.Arrays.fill(k, (byte)0x00));
     }
 
     public byte[] getEncoded() {
@@ -140,20 +147,4 @@
                         getFormat(),
                         getEncoded());
     }
-
-    /**
-     * Ensures that the password bytes of this key are
-     * set to zero when there are no more references to it.
-     */
-    @SuppressWarnings("deprecation")
-    protected void finalize() throws Throwable {
-        try {
-            if (this.key != null) {
-                java.util.Arrays.fill(this.key, (byte)0x00);
-                this.key = null;
-            }
-        } finally {
-            super.finalize();
-        }
-    }
 }
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java	Thu Aug 24 16:34:30 2017 +0200
@@ -40,6 +40,8 @@
 import javax.crypto.SecretKey;
 import javax.crypto.spec.PBEKeySpec;
 
+import jdk.internal.ref.CleanerFactory;
+
 /**
  * This class represents a PBE key derived using PBKDF2 defined
  * in PKCS#5 v2.0. meaning that
@@ -76,7 +78,8 @@
     /**
      * Creates a PBE key from a given PBE key specification.
      *
-     * @param key the given PBE key specification
+     * @param keySpec the given PBE key specification
+     * @param prfAlgo the given PBE key algorithm
      */
     PBKDF2KeyImpl(PBEKeySpec keySpec, String prfAlgo)
         throws InvalidKeySpecException {
@@ -120,6 +123,15 @@
             throw ike;
         }
         this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength);
+
+        // Use the cleaner to zero the key when no longer referenced
+        final byte[] k = this.key;
+        final char[] p = this.passwd;
+        CleanerFactory.cleaner().register(this,
+                () -> {
+                    java.util.Arrays.fill(k, (byte)0x00);
+                    java.util.Arrays.fill(p, '0');
+                });
     }
 
     private static byte[] deriveKey(final Mac prf, final byte[] password,
@@ -262,24 +274,4 @@
             return new KeyRep(KeyRep.Type.SECRET, getAlgorithm(),
                               getFormat(), getEncoded());
     }
-
-    /**
-     * Ensures that the password bytes of this key are
-     * erased when there are no more references to it.
-     */
-    @SuppressWarnings("deprecation")
-    protected void finalize() throws Throwable {
-        try {
-            if (this.passwd != null) {
-                java.util.Arrays.fill(this.passwd, '0');
-                this.passwd = null;
-            }
-            if (this.key != null) {
-                java.util.Arrays.fill(this.key, (byte)0x00);
-                this.key = null;
-            }
-        } finally {
-            super.finalize();
-        }
-    }
 }
--- a/jdk/src/java.base/share/classes/java/lang/Class.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/Class.java	Thu Aug 24 16:34:30 2017 +0200
@@ -432,18 +432,21 @@
         Objects.requireNonNull(module);
         Objects.requireNonNull(name);
 
-        Class<?> caller = Reflection.getCallerClass();
-        if (caller != null && caller.getModule() != module) {
-            // if caller is null, Class.forName is the last java frame on the stack.
-            // java.base has all permissions
-            SecurityManager sm = System.getSecurityManager();
-            if (sm != null) {
+        ClassLoader cl;
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            Class<?> caller = Reflection.getCallerClass();
+            if (caller != null && caller.getModule() != module) {
+                // if caller is null, Class.forName is the last java frame on the stack.
+                // java.base has all permissions
                 sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
             }
+            PrivilegedAction<ClassLoader> pa = module::getClassLoader;
+            cl = AccessController.doPrivileged(pa);
+        } else {
+            cl = module.getClassLoader();
         }
 
-        PrivilegedAction<ClassLoader> pa = module::getClassLoader;
-        ClassLoader cl = AccessController.doPrivileged(pa);
         if (cl != null) {
             return cl.loadClass(module, name);
         } else {
--- a/jdk/src/java.base/share/classes/java/lang/Module.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/Module.java	Thu Aug 24 16:34:30 2017 +0200
@@ -246,7 +246,6 @@
         return null;
     }
 
-
     // --
 
     // special Module to mean "all unnamed modules"
@@ -257,17 +256,38 @@
     private static final Module EVERYONE_MODULE = new Module(null);
     private static final Set<Module> EVERYONE_SET = Set.of(EVERYONE_MODULE);
 
+    /**
+     * The holder of data structures to support readability, exports, and
+     * service use added at runtime with the reflective APIs.
+     */
+    private static class ReflectionData {
+        /**
+         * A module (1st key) reads another module (2nd key)
+         */
+        static final WeakPairMap<Module, Module, Boolean> reads =
+            new WeakPairMap<>();
+
+        /**
+         * A module (1st key) exports or opens a package to another module
+         * (2nd key). The map value is a map of package name to a boolean
+         * that indicates if the package is opened.
+         */
+        static final WeakPairMap<Module, Module, Map<String, Boolean>> exports =
+            new WeakPairMap<>();
+
+        /**
+         * A module (1st key) uses a service (2nd key)
+         */
+        static final WeakPairMap<Module, Class<?>, Boolean> uses =
+            new WeakPairMap<>();
+    }
+
 
     // -- readability --
 
     // the modules that this module reads
     private volatile Set<Module> reads;
 
-    // additional module (2nd key) that some module (1st key) reflectively reads
-    private static final WeakPairMap<Module, Module, Boolean> reflectivelyReads
-        = new WeakPairMap<>();
-
-
     /**
      * Indicates if this module reads the given module. This method returns
      * {@code true} if invoked to test if this module reads itself. It also
@@ -300,13 +320,13 @@
         }
 
         // check if this module reads the other module reflectively
-        if (reflectivelyReads.containsKeyPair(this, other))
+        if (ReflectionData.reads.containsKeyPair(this, other))
             return true;
 
         // if other is an unnamed module then check if this module reads
         // all unnamed modules
         if (!other.isNamed()
-            && reflectivelyReads.containsKeyPair(this, ALL_UNNAMED_MODULE))
+            && ReflectionData.reads.containsKeyPair(this, ALL_UNNAMED_MODULE))
             return true;
 
         return false;
@@ -393,7 +413,7 @@
             }
 
             // add reflective read
-            reflectivelyReads.putIfAbsent(this, other, Boolean.TRUE);
+            ReflectionData.reads.putIfAbsent(this, other, Boolean.TRUE);
         }
     }
 
@@ -408,13 +428,6 @@
     // if the value contains EVERYONE_MODULE then the package is exported to all
     private volatile Map<String, Set<Module>> exportedPackages;
 
-    // additional exports or opens added at run-time
-    // this module (1st key), other module (2nd key)
-    // (package name, open?) (value)
-    private static final WeakPairMap<Module, Module, Map<String, Boolean>>
-        reflectivelyExports = new WeakPairMap<>();
-
-
     /**
      * Returns {@code true} if this module exports the given package to at
      * least the given module.
@@ -600,7 +613,7 @@
      */
     private boolean isReflectivelyExportedOrOpen(String pn, Module other, boolean open) {
         // exported or open to all modules
-        Map<String, Boolean> exports = reflectivelyExports.get(this, EVERYONE_MODULE);
+        Map<String, Boolean> exports = ReflectionData.exports.get(this, EVERYONE_MODULE);
         if (exports != null) {
             Boolean b = exports.get(pn);
             if (b != null) {
@@ -612,7 +625,7 @@
         if (other != EVERYONE_MODULE) {
 
             // exported or open to other
-            exports = reflectivelyExports.get(this, other);
+            exports = ReflectionData.exports.get(this, other);
             if (exports != null) {
                 Boolean b = exports.get(pn);
                 if (b != null) {
@@ -623,7 +636,7 @@
 
             // other is an unnamed module && exported or open to all unnamed
             if (!other.isNamed()) {
-                exports = reflectivelyExports.get(this, ALL_UNNAMED_MODULE);
+                exports = ReflectionData.exports.get(this, ALL_UNNAMED_MODULE);
                 if (exports != null) {
                     Boolean b = exports.get(pn);
                     if (b != null) {
@@ -886,8 +899,8 @@
             }
         }
 
-        // add package name to reflectivelyExports if absent
-        Map<String, Boolean> map = reflectivelyExports
+        // add package name to exports if absent
+        Map<String, Boolean> map = ReflectionData.exports
             .computeIfAbsent(this, other,
                              (m1, m2) -> new ConcurrentHashMap<>());
         if (open) {
@@ -932,10 +945,6 @@
 
     // -- services --
 
-    // additional service type (2nd key) that some module (1st key) uses
-    private static final WeakPairMap<Module, Class<?>, Boolean> reflectivelyUses
-        = new WeakPairMap<>();
-
     /**
      * If the caller's module is this module then update this module to add a
      * service dependence on the given service type. This method is intended
@@ -980,7 +989,7 @@
      */
     void implAddUses(Class<?> service) {
         if (!canUse(service)) {
-            reflectivelyUses.putIfAbsent(this, service, Boolean.TRUE);
+            ReflectionData.uses.putIfAbsent(this, service, Boolean.TRUE);
         }
     }
 
@@ -1011,7 +1020,7 @@
             return true;
 
         // uses added via addUses
-        return reflectivelyUses.containsKeyPair(this, service);
+        return ReflectionData.uses.containsKeyPair(this, service);
     }
 
 
@@ -1060,8 +1069,11 @@
                                              Function<String, ClassLoader> clf,
                                              ModuleLayer layer)
     {
-        Map<String, Module> nameToModule = new HashMap<>();
-        Map<String, ClassLoader> moduleToLoader = new HashMap<>();
+        boolean isBootLayer = (ModuleLayer.boot() == null);
+
+        int cap = (int)(cf.modules().size() / 0.75f + 1.0f);
+        Map<String, Module> nameToModule = new HashMap<>(cap);
+        Map<String, ClassLoader> nameToLoader = new HashMap<>(cap);
 
         Set<ClassLoader> loaders = new HashSet<>();
         boolean hasPlatformModules = false;
@@ -1070,7 +1082,7 @@
         for (ResolvedModule resolvedModule : cf.modules()) {
             String name = resolvedModule.name();
             ClassLoader loader = clf.apply(name);
-            moduleToLoader.put(name, loader);
+            nameToLoader.put(name, loader);
             if (loader == null || loader == ClassLoaders.platformClassLoader()) {
                 if (!(clf instanceof ModuleLoaderMap.Mapper)) {
                     throw new IllegalArgumentException("loader can't be 'null'"
@@ -1087,20 +1099,19 @@
             ModuleReference mref = resolvedModule.reference();
             ModuleDescriptor descriptor = mref.descriptor();
             String name = descriptor.name();
-            URI uri = mref.location().orElse(null);
-            ClassLoader loader = moduleToLoader.get(resolvedModule.name());
+            ClassLoader loader = nameToLoader.get(name);
             Module m;
             if (loader == null && name.equals("java.base")) {
                 // java.base is already defined to the VM
                 m = Object.class.getModule();
             } else {
+                URI uri = mref.location().orElse(null);
                 m = new Module(layer, loader, descriptor, uri);
             }
             nameToModule.put(name, m);
-            moduleToLoader.put(name, loader);
         }
 
-        // setup readability and exports
+        // setup readability and exports/opens
         for (ResolvedModule resolvedModule : cf.modules()) {
             ModuleReference mref = resolvedModule.reference();
             ModuleDescriptor descriptor = mref.descriptor();
@@ -1146,7 +1157,18 @@
             }
 
             // exports and opens
-            initExportsAndOpens(m, nameToSource, nameToModule, layer.parents());
+            if (descriptor.isOpen() || descriptor.isAutomatic()) {
+                // The VM doesn't special case open or automatic modules yet
+                // so need to export all packages
+                for (String source : descriptor.packages()) {
+                    addExportsToAll0(m, source);
+                }
+            } else if (isBootLayer && descriptor.opens().isEmpty()) {
+                // no open packages, no qualified exports to modules in parent layers
+                initExports(m, nameToModule);
+            } else {
+                initExportsAndOpens(m, nameToSource, nameToModule, layer.parents());
+            }
         }
 
         // if there are modules defined to the boot or platform class loaders
@@ -1161,7 +1183,7 @@
                 if (!descriptor.provides().isEmpty()) {
                     String name = descriptor.name();
                     Module m = nameToModule.get(name);
-                    ClassLoader loader = moduleToLoader.get(name);
+                    ClassLoader loader = nameToLoader.get(name);
                     if (loader == null) {
                         bootCatalog.register(m);
                     } else if (loader == pcl) {
@@ -1179,7 +1201,6 @@
         return nameToModule;
     }
 
-
     /**
      * Find the runtime Module corresponding to the given ResolvedModule
      * in the given parent layer (or its parents).
@@ -1201,25 +1222,55 @@
                 .orElse(null);
     }
 
+    /**
+     * Initialize/setup a module's exports.
+     *
+     * @param m the module
+     * @param nameToModule map of module name to Module (for qualified exports)
+     */
+    private static void initExports(Module m, Map<String, Module> nameToModule) {
+        Map<String, Set<Module>> exportedPackages = new HashMap<>();
+
+        for (Exports exports : m.getDescriptor().exports()) {
+            String source = exports.source();
+            if (exports.isQualified()) {
+                // qualified exports
+                Set<Module> targets = new HashSet<>();
+                for (String target : exports.targets()) {
+                    Module m2 = nameToModule.get(target);
+                    if (m2 != null) {
+                        addExports0(m, source, m2);
+                        targets.add(m2);
+                    }
+                }
+                if (!targets.isEmpty()) {
+                    exportedPackages.put(source, targets);
+                }
+            } else {
+                // unqualified exports
+                addExportsToAll0(m, source);
+                exportedPackages.put(source, EVERYONE_SET);
+            }
+        }
+
+        if (!exportedPackages.isEmpty())
+            m.exportedPackages = exportedPackages;
+    }
 
     /**
-     * Initialize the maps of exported and open packages for module m.
+     * Initialize/setup a module's exports.
+     *
+     * @param m the module
+     * @param nameToSource map of module name to Module for modules that m reads
+     * @param nameToModule map of module name to Module for modules in the layer
+     *                     under construction
+     * @param parents the parent layers
      */
     private static void initExportsAndOpens(Module m,
                                             Map<String, Module> nameToSource,
                                             Map<String, Module> nameToModule,
                                             List<ModuleLayer> parents) {
-        // The VM doesn't special case open or automatic modules so need to
-        // export all packages
         ModuleDescriptor descriptor = m.getDescriptor();
-        if (descriptor.isOpen() || descriptor.isAutomatic()) {
-            assert descriptor.opens().isEmpty();
-            for (String source : descriptor.packages()) {
-                addExportsToAll0(m, source);
-            }
-            return;
-        }
-
         Map<String, Set<Module>> openPackages = new HashMap<>();
         Map<String, Set<Module>> exportedPackages = new HashMap<>();
 
@@ -1272,7 +1323,6 @@
                 if (!targets.isEmpty()) {
                     exportedPackages.put(source, targets);
                 }
-
             } else {
                 // unqualified exports
                 addExportsToAll0(m, source);
--- a/jdk/src/java.base/share/classes/java/lang/System.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/System.java	Thu Aug 24 16:34:30 2017 +0200
@@ -313,6 +313,10 @@
      * @see java.lang.RuntimePermission
      */
     public static void setSecurityManager(final SecurityManager s) {
+        if (security == null) {
+            // ensure image reader is initialized
+            Object.class.getResource("java/lang/ANY");
+        }
         if (s != null) {
             try {
                 s.checkPackageAccess("java.lang");
--- a/jdk/src/java.base/share/classes/java/lang/module/Configuration.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/module/Configuration.java	Thu Aug 24 16:34:30 2017 +0200
@@ -31,6 +31,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Deque;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -41,6 +42,9 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import jdk.internal.module.ModuleReferenceImpl;
+import jdk.internal.module.ModuleTarget;
+
 /**
  * A configuration that is the result of <a href="package-summary.html#resolution">
  * resolution</a> or resolution with <a href="#service-binding">service binding</a>.
@@ -121,11 +125,8 @@
         this.targetPlatform = null;
     }
 
-    private Configuration(List<Configuration> parents,
-                          Resolver resolver,
-                          boolean check)
-    {
-        Map<ResolvedModule, Set<ResolvedModule>> g = resolver.finish(this, check);
+    private Configuration(List<Configuration> parents, Resolver resolver) {
+        Map<ResolvedModule, Set<ResolvedModule>> g = resolver.finish(this);
 
         @SuppressWarnings(value = {"rawtypes", "unchecked"})
         Entry<String, ResolvedModule>[] nameEntries
@@ -147,6 +148,62 @@
     }
 
     /**
+     * Creates the Configuration for the boot layer from a pre-generated
+     * readability graph.
+     *
+     * @apiNote This method is coded for startup performance.
+     */
+    Configuration(ModuleFinder finder, Map<String, Set<String>> map) {
+        int moduleCount = map.size();
+
+        // create map of name -> ResolvedModule
+        @SuppressWarnings(value = {"rawtypes", "unchecked"})
+        Entry<String, ResolvedModule>[] nameEntries
+            = (Entry<String, ResolvedModule>[])new Entry[moduleCount];
+        ResolvedModule[] moduleArray = new ResolvedModule[moduleCount];
+        String targetPlatform = null;
+        int i = 0;
+        for (String name : map.keySet()) {
+            ModuleReference mref = finder.find(name).orElse(null);
+            assert mref != null;
+
+            if (targetPlatform == null && mref instanceof ModuleReferenceImpl) {
+                ModuleTarget target = ((ModuleReferenceImpl)mref).moduleTarget();
+                if (target != null) {
+                    targetPlatform = target.targetPlatform();
+                }
+            }
+
+            ResolvedModule resolvedModule = new ResolvedModule(this, mref);
+            moduleArray[i] = resolvedModule;
+            nameEntries[i] = Map.entry(name, resolvedModule);
+            i++;
+        }
+        Map<String, ResolvedModule> nameToModule = Map.ofEntries(nameEntries);
+
+        // create entries for readability graph
+        @SuppressWarnings(value = {"rawtypes", "unchecked"})
+        Entry<ResolvedModule, Set<ResolvedModule>>[] moduleEntries
+            = (Entry<ResolvedModule, Set<ResolvedModule>>[])new Entry[moduleCount];
+        i = 0;
+        for (ResolvedModule resolvedModule : moduleArray) {
+            Set<String> names = map.get(resolvedModule.name());
+            ResolvedModule[] readsArray = new ResolvedModule[names.size()];
+            int j = 0;
+            for (String name : names) {
+                readsArray[j++] = nameToModule.get(name);
+            }
+            moduleEntries[i++] = Map.entry(resolvedModule, Set.of(readsArray));
+        }
+
+        this.parents = List.of(empty());
+        this.graph = Map.ofEntries(moduleEntries);
+        this.modules = Set.of(moduleArray);
+        this.nameToModule = nameToModule;
+        this.targetPlatform = targetPlatform;
+    }
+
+    /**
      * Resolves a collection of root modules, with this configuration as its
      * parent, to create a new configuration. This method works exactly as
      * specified by the static {@link
@@ -233,24 +290,20 @@
 
     /**
      * Resolves a collection of root modules, with service binding, and with
-     * the empty configuration as its parent. The consistency checks
-     * are optionally run.
+     * the empty configuration as its parent.
      *
      * This method is used to create the configuration for the boot layer.
      */
     static Configuration resolveAndBind(ModuleFinder finder,
                                         Collection<String> roots,
-                                        boolean check,
                                         PrintStream traceOutput)
     {
         List<Configuration> parents = List.of(empty());
         Resolver resolver = new Resolver(finder, parents, ModuleFinder.of(), traceOutput);
         resolver.resolve(roots).bind();
-
-        return new Configuration(parents, resolver, check);
+        return new Configuration(parents, resolver);
     }
 
-
     /**
      * Resolves a collection of root modules to create a configuration.
      *
@@ -356,7 +409,7 @@
         Resolver resolver = new Resolver(before, parentList, after, null);
         resolver.resolve(roots);
 
-        return new Configuration(parentList, resolver, true);
+        return new Configuration(parentList, resolver);
     }
 
     /**
@@ -427,7 +480,7 @@
         Resolver resolver = new Resolver(before, parentList, after, null);
         resolver.resolve(roots).bind();
 
-        return new Configuration(parentList, resolver, true);
+        return new Configuration(parentList, resolver);
     }
 
 
--- a/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Thu Aug 24 16:34:30 2017 +0200
@@ -2728,10 +2728,15 @@
                 @Override
                 public Configuration resolveAndBind(ModuleFinder finder,
                                                     Collection<String> roots,
-                                                    boolean check,
                                                     PrintStream traceOutput)
                 {
-                    return Configuration.resolveAndBind(finder, roots, check, traceOutput);
+                    return Configuration.resolveAndBind(finder, roots, traceOutput);
+                }
+
+                @Override
+                public Configuration newConfiguration(ModuleFinder finder,
+                                                      Map<String, Set<String>> graph) {
+                    return new Configuration(finder, graph);
                 }
             });
     }
--- a/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Thu Aug 24 16:34:30 2017 +0200
@@ -25,9 +25,7 @@
 
 package java.lang.module;
 
-import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.security.AccessController;
 import java.security.Permission;
 import java.security.PrivilegedAction;
@@ -40,10 +38,8 @@
 import java.util.Optional;
 import java.util.Set;
 
-import jdk.internal.module.ModuleBootstrap;
-import jdk.internal.module.ModulePatcher;
 import jdk.internal.module.ModulePath;
-import jdk.internal.module.SystemModuleFinder;
+import jdk.internal.module.SystemModuleFinders;
 
 /**
  * A finder of modules. A {@code ModuleFinder} is used to find modules during
@@ -157,53 +153,14 @@
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
             sm.checkPermission(new RuntimePermission("accessSystemModules"));
-            PrivilegedAction<ModuleFinder> pa = ModuleFinder::privilegedOfSystem;
+            PrivilegedAction<ModuleFinder> pa = SystemModuleFinders::ofSystem;
             return AccessController.doPrivileged(pa);
         } else {
-            return privilegedOfSystem();
-        }
-    }
-
-    /**
-     * Returns a module finder that locates the system modules. This method
-     * assumes it has permissions to access the runtime image.
-     */
-    private static ModuleFinder privilegedOfSystem() {
-        String home = System.getProperty("java.home");
-        Path modules = Paths.get(home, "lib", "modules");
-        if (Files.isRegularFile(modules)) {
-            return SystemModuleFinder.getInstance();
-        } else {
-            Path dir = Paths.get(home, "modules");
-            if (Files.isDirectory(dir)) {
-                return privilegedOf(ModuleBootstrap.patcher(), dir);
-            } else {
-                throw new InternalError("Unable to detect the run-time image");
-            }
+            return SystemModuleFinders.ofSystem();
         }
     }
 
     /**
-     * Returns a module finder that locates the system modules in an exploded
-     * image. The image may be patched.
-     */
-    private static ModuleFinder privilegedOf(ModulePatcher patcher, Path dir) {
-        ModuleFinder finder = ModulePath.of(patcher, dir);
-        return new ModuleFinder() {
-            @Override
-            public Optional<ModuleReference> find(String name) {
-                PrivilegedAction<Optional<ModuleReference>> pa = () -> finder.find(name);
-                return AccessController.doPrivileged(pa);
-            }
-            @Override
-            public Set<ModuleReference> findAll() {
-                PrivilegedAction<Set<ModuleReference>> pa = finder::findAll;
-                return AccessController.doPrivileged(pa);
-            }
-        };
-    }
-
-    /**
      * Returns a module finder that locates modules on the file system by
      * searching a sequence of directories and/or packaged modules.
      *
--- a/jdk/src/java.base/share/classes/java/lang/module/Resolver.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/module/Resolver.java	Thu Aug 24 16:34:30 2017 +0200
@@ -353,25 +353,13 @@
 
     /**
      * Execute post-resolution checks and returns the module graph of resolved
-     * modules as {@code Map}. The resolved modules will be in the given
-     * configuration.
-     *
-     * @param check {@true} to execute the post resolution checks
+     * modules as a map.
      */
-    Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf,
-                                                    boolean check)
-    {
-        if (check) {
-            detectCycles();
-            checkHashes();
-        }
-
+    Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf) {
+        detectCycles();
+        checkHashes();
         Map<ResolvedModule, Set<ResolvedModule>> graph = makeGraph(cf);
-
-        if (check) {
-            checkExportSuppliers(graph);
-        }
-
+        checkExportSuppliers(graph);
         return graph;
     }
 
--- a/jdk/src/java.base/share/classes/java/net/URL.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/net/URL.java	Thu Aug 24 16:34:30 2017 +0200
@@ -409,7 +409,7 @@
             }
         }
 
-        protocol = protocol.toLowerCase(Locale.ROOT);
+        protocol = toLowerCase(protocol);
         this.protocol = protocol;
         if (host != null) {
 
@@ -585,7 +585,7 @@
             for (i = start ; !aRef && (i < limit) &&
                      ((c = spec.charAt(i)) != '/') ; i++) {
                 if (c == ':') {
-                    String s = spec.substring(start, i).toLowerCase(Locale.ROOT);
+                    String s = toLowerCase(spec.substring(start, i));
                     if (isValidProtocol(s)) {
                         newProtocol = s;
                         start = i + 1;
@@ -1318,6 +1318,17 @@
         }
     }
 
+    /**
+     * Returns the protocol in lower case. Special cases known protocols
+     * to avoid loading locale classes during startup.
+     */
+    static String toLowerCase(String protocol) {
+        if (protocol.equals("jrt") || protocol.equals("file") || protocol.equals("jar")) {
+            return protocol;
+        } else {
+            return protocol.toLowerCase(Locale.ROOT);
+        }
+    }
 
     /**
      * Non-overrideable protocols: "jrt" and "file"
--- a/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Thu Aug 24 16:34:30 2017 +0200
@@ -55,13 +55,13 @@
 import java.util.NoSuchElementException;
 import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
 import java.util.stream.Stream;
 
 import jdk.internal.misc.VM;
 import jdk.internal.module.ModulePatcher.PatchedModuleReader;
-import jdk.internal.module.SystemModules;
 import jdk.internal.module.Resources;
 
 
@@ -139,7 +139,7 @@
 
     // maps package name to loaded module for modules in the boot layer
     private static final Map<String, LoadedModule> packageToModule
-        = new ConcurrentHashMap<>(SystemModules.PACKAGES_IN_BOOT_LAYER);
+        = new ConcurrentHashMap<>(1024);
 
     // maps a module name to a module reference
     private final Map<String, ModuleReference> nameToModule;
@@ -946,9 +946,16 @@
         URL url = cs.getLocation();
         if (url == null)
             return perms;
-        Permission p = null;
+
+        // avoid opening connection when URL is to resource in run-time image
+        if (url.getProtocol().equals("jrt")) {
+            perms.add(new RuntimePermission("accessSystemModules"));
+            return perms;
+        }
+
+        // open connection to determine the permission needed
         try {
-            p = url.openConnection().getPermission();
+            Permission p = url.openConnection().getPermission();
             if (p != null) {
                 // for directories then need recursive access
                 if (p instanceof FilePermission) {
@@ -969,23 +976,26 @@
     // -- miscellaneous supporting methods
 
     /**
-     * Returns the ModuleReader for the given module.
+     * Returns the ModuleReader for the given module, creating it if needed
      */
     private ModuleReader moduleReaderFor(ModuleReference mref) {
-        return moduleToReader.computeIfAbsent(mref, BuiltinClassLoader::createModuleReader);
-    }
-
-    /**
-     * Creates a ModuleReader for the given module.
-     */
-    private static ModuleReader createModuleReader(ModuleReference mref) {
-        try {
-            return mref.open();
-        } catch (IOException e) {
-            // Return a null module reader to avoid a future class load
-            // attempting to open the module again.
-            return new NullModuleReader();
+        ModuleReader reader = moduleToReader.get(mref);
+        if (reader == null) {
+            // avoid method reference during startup
+            Function<ModuleReference, ModuleReader> create = new Function<>() {
+                public ModuleReader apply(ModuleReference moduleReference) {
+                    try {
+                        return mref.open();
+                    } catch (IOException e) {
+                        // Return a null module reader to avoid a future class
+                        // load attempting to open the module again.
+                        return new NullModuleReader();
+                    }
+                }
+            };
+            reader = moduleToReader.computeIfAbsent(mref, create);
         }
+        return reader;
     }
 
     /**
--- a/jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java	Thu Aug 24 16:34:30 2017 +0200
@@ -25,7 +25,6 @@
 
 package jdk.internal.loader;
 
-import java.io.File;
 import java.io.IOException;
 import java.net.URL;
 import java.nio.file.InvalidPathException;
@@ -38,7 +37,6 @@
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.misc.VM;
 
-
 /**
  * Creates and provides access to the built-in platform and application class
  * loaders. It also creates the class loader that is used to locate resources
@@ -61,23 +59,30 @@
      */
     static {
 
-        // -Xbootclasspth/a or -javaagent Boot-Class-Path
+        // -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute
         URLClassPath bcp = null;
         String s = VM.getSavedProperty("jdk.boot.class.path.append");
         if (s != null && s.length() > 0)
-            bcp = toURLClassPath(s);
+            bcp = new URLClassPath(s, true);
 
         // we have a class path if -cp is specified or -m is not specified.
         // If neither is specified then default to -cp <working directory>
         // If -cp is not specified and -m is specified, the value of
         // java.class.path is an empty string, then no class path.
-        URLClassPath ucp = new URLClassPath(new URL[0]);
         String mainMid = System.getProperty("jdk.module.main");
         String cp = System.getProperty("java.class.path");
-        if (cp == null)
-            cp = "";
-        if (mainMid == null || cp.length() > 0)
-            addClassPathToUCP(cp, ucp);
+        if (mainMid == null) {
+            // no main module specified so class path required
+            if (cp == null) {
+                cp = "";
+            }
+        } else {
+            // main module specified, ignore empty class path
+            if (cp != null && cp.length() == 0) {
+                cp = null;
+            }
+        }
+        URLClassPath ucp = new URLClassPath(cp, false);
 
         // create the class loaders
         BOOT_LOADER = new BootClassLoader(bcp);
@@ -198,7 +203,7 @@
          * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch
          */
         void appendToClassPathForInstrumentation(String path) {
-            addClassPathToUCP(path, ucp);
+            ucp.addFile(path);
         }
 
         /**
@@ -220,40 +225,11 @@
     }
 
     /**
-     * Returns a {@code URLClassPath} of file URLs to each of the elements in
-     * the given class path.
-     */
-    private static URLClassPath toURLClassPath(String cp) {
-        URLClassPath ucp = new URLClassPath(new URL[0]);
-        addClassPathToUCP(cp, ucp);
-        return ucp;
-    }
-
-    /**
-     * Converts the elements in the given class path to file URLs and adds
-     * them to the given URLClassPath.
-     */
-    private static void addClassPathToUCP(String cp, URLClassPath ucp) {
-        int off = 0;
-        int next;
-        while ((next = cp.indexOf(File.pathSeparator, off)) != -1) {
-            URL url = toFileURL(cp.substring(off, next));
-            if (url != null)
-                ucp.addURL(url);
-            off = next + 1;
-        }
-
-        // remaining
-        URL url = toFileURL(cp.substring(off));
-        if (url != null)
-            ucp.addURL(url);
-    }
-
-    /**
      * Attempts to convert the given string to a file URL.
      *
      * @apiNote This is called by the VM
      */
+    @Deprecated
     private static URL toFileURL(String s) {
         try {
             // Use an intermediate File object to construct a URI/URL without
@@ -265,5 +241,4 @@
             return null;
         }
     }
-
 }
--- a/jdk/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java	Thu Aug 24 16:34:30 2017 +0200
@@ -46,6 +46,7 @@
 import java.security.PrivilegedExceptionAction;
 import java.security.cert.Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -66,7 +67,6 @@
 import java.util.zip.ZipFile;
 
 import jdk.internal.misc.JavaNetURLAccess;
-import jdk.internal.misc.JavaNetURLClassLoaderAccess;
 import jdk.internal.misc.JavaUtilZipFileAccess;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.util.jar.InvalidJarIndexError;
@@ -100,19 +100,19 @@
     }
 
     /* The original search path of URLs. */
-    private ArrayList<URL> path = new ArrayList<>();
+    private final List<URL> path;
 
     /* The stack of unopened URLs */
-    Stack<URL> urls = new Stack<>();
+    private final Stack<URL> urls = new Stack<>();
 
     /* The resulting search path of Loaders */
-    ArrayList<Loader> loaders = new ArrayList<>();
+    private final ArrayList<Loader> loaders = new ArrayList<>();
 
     /* Map of each URL opened to its corresponding Loader */
-    HashMap<String, Loader> lmap = new HashMap<>();
+    private final HashMap<String, Loader> lmap = new HashMap<>();
 
     /* The jar protocol handler to use when creating new URLs */
-    private URLStreamHandler jarHandler;
+    private final URLStreamHandler jarHandler;
 
     /* Whether this URLClassLoader has been closed yet */
     private boolean closed = false;
@@ -137,12 +137,16 @@
     public URLClassPath(URL[] urls,
                         URLStreamHandlerFactory factory,
                         AccessControlContext acc) {
-        for (int i = 0; i < urls.length; i++) {
-            path.add(urls[i]);
+        List<URL> path = new ArrayList<>(urls.length);
+        for (URL url : urls) {
+            path.add(url);
         }
+        this.path = path;
         push(urls);
         if (factory != null) {
             jarHandler = factory.createURLStreamHandler("jar");
+        } else {
+            jarHandler = null;
         }
         if (DISABLE_ACC_CHECKING)
             this.acc = null;
@@ -150,16 +154,50 @@
             this.acc = acc;
     }
 
-    /**
-     * Constructs a URLClassPath with no additional security restrictions.
-     * Used by code that implements the class path.
-     */
-    public URLClassPath(URL[] urls) {
-        this(urls, null, null);
+    public URLClassPath(URL[] urls, AccessControlContext acc) {
+        this(urls, null, acc);
     }
 
-    public URLClassPath(URL[] urls, AccessControlContext acc) {
-        this(urls, null, acc);
+    /**
+     * Constructs a URLClassPath from a class path string.
+     *
+     * @param cp the class path string
+     * @param skipEmptyElements indicates if empty elements are ignored or
+     *        treated as the current working directory
+     *
+     * @apiNote Used to create the application class path.
+     */
+    URLClassPath(String cp, boolean skipEmptyElements) {
+        List<URL> path = new ArrayList<>();
+        if (cp != null) {
+            // map each element of class path to a file URL
+            int off = 0;
+            int next;
+            while ((next = cp.indexOf(File.pathSeparator, off)) != -1) {
+                String element = cp.substring(off, next);
+                if (element.length() > 0 || !skipEmptyElements) {
+                    URL url = toFileURL(element);
+                    if (url != null) path.add(url);
+                }
+                off = next + 1;
+            }
+
+            // remaining element
+            String element = cp.substring(off);
+            if (element.length() > 0 || !skipEmptyElements) {
+                URL url = toFileURL(element);
+                if (url != null) path.add(url);
+            }
+
+            // push the URLs
+            for (int i = path.size() - 1; i >= 0; --i) {
+                urls.push(path.get(i));
+            }
+        }
+
+        this.path = path;
+        this.jarHandler = null;
+        this.acc = null;
     }
 
     public synchronized List<IOException> closeLoaders() {
@@ -198,6 +236,28 @@
     }
 
     /**
+     * Appends the specified file path as a file URL to the search path.
+     */
+    public void addFile(String s) {
+        URL url = toFileURL(s);
+        if (url != null) {
+            addURL(url);
+        }
+    }
+
+    /**
+     * Returns a file URL for the given file path.
+     */
+    private static URL toFileURL(String s) {
+        try {
+            File f = new File(s).getCanonicalFile();
+            return ParseUtil.fileToEncodedURL(f);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
      * Returns the original search path of URLs.
      */
     public URL[] getURLs() {
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Thu Aug 24 16:34:30 2017 +0200
@@ -34,16 +34,10 @@
 import java.lang.module.ModuleDescriptor.Provides;
 import java.lang.module.ModuleDescriptor.Version;
 import java.lang.module.ModuleFinder;
-import java.lang.module.ModuleReader;
-import java.lang.module.ModuleReference;
-import java.net.URI;
-import java.nio.file.Path;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
-import java.util.function.Supplier;
-
-import jdk.internal.module.ModuleHashes;
 
 /**
  * Provides access to non-public methods in java.lang.module.
@@ -131,12 +125,16 @@
 
     /**
      * Resolves a collection of root modules, with service binding
-     * and the empty configuration as the parent. The post resolution
-     * checks are optionally run.
+     * and the empty configuration as the parent.
      */
     Configuration resolveAndBind(ModuleFinder finder,
                                  Collection<String> roots,
-                                 boolean check,
                                  PrintStream traceOutput);
 
+    /**
+     * Creates a configuration from a pre-generated readability graph.
+     */
+    Configuration newConfiguration(ModuleFinder finder,
+                                   Map<String, Set<String>> graph);
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/DefaultRoots.java	Thu Aug 24 16:34:30 2017 +0200
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.module;
+
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Defines methods to compute the default set of root modules for the unnamed
+ * module.
+ */
+
+public final class DefaultRoots {
+    private static final String JAVA_SE = "java.se";
+
+    private DefaultRoots() { }
+
+    /**
+     * Returns the default set of root modules for the unnamed module computed from
+     * the system modules observable with the given module finder.
+     */
+    static Set<String> compute(ModuleFinder systemModuleFinder, ModuleFinder finder) {
+        Set<String> roots = new HashSet<>();
+
+        boolean hasJava = false;
+        if (systemModuleFinder.find(JAVA_SE).isPresent()) {
+            if (finder == systemModuleFinder || finder.find(JAVA_SE).isPresent()) {
+                // java.se is a system module
+                hasJava = true;
+                roots.add(JAVA_SE);
+            }
+        }
+
+        for (ModuleReference mref : systemModuleFinder.findAll()) {
+            String mn = mref.descriptor().name();
+            if (hasJava && mn.startsWith("java.")) {
+                // not a root
+                continue;
+            }
+
+            if (ModuleResolution.doNotResolveByDefault(mref)) {
+                // not a root
+                continue;
+            }
+
+            if ((finder == systemModuleFinder || finder.find(mn).isPresent())) {
+                // add as root if exports at least one package to all modules
+                ModuleDescriptor descriptor = mref.descriptor();
+                for (ModuleDescriptor.Exports e : descriptor.exports()) {
+                    if (!e.isQualified()) {
+                        roots.add(mn);
+                        break;
+                    }
+                }
+            }
+        }
+
+        return roots;
+    }
+
+    /**
+     * Returns the default set of root modules for the unnamed module from the
+     * modules observable with the given module finder.
+     */
+    public static Set<String> compute(ModuleFinder finder) {
+        return compute(finder, finder);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ExplodedSystemModules.java	Thu Aug 24 16:34:30 2017 +0200
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.module;
+
+import java.lang.module.ModuleDescriptor;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A dummy SystemModules for use with exploded builds or testing.
+ */
+
+class ExplodedSystemModules implements SystemModules {
+    @Override
+    public boolean hasSplitPackages() {
+        return true;  // not known
+    }
+
+    @Override
+    public boolean hasIncubatorModules() {
+        return true;  // not known
+    }
+
+    @Override
+    public ModuleDescriptor[] moduleDescriptors() {
+        throw new InternalError();
+    }
+
+    @Override
+    public ModuleTarget[] moduleTargets() {
+        throw new InternalError();
+    }
+
+    @Override
+    public ModuleHashes[] moduleHashes() {
+        throw new InternalError();
+    }
+
+    @Override
+    public ModuleResolution[] moduleResolutions() {
+        throw new InternalError();
+    }
+
+    @Override
+    public Map<String, Set<String>> moduleReads() {
+        throw new InternalError();
+    }
+
+    @Override
+    public  Map<String, Set<String>> concealedPackagesToOpen() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public  Map<String, Set<String>> exportedPackagesToOpen() {
+        return Collections.emptyMap();
+    }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Thu Aug 24 16:34:30 2017 +0200
@@ -40,16 +40,20 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 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.JavaLangAccess;
+import jdk.internal.misc.JavaLangModuleAccess;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.perf.PerfCounter;
 
@@ -70,8 +74,6 @@
 
     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";
 
@@ -84,13 +86,13 @@
     // the token for "all modules on the module path"
     private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
 
+    // access to java.lang/module
+    private static final JavaLangModuleAccess JLMA
+        = SharedSecrets.getJavaLangModuleAccess();
+
     // The ModulePatcher for the initial configuration
     private static final ModulePatcher patcher = initModulePatcher();
 
-    // ModuleFinders for the initial configuration
-    private static ModuleFinder unlimitedFinder;
-    private static ModuleFinder limitedFinder;
-
     /**
      * Returns the ModulePatcher for the initial configuration.
      */
@@ -98,21 +100,38 @@
         return patcher;
     }
 
+    // ModuleFinders for the initial configuration
+    private static volatile ModuleFinder unlimitedFinder;
+    private static volatile ModuleFinder limitedFinder;
+
     /**
-     * Returns the ModuleFinder for the initial configuration before observability
-     * is limited by the --limit-modules command line option.
+     * Returns the ModuleFinder for the initial configuration before
+     * observability is limited by the --limit-modules command line option.
+     *
+     * @apiNote Used to support locating modules {@code java.instrument} and
+     * {@code jdk.management.agent} modules when they are loaded dynamically.
      */
     public static ModuleFinder unlimitedFinder() {
-        assert unlimitedFinder != null;
-        return unlimitedFinder;
+        ModuleFinder finder = unlimitedFinder;
+        if (finder == null) {
+            return ModuleFinder.ofSystem();
+        } else {
+            return finder;
+        }
     }
 
     /**
      * Returns the ModuleFinder for the initial configuration.
+     *
+     * @apiNote Used to support "{@code java --list-modules}".
      */
     public static ModuleFinder limitedFinder() {
-        assert limitedFinder != null;
-        return limitedFinder;
+        ModuleFinder finder = limitedFinder;
+        if (finder == null) {
+            return unlimitedFinder();
+        } else {
+            return finder;
+        }
     }
 
     /**
@@ -120,13 +139,60 @@
      *
      * @see java.lang.System#initPhase2()
      */
-    public static ModuleLayer boot() {
+    public static ModuleLayer boot() throws Exception {
+
+        // Step 0: Command line options
+
+        long t0 = System.nanoTime();
+
+        ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path");
+        ModuleFinder appModulePath = finderFor("jdk.module.path");
+        boolean isPatched = patcher.hasPatches();
 
-        // Step 1: Locate system modules (may be patched)
+        String mainModule = System.getProperty("jdk.module.main");
+        Set<String> addModules = addModules();
+        Set<String> limitModules = limitModules();
+
+        PrintStream traceOutput = null;
+        String trace = getAndRemoveProperty("jdk.module.showModuleResolution");
+        if (trace != null && Boolean.parseBoolean(trace))
+            traceOutput = System.out;
+
+
+        // Step 1: The observable system modules, either all system modules
+        // or the system modules pre-generated for the initial module (the
+        // initial module may be the unnamed module). If the system modules
+        // are pre-generated for the initial module then resolution can be
+        // skipped.
 
         long t1 = System.nanoTime();
-        ModuleFinder systemModules = ModuleFinder.ofSystem();
-        PerfCounters.systemModulesTime.addElapsedTimeFrom(t1);
+
+        SystemModules systemModules = null;
+        ModuleFinder systemModuleFinder;
+
+        boolean haveModulePath = (appModulePath != null || upgradeModulePath != null);
+        boolean needResolution = true;
+
+        if (!haveModulePath && addModules.isEmpty() && limitModules.isEmpty()) {
+            systemModules = SystemModuleFinders.systemModules(mainModule);
+            if (systemModules != null && !isPatched && (traceOutput == null)) {
+                needResolution = false;
+            }
+        }
+        if (systemModules == null) {
+            // all system modules are observable
+            systemModules = SystemModuleFinders.allSystemModules();
+        }
+        if (systemModules != null) {
+            // images build
+            systemModuleFinder = SystemModuleFinders.of(systemModules);
+        } else {
+            // exploded build or testing
+            systemModules = new ExplodedSystemModules();
+            systemModuleFinder = SystemModuleFinders.ofSystem();
+        }
+
+        Counters.add("jdk.module.boot.1.systemModulesTime", t1);
 
 
         // Step 2: Define and load java.base. This patches all classes loaded
@@ -136,7 +202,7 @@
 
         long t2 = System.nanoTime();
 
-        ModuleReference base = systemModules.find(JAVA_BASE).orElse(null);
+        ModuleReference base = systemModuleFinder.find(JAVA_BASE).orElse(null);
         if (base == null)
             throw new InternalError(JAVA_BASE + " not found");
         URI baseUri = base.location().orElse(null);
@@ -145,171 +211,138 @@
         BootLoader.loadModule(base);
         Modules.defineModule(null, base.descriptor(), baseUri);
 
-        PerfCounters.defineBaseTime.addElapsedTimeFrom(t2);
+        Counters.add("jdk.module.boot.2.defineBaseTime", t2);
 
 
         // 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) {
+        if (getAndRemoveProperty("jdk.module.minimumBoot") != null) {
             return createMinimalBootLayer();
         }
 
 
-        // 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.
+        // Step 3: If resolution is needed then create the module finder and
+        // the set of root modules to resolve.
 
         long t3 = System.nanoTime();
 
-        // --upgrade-module-path option specified to launcher
-        ModuleFinder upgradeModulePath
-            = createModulePathFinder("jdk.module.upgrade.path");
-        if (upgradeModulePath != null)
-            systemModules = ModuleFinder.compose(upgradeModulePath, systemModules);
+        ModuleFinder savedModuleFinder = null;
+        ModuleFinder finder;
+        Set<String> roots;
+        if (needResolution) {
 
-        // --module-path option specified to the launcher
-        ModuleFinder appModulePath = createModulePathFinder("jdk.module.path");
+            // upgraded modules override the modules in the run-time image
+            if (upgradeModulePath != null)
+                systemModuleFinder = ModuleFinder.compose(upgradeModulePath,
+                                                          systemModuleFinder);
 
-        // The module finder: [--upgrade-module-path] system [--module-path]
-        ModuleFinder finder = systemModules;
-        if (appModulePath != null)
-            finder = ModuleFinder.compose(finder, appModulePath);
+            // The module finder: [--upgrade-module-path] system [--module-path]
+            if (appModulePath != null) {
+                finder = ModuleFinder.compose(systemModuleFinder, appModulePath);
+            } else {
+                finder = systemModuleFinder;
+            }
 
-        // 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);
+            // The root modules to resolve
+            roots = new HashSet<>();
 
-        // additional module(s) specified by --add-modules
-        boolean addAllDefaultModules = false;
-        boolean addAllSystemModules = false;
-        boolean addAllApplicationModules = false;
-        for (String mod: getExtraAddModules()) {
-            switch (mod) {
-                case ALL_DEFAULT:
-                    addAllDefaultModules = true;
-                    break;
-                case ALL_SYSTEM:
-                    addAllSystemModules = true;
-                    break;
-                case ALL_MODULE_PATH:
-                    addAllApplicationModules = true;
-                    break;
-                default :
-                    roots.add(mod);
-            }
-        }
+            // launcher -m option to specify the main/initial module
+            if (mainModule != null)
+                roots.add(mainModule);
 
-        // --limit-modules
-        unlimitedFinder = finder;
-        propValue = getAndRemoveProperty("jdk.module.limitmods");
-        if (propValue != null) {
-            Set<String> mods = new HashSet<>();
-            for (String mod: propValue.split(",")) {
-                mods.add(mod);
-            }
-            finder = limitFinder(finder, mods, roots);
-        }
-        limitedFinder = finder;
-
-        // 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. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT
-        // bit set in their ModuleResolution attribute flags are excluded from
-        // the default set of roots.
-        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);
+            // additional module(s) specified by --add-modules
+            boolean addAllDefaultModules = false;
+            boolean addAllSystemModules = false;
+            boolean addAllApplicationModules = false;
+            for (String mod : addModules) {
+                switch (mod) {
+                    case ALL_DEFAULT:
+                        addAllDefaultModules = true;
+                        break;
+                    case ALL_SYSTEM:
+                        addAllSystemModules = true;
+                        break;
+                    case ALL_MODULE_PATH:
+                        addAllApplicationModules = true;
+                        break;
+                    default:
+                        roots.add(mod);
                 }
             }
 
-            for (ModuleReference mref : systemModules.findAll()) {
-                String mn = mref.descriptor().name();
-                if (hasJava && mn.startsWith("java."))
-                    continue;
+            // --limit-modules
+            savedModuleFinder = finder;
+            if (!limitModules.isEmpty()) {
+                finder = limitFinder(finder, limitModules, roots);
+            }
 
-                if (ModuleResolution.doNotResolveByDefault(mref))
-                    continue;
+            // 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. Modules that have the DO_NOT_RESOLVE_BY_DEFAULT
+            // bit set in their ModuleResolution attribute flags are excluded from
+            // the default set of roots.
+            if (mainModule == null || addAllDefaultModules) {
+                roots.addAll(DefaultRoots.compute(systemModuleFinder, finder));
+            }
 
-                // 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;
-                        }
-                    }
-                }
+            // If `--add-modules ALL-SYSTEM` is specified then all observable system
+            // modules will be resolved.
+            if (addAllSystemModules) {
+                ModuleFinder f = finder;  // observable modules
+                systemModuleFinder.findAll()
+                    .stream()
+                    .map(ModuleReference::descriptor)
+                    .map(ModuleDescriptor::name)
+                    .filter(mn -> f.find(mn).isPresent())  // observable
+                    .forEach(mn -> roots.add(mn));
             }
+
+            // If `--add-modules 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));
+            }
+        } else {
+            // no resolution case
+            finder = systemModuleFinder;
+            roots = null;
         }
 
-        // If `--add-modules 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 `--add-modules 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(t3);
-
+        Counters.add("jdk.module.boot.3.optionsAndRootsTime", t3);
 
         // Step 4: Resolve the root modules, with service binding, to create
-        // the configuration for the boot layer.
+        // the configuration for the boot layer. If resolution is not needed
+        // then create the configuration for the boot layer from the
+        // readability graph created at link time.
 
         long t4 = 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)
-                && (patcher.isEmpty())) {
-            needPostResolutionChecks = false;
+        Configuration cf;
+        if (needResolution) {
+            cf = JLMA.resolveAndBind(finder, roots, traceOutput);
+        } else {
+            Map<String, Set<String>> map = systemModules.moduleReads();
+            cf = JLMA.newConfiguration(systemModuleFinder, map);
         }
 
-        PrintStream traceOutput = null;
-        propValue = getAndRemoveProperty("jdk.module.showModuleResolution");
-        if (propValue != null && Boolean.parseBoolean(propValue))
-            traceOutput = System.out;
+        // check that modules specified to --patch-module are resolved
+        if (isPatched) {
+            patcher.patchedModules()
+                    .stream()
+                    .filter(mn -> !cf.findModule(mn).isPresent())
+                    .forEach(mn -> warnUnknownModule(PATCH_MODULE, mn));
+        }
 
-        // run the resolver to create the configuration
-        Configuration cf = SharedSecrets.getJavaLangModuleAccess()
-                .resolveAndBind(finder,
-                                roots,
-                                needPostResolutionChecks,
-                                traceOutput);
-
-        PerfCounters.resolveTime.addElapsedTimeFrom(t4);
+        Counters.add("jdk.module.boot.4.resolveTime", t4);
 
 
         // Step 5: Map the modules in the configuration to class loaders.
@@ -326,7 +359,7 @@
 
         // check that all modules to be mapped to the boot loader will be
         // loaded from the runtime image
-        if (needPostResolutionChecks) {
+        if (haveModulePath) {
             for (ResolvedModule resolvedModule : cf.modules()) {
                 ModuleReference mref = resolvedModule.reference();
                 String name = mref.descriptor().name();
@@ -335,51 +368,54 @@
                     if (upgradeModulePath != null
                             && upgradeModulePath.find(name).isPresent())
                         fail(name + ": cannot be loaded from upgrade module path");
-                    if (!systemModules.find(name).isPresent())
+                    if (!systemModuleFinder.find(name).isPresent())
                         fail(name + ": cannot be loaded from application module path");
                 }
             }
-
-            // check if module specified in --patch-module is present
-            for (String mn: patcher.patchedModules()) {
-                if (!cf.findModule(mn).isPresent()) {
-                    warnUnknownModule(PATCH_MODULE, mn);
-                }
-            }
         }
 
         // check for split packages in the modules mapped to the built-in loaders
-        if (SystemModules.hasSplitPackages() || needPostResolutionChecks) {
+        if (systemModules.hasSplitPackages() || isPatched || haveModulePath) {
             checkSplitPackages(cf, clf);
         }
 
         // load/register the modules with the built-in class loaders
         loadModules(cf, clf);
 
-        PerfCounters.loadModulesTime.addElapsedTimeFrom(t5);
+        Counters.add("jdk.module.boot.5.loadModulesTime", t5);
 
 
         // Step 6: Define all modules to the VM
 
         long t6 = System.nanoTime();
         ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf);
-        PerfCounters.layerCreateTime.addElapsedTimeFrom(t6);
+        Counters.add("jdk.module.boot.6.layerCreateTime", t6);
 
 
         // Step 7: Miscellaneous
 
         // check incubating status
-        checkIncubatingStatus(cf);
+        if (systemModules.hasIncubatorModules() || haveModulePath) {
+            checkIncubatingStatus(cf);
+        }
 
-        // --add-reads, --add-exports/--add-opens, and -illegal-access
+        // --add-reads, --add-exports/--add-opens, and --illegal-access
         long t7 = System.nanoTime();
         addExtraReads(bootLayer);
         boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer);
-        addIllegalAccess(bootLayer, upgradeModulePath, extraExportsOrOpens);
-        PerfCounters.adjustModulesTime.addElapsedTimeFrom(t7);
+        addIllegalAccess(upgradeModulePath, systemModules, bootLayer, extraExportsOrOpens);
+        Counters.add("jdk.module.boot.7.adjustModulesTime", t7);
+
+        // save module finders for later use
+        if (savedModuleFinder != null) {
+            unlimitedFinder = new SafeModuleFinder(savedModuleFinder);
+            if (savedModuleFinder != finder)
+                limitedFinder = new SafeModuleFinder(finder);
+        }
 
         // total time to initialize
-        PerfCounters.bootstrapTime.addElapsedTimeFrom(t1);
+        Counters.add("jdk.module.boot.totalTime", t0);
+        Counters.publish();
 
         return bootLayer;
     }
@@ -391,7 +427,6 @@
         Configuration cf = SharedSecrets.getJavaLangModuleAccess()
             .resolveAndBind(ModuleFinder.ofSystem(),
                             Set.of(JAVA_BASE),
-                            false,
                             null);
 
         Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
@@ -439,7 +474,6 @@
                     }
                 }
             }
-
         }
     }
 
@@ -489,7 +523,7 @@
      * Creates a finder from the module path that is the value of the given
      * system property and optionally patched by --patch-module
      */
-    private static ModuleFinder createModulePathFinder(String prop) {
+    private static ModuleFinder finderFor(String prop) {
         String s = System.getProperty(prop);
         if (s == null) {
             return null;
@@ -510,35 +544,48 @@
      */
     private static ModulePatcher initModulePatcher() {
         Map<String, List<String>> map = decode("jdk.module.patch.",
-                File.pathSeparator,
-                false);
+                                               File.pathSeparator,
+                                               false);
         return new ModulePatcher(map);
     }
 
     /**
-     * Returns the set of module names specified via --add-modules options
-     * on the command line
+     * Returns the set of module names specified by --add-module options.
      */
-    private static Set<String> getExtraAddModules() {
+    private static Set<String> addModules() {
         String prefix = "jdk.module.addmods.";
         int index = 0;
-
         // the system property is removed after decoding
         String value = getAndRemoveProperty(prefix + index);
         if (value == null) {
             return Collections.emptySet();
+        } else {
+            Set<String> modules = new HashSet<>();
+            while (value != null) {
+                for (String s : value.split(",")) {
+                    if (s.length() > 0) modules.add(s);
+                }
+                index++;
+                value = getAndRemoveProperty(prefix + index);
+            }
+            return modules;
         }
+    }
 
-        Set<String> modules = new HashSet<>();
-        while (value != null) {
-            for (String s : value.split(",")) {
-                if (s.length() > 0) modules.add(s);
+    /**
+     * Returns the set of module names specified by --limit-modules.
+     */
+    private static Set<String> limitModules() {
+        String value = getAndRemoveProperty("jdk.module.limitmods");
+        if (value == null) {
+            return Collections.emptySet();
+        } else {
+            Set<String> names = new HashSet<>();
+            for (String name : value.split(",")) {
+                if (name.length() > 0) names.add(name);
             }
-            index++;
-            value = getAndRemoveProperty(prefix + index);
+            return names;
         }
-
-        return modules;
     }
 
     /**
@@ -676,8 +723,9 @@
      * 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,
+    private static void addIllegalAccess(ModuleFinder upgradeModulePath,
+                                         SystemModules systemModules,
+                                         ModuleLayer bootLayer,
                                          boolean extraExportsOrOpens) {
         String value = getAndRemoveProperty("jdk.module.illegalAccess");
         IllegalAccessLogger.Mode mode = IllegalAccessLogger.Mode.ONESHOT;
@@ -702,10 +750,10 @@
         IllegalAccessLogger.Builder builder
             = new IllegalAccessLogger.Builder(mode, System.err);
 
-        Map<String, Set<String>> map1 = SystemModules.concealedPackagesToOpen();
-        Map<String, Set<String>> map2 = SystemModules.exportedPackagesToOpen();
+        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
+            // need to generate (exploded build)
             IllegalAccessMaps maps = IllegalAccessMaps.generate(limitedFinder());
             map1 = maps.concealedPackagesToOpen();
             map2 = maps.exportedPackagesToOpen();
@@ -906,6 +954,10 @@
         }
     }
 
+    /**
+     * Returns an iterator that yields all elements of the first iterator
+     * followed by all the elements of the second iterator.
+     */
     static <T> Iterator<T> concat(Iterator<T> iterator1, Iterator<T> iterator2) {
         return new Iterator<T>() {
             @Override
@@ -921,23 +973,76 @@
         };
     }
 
-    static class PerfCounters {
+    /**
+     * Wraps a (potentially not thread safe) ModuleFinder created during startup
+     * for use after startup.
+     */
+    static class SafeModuleFinder implements ModuleFinder {
+        private final Set<ModuleReference> mrefs;
+        private volatile Map<String, ModuleReference> nameToModule;
+
+        SafeModuleFinder(ModuleFinder finder) {
+            this.mrefs = Collections.unmodifiableSet(finder.findAll());
+        }
+        @Override
+        public Optional<ModuleReference> find(String name) {
+            Objects.requireNonNull(name);
+            Map<String, ModuleReference> nameToModule = this.nameToModule;
+            if (nameToModule == null) {
+                this.nameToModule = nameToModule = mrefs.stream()
+                        .collect(Collectors.toMap(m -> m.descriptor().name(),
+                                                  Function.identity()));
+            }
+            return Optional.ofNullable(nameToModule.get(name));
+        }
+        @Override
+        public Set<ModuleReference> findAll() {
+            return mrefs;
+        }
+    }
 
-        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
-            = 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");
+    /**
+     * Counters for startup performance analysis.
+     */
+    static class Counters {
+        private static final boolean PUBLISH_COUNTERS;
+        private static final boolean PRINT_COUNTERS;
+        private static Map<String, Long> counters;
+        static {
+            String s = System.getProperty("jdk.module.boot.usePerfData");
+            if (s == null) {
+                PUBLISH_COUNTERS = false;
+                PRINT_COUNTERS = false;
+            } else {
+                PUBLISH_COUNTERS = true;
+                PRINT_COUNTERS = s.equals("debug");
+                counters = new LinkedHashMap<>();  // preserve insert order
+            }
+        }
+
+        /**
+         * Add a counter
+         */
+        static void add(String name, long start) {
+            if (PUBLISH_COUNTERS || PRINT_COUNTERS) {
+                counters.put(name, (System.nanoTime() - start));
+            }
+        }
+
+        /**
+         * Publish the counters to the instrumentation buffer or stdout.
+         */
+        static void publish() {
+            if (PUBLISH_COUNTERS || PRINT_COUNTERS) {
+                for (Map.Entry<String, Long> e : counters.entrySet()) {
+                    String name = e.getKey();
+                    long value = e.getValue();
+                    if (PUBLISH_COUNTERS)
+                        PerfCounter.newPerfCounter(name).set(value);
+                    if (PRINT_COUNTERS)
+                        System.out.println(name + " = " + value);
+                }
+            }
+        }
     }
 }
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Thu Aug 24 16:34:30 2017 +0200
@@ -200,10 +200,10 @@
     }
 
     /**
-     * Returns true is this module patcher has no patches.
+     * Returns true is this module patcher has patches.
      */
-    public boolean isEmpty() {
-        return map.isEmpty();
+    public boolean hasPatches() {
+        return !map.isEmpty();
     }
 
     /*
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferenceImpl.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferenceImpl.java	Thu Aug 24 16:34:30 2017 +0200
@@ -68,14 +68,14 @@
     /**
      * Constructs a new instance of this class.
      */
-    ModuleReferenceImpl(ModuleDescriptor descriptor,
-                        URI location,
-                        Supplier<ModuleReader> readerSupplier,
-                        ModulePatcher patcher,
-                        ModuleTarget target,
-                        ModuleHashes recordedHashes,
-                        ModuleHashes.HashSupplier hasher,
-                        ModuleResolution moduleResolution)
+    public ModuleReferenceImpl(ModuleDescriptor descriptor,
+                               URI location,
+                               Supplier<ModuleReader> readerSupplier,
+                               ModulePatcher patcher,
+                               ModuleTarget target,
+                               ModuleHashes recordedHashes,
+                               ModuleHashes.HashSupplier hasher,
+                               ModuleResolution moduleResolution)
     {
         super(descriptor, Objects.requireNonNull(location));
         this.location = location;
--- a/jdk/src/java.base/share/classes/jdk/internal/module/SystemModuleFinder.java	Fri Aug 04 23:29:04 2017 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,469 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.internal.module;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
-import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleFinder;
-import java.lang.module.ModuleReader;
-import java.lang.module.ModuleReference;
-import java.net.URI;
-import java.net.URLConnection;
-import java.nio.ByteBuffer;
-import java.util.ArrayDeque;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-import jdk.internal.jimage.ImageLocation;
-import jdk.internal.jimage.ImageReader;
-import jdk.internal.jimage.ImageReaderFactory;
-import jdk.internal.misc.JavaNetUriAccess;
-import jdk.internal.misc.SharedSecrets;
-import jdk.internal.module.ModuleHashes.HashSupplier;
-import jdk.internal.perf.PerfCounter;
-
-/**
- * A {@code ModuleFinder} that finds modules that are linked into the
- * run-time image.
- *
- * The modules linked into the run-time image are assumed to have the
- * Packages attribute.
- */
-
-public class SystemModuleFinder implements ModuleFinder {
-
-    private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();
-
-    private static final PerfCounter initTime
-        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.initTime");
-    private static final PerfCounter moduleCount
-        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.modules");
-    private static final PerfCounter packageCount
-        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.packages");
-    private static final PerfCounter exportsCount
-        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.exports");
-
-    // singleton finder to find modules in the run-time images
-    private static final SystemModuleFinder INSTANCE;
-
-    public static SystemModuleFinder getInstance() {
-        return INSTANCE;
-    }
-
-    /**
-     * For now, the module references are created eagerly on the assumption
-     * that service binding will require all modules to be located.
-     */
-    static {
-        long t0 = System.nanoTime();
-
-        INSTANCE = new SystemModuleFinder();
-
-        initTime.addElapsedTimeFrom(t0);
-    }
-
-    /**
-     * Holder class for the ImageReader
-     */
-    private static class SystemImage {
-        static final ImageReader READER;
-        static {
-            long t0 = System.nanoTime();
-            READER = ImageReaderFactory.getImageReader();
-            initTime.addElapsedTimeFrom(t0);
-        }
-
-        static ImageReader reader() {
-            return READER;
-        }
-    }
-
-    private static boolean isFastPathSupported() {
-       return SystemModules.MODULE_NAMES.length > 0;
-    }
-
-    private static String[] moduleNames() {
-        if (isFastPathSupported())
-            // module names recorded at link time
-            return SystemModules.MODULE_NAMES;
-
-        // this happens when java.base is patched with java.base
-        // from an exploded image
-        return SystemImage.reader().getModuleNames();
-    }
-
-    // the set of modules in the run-time image
-    private final Set<ModuleReference> modules;
-
-    // maps module name to module reference
-    private final Map<String, ModuleReference> nameToModule;
-
-    // module name to hashes
-    private final Map<String, byte[]> hashes;
-
-    private SystemModuleFinder() {
-        String[] names = moduleNames();
-        int n = names.length;
-        moduleCount.add(n);
-
-        // fastpath is enabled by default.
-        // It can be disabled for troubleshooting purpose.
-        boolean disabled =
-            System.getProperty("jdk.system.module.finder.disabledFastPath") != null;
-
-        ModuleDescriptor[] descriptors;
-        ModuleTarget[] targets;
-        ModuleHashes[] recordedHashes;
-        ModuleResolution[] moduleResolutions;
-
-        // fast loading of ModuleDescriptor of system modules
-        if (isFastPathSupported() && !disabled) {
-            descriptors = SystemModules.descriptors();
-            targets = SystemModules.targets();
-            recordedHashes = SystemModules.hashes();
-            moduleResolutions = SystemModules.moduleResolutions();
-        } else {
-            // if fast loading of ModuleDescriptors is disabled
-            // fallback to read module-info.class
-            descriptors = new ModuleDescriptor[n];
-            targets = new ModuleTarget[n];
-            recordedHashes = new ModuleHashes[n];
-            moduleResolutions = new ModuleResolution[n];
-            ImageReader imageReader = SystemImage.reader();
-            for (int i = 0; i < names.length; i++) {
-                String mn = names[i];
-                ImageLocation loc = imageReader.findLocation(mn, "module-info.class");
-                ModuleInfo.Attributes attrs =
-                    ModuleInfo.read(imageReader.getResourceBuffer(loc), null);
-                descriptors[i] = attrs.descriptor();
-                targets[i] = attrs.target();
-                recordedHashes[i] = attrs.recordedHashes();
-                moduleResolutions[i] = attrs.moduleResolution();
-            }
-        }
-
-        Map<String, byte[]> hashes = null;
-        boolean secondSeen = false;
-        // record the hashes to build HashSupplier
-        for (ModuleHashes mh : recordedHashes) {
-            if (mh != null) {
-                // if only one module contain ModuleHashes, use it
-                if (hashes == null) {
-                    hashes = mh.hashes();
-                } else {
-                    if (!secondSeen) {
-                        hashes = new HashMap<>(hashes);
-                        secondSeen = true;
-                    }
-                    hashes.putAll(mh.hashes());
-                }
-            }
-        }
-        this.hashes = (hashes == null) ? Map.of() : hashes;
-
-        ModuleReference[] mods = new ModuleReference[n];
-
-        @SuppressWarnings(value = {"rawtypes", "unchecked"})
-        Entry<String, ModuleReference>[] map
-            = (Entry<String, ModuleReference>[])new Entry[n];
-
-        for (int i = 0; i < n; i++) {
-            ModuleDescriptor md = descriptors[i];
-
-            // create the ModuleReference
-            ModuleReference mref = toModuleReference(md,
-                                                     targets[i],
-                                                     recordedHashes[i],
-                                                     hashSupplier(names[i]),
-                                                     moduleResolutions[i]);
-            mods[i] = mref;
-            map[i] = Map.entry(names[i], mref);
-
-            // counters
-            packageCount.add(md.packages().size());
-            exportsCount.add(md.exports().size());
-        }
-
-        modules = Set.of(mods);
-        nameToModule = Map.ofEntries(map);
-    }
-
-    @Override
-    public Optional<ModuleReference> find(String name) {
-        Objects.requireNonNull(name);
-        return Optional.ofNullable(nameToModule.get(name));
-    }
-
-    @Override
-    public Set<ModuleReference> findAll() {
-        return modules;
-    }
-
-    private ModuleReference toModuleReference(ModuleDescriptor md,
-                                              ModuleTarget target,
-                                              ModuleHashes recordedHashes,
-                                              HashSupplier hasher,
-                                              ModuleResolution mres) {
-        String mn = md.name();
-        URI uri = JNUA.create("jrt", "/".concat(mn));
-
-        Supplier<ModuleReader> readerSupplier = new Supplier<>() {
-            @Override
-            public ModuleReader get() {
-                return new ImageModuleReader(mn, uri);
-            }
-        };
-
-        ModuleReference mref = new ModuleReferenceImpl(md,
-                                                       uri,
-                                                       readerSupplier,
-                                                       null,
-                                                       target,
-                                                       recordedHashes,
-                                                       hasher,
-                                                       mres);
-
-        // may need a reference to a patched module if --patch-module specified
-        mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
-
-        return mref;
-    }
-
-    private HashSupplier hashSupplier(String name) {
-        if (!hashes.containsKey(name))
-            return null;
-
-        return new HashSupplier() {
-            @Override
-            public byte[] generate(String algorithm) {
-                return hashes.get(name);
-            }
-        };
-    }
-
-    /**
-     * A ModuleReader for reading resources from a module linked into the
-     * run-time image.
-     */
-    static class ImageModuleReader implements ModuleReader {
-        private final String module;
-        private volatile boolean closed;
-
-        /**
-         * If there is a security manager set then check permission to
-         * connect to the run-time image.
-         */
-        private static void checkPermissionToConnect(URI uri) {
-            SecurityManager sm = System.getSecurityManager();
-            if (sm != null) {
-                try {
-                    URLConnection uc = uri.toURL().openConnection();
-                    sm.checkPermission(uc.getPermission());
-                } catch (IOException ioe) {
-                    throw new UncheckedIOException(ioe);
-                }
-            }
-        }
-
-        ImageModuleReader(String module, URI uri) {
-            checkPermissionToConnect(uri);
-            this.module = module;
-        }
-
-        /**
-         * Returns the ImageLocation for the given resource, {@code null}
-         * if not found.
-         */
-        private ImageLocation findImageLocation(String name) throws IOException {
-            Objects.requireNonNull(name);
-            if (closed)
-                throw new IOException("ModuleReader is closed");
-            ImageReader imageReader = SystemImage.reader();
-            if (imageReader != null) {
-                return imageReader.findLocation(module, name);
-            } else {
-                // not an images build
-                return null;
-            }
-        }
-
-        @Override
-        public Optional<URI> find(String name) throws IOException {
-            ImageLocation location = findImageLocation(name);
-            if (location != null) {
-                URI u = URI.create("jrt:/" + module + "/" + name);
-                return Optional.of(u);
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        public Optional<InputStream> open(String name) throws IOException {
-            return read(name).map(this::toInputStream);
-        }
-
-        private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
-            try {
-                int rem = bb.remaining();
-                byte[] bytes = new byte[rem];
-                bb.get(bytes);
-                return new ByteArrayInputStream(bytes);
-            } finally {
-                release(bb);
-            }
-        }
-
-        @Override
-        public Optional<ByteBuffer> read(String name) throws IOException {
-            ImageLocation location = findImageLocation(name);
-            if (location != null) {
-                return Optional.of(SystemImage.reader().getResourceBuffer(location));
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        public void release(ByteBuffer bb) {
-            Objects.requireNonNull(bb);
-            ImageReader.releaseByteBuffer(bb);
-        }
-
-        @Override
-        public Stream<String> list() throws IOException {
-            if (closed)
-                throw new IOException("ModuleReader is closed");
-
-            Spliterator<String> s = new ModuleContentSpliterator(module);
-            return StreamSupport.stream(s, false);
-        }
-
-        @Override
-        public void close() {
-            // nothing else to do
-            closed = true;
-        }
-    }
-
-    /**
-     * A Spliterator for traversing the resources of a module linked into the
-     * run-time image.
-     */
-    static class ModuleContentSpliterator implements Spliterator<String> {
-        final String moduleRoot;
-        final Deque<ImageReader.Node> stack;
-        Iterator<ImageReader.Node> iterator;
-
-        ModuleContentSpliterator(String module) throws IOException {
-            moduleRoot = "/modules/" + module;
-            stack = new ArrayDeque<>();
-
-            // push the root node to the stack to get started
-            ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot);
-            if (dir == null || !dir.isDirectory())
-                throw new IOException(moduleRoot + " not a directory");
-            stack.push(dir);
-            iterator = Collections.emptyIterator();
-        }
-
-        /**
-         * Returns the name of the next non-directory node or {@code null} if
-         * there are no remaining nodes to visit.
-         */
-        private String next() throws IOException {
-            for (;;) {
-                while (iterator.hasNext()) {
-                    ImageReader.Node node = iterator.next();
-                    String name = node.getName();
-                    if (node.isDirectory()) {
-                        // build node
-                        ImageReader.Node dir = SystemImage.reader().findNode(name);
-                        assert dir.isDirectory();
-                        stack.push(dir);
-                    } else {
-                        // strip /modules/$MODULE/ prefix
-                        return name.substring(moduleRoot.length() + 1);
-                    }
-                }
-
-                if (stack.isEmpty()) {
-                    return null;
-                } else {
-                    ImageReader.Node dir = stack.poll();
-                    assert dir.isDirectory();
-                    iterator = dir.getChildren().iterator();
-                }
-            }
-        }
-
-        @Override
-        public boolean tryAdvance(Consumer<? super String> action) {
-            String next;
-            try {
-                next = next();
-            } catch (IOException ioe) {
-                throw new UncheckedIOException(ioe);
-            }
-            if (next != null) {
-                action.accept(next);
-                return true;
-            } else {
-                return false;
-            }
-        }
-
-        @Override
-        public Spliterator<String> trySplit() {
-            return null;
-        }
-
-        @Override
-        public int characteristics() {
-            return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE;
-        }
-
-        @Override
-        public long estimateSize() {
-            return Long.MAX_VALUE;
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java	Thu Aug 24 16:34:30 2017 +0200
@@ -0,0 +1,576 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.module;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.lang.reflect.Constructor;
+import java.net.URI;
+import java.net.URLConnection;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import jdk.internal.jimage.ImageLocation;
+import jdk.internal.jimage.ImageReader;
+import jdk.internal.jimage.ImageReaderFactory;
+import jdk.internal.misc.JavaNetUriAccess;
+import jdk.internal.misc.SharedSecrets;
+import jdk.internal.module.ModuleHashes.HashSupplier;
+
+/**
+ * The factory for SystemModules objects and for creating ModuleFinder objects
+ * that find modules in the runtime image.
+ *
+ * This class supports initializing the module system when the runtime is an
+ * images build, an exploded build, or an images build with java.base patched
+ * by an exploded java.base. It also supports a testing mode that re-parses
+ * the module-info.class resources in the run-time image.
+ */
+
+public final class SystemModuleFinders {
+    private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();
+
+    private static final boolean USE_FAST_PATH;
+    static {
+        String value = System.getProperty("jdk.system.module.finder.disableFastPath");
+        if (value == null) {
+            USE_FAST_PATH = true;
+        } else {
+            USE_FAST_PATH = (value.length() > 0) && !Boolean.parseBoolean(value);
+        }
+    }
+
+    // cached ModuleFinder returned from ofSystem
+    private static volatile ModuleFinder cachedSystemModuleFinder;
+
+    private SystemModuleFinders() { }
+
+    /**
+     * Returns the SystemModules object to reconstitute all modules. Returns
+     * null if this is an exploded build or java.base is patched by an exploded
+     * build.
+     */
+    static SystemModules allSystemModules() {
+        if (USE_FAST_PATH) {
+            return SystemModulesMap.allSystemModules();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns a SystemModules object to reconstitute the modules for the
+     * given initial module. If the initial module is null then return the
+     * SystemModules object to reconstitute the default modules.
+     *
+     * Return null if there is no SystemModules class for the initial module,
+     * this is an exploded build, or java.base is patched by an exploded build.
+     */
+    static SystemModules systemModules(String initialModule) {
+        if (USE_FAST_PATH) {
+            if (initialModule == null) {
+                return SystemModulesMap.defaultSystemModules();
+            }
+
+            String[] initialModules = SystemModulesMap.moduleNames();
+            for (int i = 0; i < initialModules.length; i++) {
+                String moduleName = initialModules[i];
+                if (initialModule.equals(moduleName)) {
+                    String cn = SystemModulesMap.classNames()[i];
+                    try {
+                        // one-arg Class.forName as java.base may not be defined
+                        Constructor<?> ctor = Class.forName(cn).getConstructor();
+                        return (SystemModules) ctor.newInstance();
+                    } catch (Exception e) {
+                        throw new InternalError(e);
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a ModuleFinder that is backed by the given SystemModules object.
+     *
+     * @apiNote The returned ModuleFinder is thread safe.
+     */
+    static ModuleFinder of(SystemModules systemModules) {
+        ModuleDescriptor[] descriptors = systemModules.moduleDescriptors();
+        ModuleTarget[] targets = systemModules.moduleTargets();
+        ModuleHashes[] recordedHashes = systemModules.moduleHashes();
+        ModuleResolution[] moduleResolutions = systemModules.moduleResolutions();
+
+        int moduleCount = descriptors.length;
+        ModuleReference[] mrefs = new ModuleReference[moduleCount];
+        @SuppressWarnings(value = {"rawtypes", "unchecked"})
+        Map.Entry<String, ModuleReference>[] map
+            = (Map.Entry<String, ModuleReference>[])new Map.Entry[moduleCount];
+
+        Map<String, byte[]> nameToHash = generateNameToHash(recordedHashes);
+
+        for (int i = 0; i < moduleCount; i++) {
+            String name = descriptors[i].name();
+            HashSupplier hashSupplier = hashSupplier(nameToHash, name);
+            ModuleReference mref = toModuleReference(descriptors[i],
+                                                     targets[i],
+                                                     recordedHashes[i],
+                                                     hashSupplier,
+                                                     moduleResolutions[i]);
+            mrefs[i] = mref;
+            map[i] = Map.entry(name, mref);
+        }
+
+        return new SystemModuleFinder(mrefs, map);
+    }
+
+    /**
+     * Returns the ModuleFinder to find all system modules. Supports both
+     * images and exploded builds.
+     *
+     * @apiNote Used by ModuleFinder.ofSystem()
+     */
+    public static ModuleFinder ofSystem() {
+        ModuleFinder finder = cachedSystemModuleFinder;
+        if (finder != null) {
+            return finder;
+        }
+
+        // probe to see if this is an images build
+        String home = System.getProperty("java.home");
+        Path modules = Paths.get(home, "lib", "modules");
+        if (Files.isRegularFile(modules)) {
+            if (USE_FAST_PATH) {
+                SystemModules systemModules = allSystemModules();
+                if (systemModules != null) {
+                    finder = of(systemModules);
+                }
+            }
+
+            // fall back to parsing the module-info.class files in image
+            if (finder == null) {
+                finder = ofModuleInfos();
+            }
+
+            cachedSystemModuleFinder = finder;
+            return finder;
+
+        }
+
+        // exploded build (do not cache module finder)
+        Path dir = Paths.get(home, "modules");
+        if (!Files.isDirectory(dir))
+            throw new InternalError("Unable to detect the run-time image");
+        ModuleFinder f = ModulePath.of(ModuleBootstrap.patcher(), dir);
+        return new ModuleFinder() {
+            @Override
+            public Optional<ModuleReference> find(String name) {
+                PrivilegedAction<Optional<ModuleReference>> pa = () -> f.find(name);
+                return AccessController.doPrivileged(pa);
+            }
+            @Override
+            public Set<ModuleReference> findAll() {
+                PrivilegedAction<Set<ModuleReference>> pa = f::findAll;
+                return AccessController.doPrivileged(pa);
+            }
+        };
+    }
+
+    /**
+     * Parses the module-info.class of all module in the runtime image and
+     * returns a ModuleFinder to find the modules.
+     *
+     * @apiNote The returned ModuleFinder is thread safe.
+     */
+    private static ModuleFinder ofModuleInfos() {
+        // parse the module-info.class in every module
+        Map<String, ModuleInfo.Attributes> nameToAttributes = new HashMap<>();
+        Map<String, byte[]> nameToHash = new HashMap<>();
+        ImageReader reader = SystemImage.reader();
+        for (String mn : reader.getModuleNames()) {
+            ImageLocation loc = reader.findLocation(mn, "module-info.class");
+            ModuleInfo.Attributes attrs
+                = ModuleInfo.read(reader.getResourceBuffer(loc), null);
+
+            nameToAttributes.put(mn, attrs);
+            ModuleHashes hashes = attrs.recordedHashes();
+            if (hashes != null) {
+                for (String name : hashes.names()) {
+                    nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name));
+                }
+            }
+        }
+
+        // create a ModuleReference for each module
+        Set<ModuleReference> mrefs = new HashSet<>();
+        Map<String, ModuleReference> nameToModule = new HashMap<>();
+        for (Map.Entry<String, ModuleInfo.Attributes> e : nameToAttributes.entrySet()) {
+            String mn = e.getKey();
+            ModuleInfo.Attributes attrs = e.getValue();
+            HashSupplier hashSupplier = hashSupplier(nameToHash, mn);
+            ModuleReference mref = toModuleReference(attrs.descriptor(),
+                                                     attrs.target(),
+                                                     attrs.recordedHashes(),
+                                                     hashSupplier,
+                                                     attrs.moduleResolution());
+            mrefs.add(mref);
+            nameToModule.put(mn, mref);
+        }
+
+        return new SystemModuleFinder(mrefs, nameToModule);
+    }
+
+    /**
+     * A ModuleFinder that finds module in an array or set of modules.
+     */
+    private static class SystemModuleFinder implements ModuleFinder {
+        final Set<ModuleReference> mrefs;
+        final Map<String, ModuleReference> nameToModule;
+
+        SystemModuleFinder(ModuleReference[] array,
+                           Map.Entry<String, ModuleReference>[] map) {
+            this.mrefs = Set.of(array);
+            this.nameToModule = Map.ofEntries(map);
+        }
+
+        SystemModuleFinder(Set<ModuleReference> mrefs,
+                           Map<String, ModuleReference> nameToModule) {
+            this.mrefs = Collections.unmodifiableSet(mrefs);
+            this.nameToModule = Collections.unmodifiableMap(nameToModule);
+        }
+
+        @Override
+        public Optional<ModuleReference> find(String name) {
+            Objects.requireNonNull(name);
+            return Optional.ofNullable(nameToModule.get(name));
+        }
+
+        @Override
+        public Set<ModuleReference> findAll() {
+            return mrefs;
+        }
+    }
+
+    /**
+     * Creates a ModuleReference to the system module.
+     */
+    static ModuleReference toModuleReference(ModuleDescriptor descriptor,
+                                             ModuleTarget target,
+                                             ModuleHashes recordedHashes,
+                                             HashSupplier hasher,
+                                             ModuleResolution mres) {
+        String mn = descriptor.name();
+        URI uri = JNUA.create("jrt", "/".concat(mn));
+
+        Supplier<ModuleReader> readerSupplier = new Supplier<>() {
+            @Override
+            public ModuleReader get() {
+                return new SystemModuleReader(mn, uri);
+            }
+        };
+
+        ModuleReference mref = new ModuleReferenceImpl(descriptor,
+                                                       uri,
+                                                       readerSupplier,
+                                                       null,
+                                                       target,
+                                                       recordedHashes,
+                                                       hasher,
+                                                       mres);
+
+        // may need a reference to a patched module if --patch-module specified
+        mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
+
+        return mref;
+    }
+
+    /**
+     * Generates a map of module name to hash value.
+     */
+    static Map<String, byte[]> generateNameToHash(ModuleHashes[] recordedHashes) {
+        Map<String, byte[]> nameToHash = null;
+
+        boolean secondSeen = false;
+        // record the hashes to build HashSupplier
+        for (ModuleHashes mh : recordedHashes) {
+            if (mh != null) {
+                // if only one module contain ModuleHashes, use it
+                if (nameToHash == null) {
+                    nameToHash = mh.hashes();
+                } else {
+                    if (!secondSeen) {
+                        nameToHash = new HashMap<>(nameToHash);
+                        secondSeen = true;
+                    }
+                    nameToHash.putAll(mh.hashes());
+                }
+            }
+        }
+        return (nameToHash != null) ? nameToHash : Collections.emptyMap();
+    }
+
+    /**
+     * Returns a HashSupplier that returns the hash of the given module.
+     */
+    static HashSupplier hashSupplier(Map<String, byte[]> nameToHash, String name) {
+        byte[] hash = nameToHash.get(name);
+        if (hash != null) {
+            // avoid lambda here
+            return new HashSupplier() {
+                @Override
+                public byte[] generate(String algorithm) {
+                    return hash;
+                }
+            };
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Holder class for the ImageReader
+     *
+     * @apiNote This class must be loaded before a security manager is set.
+     */
+    private static class SystemImage {
+        static final ImageReader READER = ImageReaderFactory.getImageReader();
+        static ImageReader reader() {
+            return READER;
+        }
+    }
+
+    /**
+     * A ModuleReader for reading resources from a module linked into the
+     * run-time image.
+     */
+    private static class SystemModuleReader implements ModuleReader {
+        private final String module;
+        private volatile boolean closed;
+
+        /**
+         * If there is a security manager set then check permission to
+         * connect to the run-time image.
+         */
+        private static void checkPermissionToConnect(URI uri) {
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                try {
+                    URLConnection uc = uri.toURL().openConnection();
+                    sm.checkPermission(uc.getPermission());
+                } catch (IOException ioe) {
+                    throw new UncheckedIOException(ioe);
+                }
+            }
+        }
+
+        SystemModuleReader(String module, URI uri) {
+            checkPermissionToConnect(uri);
+            this.module = module;
+        }
+
+        /**
+         * Returns the ImageLocation for the given resource, {@code null}
+         * if not found.
+         */
+        private ImageLocation findImageLocation(String name) throws IOException {
+            Objects.requireNonNull(name);
+            if (closed)
+                throw new IOException("ModuleReader is closed");
+            ImageReader imageReader = SystemImage.reader();
+            if (imageReader != null) {
+                return imageReader.findLocation(module, name);
+            } else {
+                // not an images build
+                return null;
+            }
+        }
+
+        @Override
+        public Optional<URI> find(String name) throws IOException {
+            ImageLocation location = findImageLocation(name);
+            if (location != null) {
+                URI u = URI.create("jrt:/" + module + "/" + name);
+                return Optional.of(u);
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        public Optional<InputStream> open(String name) throws IOException {
+            return read(name).map(this::toInputStream);
+        }
+
+        private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
+            try {
+                int rem = bb.remaining();
+                byte[] bytes = new byte[rem];
+                bb.get(bytes);
+                return new ByteArrayInputStream(bytes);
+            } finally {
+                release(bb);
+            }
+        }
+
+        @Override
+        public Optional<ByteBuffer> read(String name) throws IOException {
+            ImageLocation location = findImageLocation(name);
+            if (location != null) {
+                return Optional.of(SystemImage.reader().getResourceBuffer(location));
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        public void release(ByteBuffer bb) {
+            Objects.requireNonNull(bb);
+            ImageReader.releaseByteBuffer(bb);
+        }
+
+        @Override
+        public Stream<String> list() throws IOException {
+            if (closed)
+                throw new IOException("ModuleReader is closed");
+
+            Spliterator<String> s = new ModuleContentSpliterator(module);
+            return StreamSupport.stream(s, false);
+        }
+
+        @Override
+        public void close() {
+            // nothing else to do
+            closed = true;
+        }
+    }
+
+    /**
+     * A Spliterator for traversing the resources of a module linked into the
+     * run-time image.
+     */
+    private static class ModuleContentSpliterator implements Spliterator<String> {
+        final String moduleRoot;
+        final Deque<ImageReader.Node> stack;
+        Iterator<ImageReader.Node> iterator;
+
+        ModuleContentSpliterator(String module) throws IOException {
+            moduleRoot = "/modules/" + module;
+            stack = new ArrayDeque<>();
+
+            // push the root node to the stack to get started
+            ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot);
+            if (dir == null || !dir.isDirectory())
+                throw new IOException(moduleRoot + " not a directory");
+            stack.push(dir);
+            iterator = Collections.emptyIterator();
+        }
+
+        /**
+         * Returns the name of the next non-directory node or {@code null} if
+         * there are no remaining nodes to visit.
+         */
+        private String next() throws IOException {
+            for (;;) {
+                while (iterator.hasNext()) {
+                    ImageReader.Node node = iterator.next();
+                    String name = node.getName();
+                    if (node.isDirectory()) {
+                        // build node
+                        ImageReader.Node dir = SystemImage.reader().findNode(name);
+                        assert dir.isDirectory();
+                        stack.push(dir);
+                    } else {
+                        // strip /modules/$MODULE/ prefix
+                        return name.substring(moduleRoot.length() + 1);
+                    }
+                }
+
+                if (stack.isEmpty()) {
+                    return null;
+                } else {
+                    ImageReader.Node dir = stack.poll();
+                    assert dir.isDirectory();
+                    iterator = dir.getChildren().iterator();
+                }
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(Consumer<? super String> action) {
+            String next;
+            try {
+                next = next();
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+            if (next != null) {
+                action.accept(next);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public Spliterator<String> trySplit() {
+            return null;
+        }
+
+        @Override
+        public int characteristics() {
+            return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE;
+        }
+
+        @Override
+        public long estimateSize() {
+            return Long.MAX_VALUE;
+        }
+    }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java	Thu Aug 24 16:34:30 2017 +0200
@@ -26,94 +26,73 @@
 package jdk.internal.module;
 
 import java.lang.module.ModuleDescriptor;
-import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 
 /**
- * SystemModules class will be generated at link time to create
- * ModuleDescriptor for the system modules directly to improve
- * the module descriptor reconstitution time.
+ * A SystemModules object reconstitutes module descriptors and other modules
+ * attributes in an efficient way to avoid parsing module-info.class files at
+ * startup. Implementations of this class are generated by the "system modules"
+ * jlink plugin.
  *
- * This will skip parsing of module-info.class file and validating
- * names such as module name, package name, service and provider type names.
- * It also avoids taking a defensive copy of any collection.
- *
+ * @see SystemModuleFinders
  * @see jdk.tools.jlink.internal.plugins.SystemModulesPlugin
  */
-public final class SystemModules {
-    /**
-     * Name of the system modules.
-     *
-     * This array provides a way for SystemModuleFinder to fallback
-     * and read module-info.class from the run-time image instead of
-     * the fastpath.
-     */
-    public static final String[] MODULE_NAMES = new String[0];
+
+interface SystemModules {
 
     /**
-     * Number of packages in the boot layer from the installed modules.
-     *
-     * Don't make it final to avoid inlining during compile time as
-     * the value will be changed at jlink time.
+     * Returns false if the module reconstituted by this SystemModules object
+     * have no overlapping packages. Returns true if there are overlapping
+     * packages or unknown.
      */
-    public static int PACKAGES_IN_BOOT_LAYER = 1024;
+    boolean hasSplitPackages();
+
+    /**
+     * Return false if the modules reconstituted by this SystemModules object
+     * do not include any incubator modules. Returns true if there are
+     * incubating modules or unknown.
+     */
+    boolean hasIncubatorModules();
 
     /**
-     * Return true if there are no split packages in the run-time image.
+     * Returns the non-empty array of ModuleDescriptor objects.
      */
-    public static boolean hasSplitPackages() {
-        return true;
-    }
+    ModuleDescriptor[] moduleDescriptors();
 
     /**
-     * Returns a non-empty array of ModuleDescriptor objects in the run-time image.
-     *
-     * When running an exploded image it returns an empty array.
+     * Returns the array of ModuleTarget objects. The array elements correspond
+     * to the array of ModuleDescriptor objects.
      */
-    public static ModuleDescriptor[] descriptors() {
-        throw new InternalError("expected to be overridden at link time");
-    }
+    ModuleTarget[] moduleTargets();
 
     /**
-     * Returns a non-empty array of ModuleTarget objects in the run-time image.
-     *
-     * When running an exploded image it returns an empty array.
+     * Returns the array of ModuleHashes objects. The array elements correspond
+     * to the array of ModuleDescriptor objects.
      */
-    public static ModuleTarget[] targets() {
-        throw new InternalError("expected to be overridden at link time");
-    }
+    ModuleHashes[] moduleHashes();
 
     /**
-     * Returns a non-empty array of ModuleHashes recorded in each module
-     * in the run-time image.
-     *
-     * When running an exploded image it returns an empty array.
+     * Returns the array of ModuleResolution objects. The array elements correspond
+     * to the array of ModuleDescriptor objects.
      */
-    public static ModuleHashes[] hashes() {
-        throw new InternalError("expected to be overridden at link time");
-    }
+    ModuleResolution[] moduleResolutions();
 
     /**
-     * Returns a non-empty array of ModuleResolutions in the run-time image.
+     * Returns the map representing readability graph for the modules reconstituted
+     * by this SystemModules object.
      */
-    public static ModuleResolution[] moduleResolutions() {
-        throw new InternalError("expected to be overridden at link time");
-    }
+    Map<String, Set<String>> moduleReads();
 
     /**
      * Returns the map of module concealed packages to open. The map key is the
      * module name, the value is the set of concealed packages to open.
      */
-    public static Map<String, Set<String>> concealedPackagesToOpen() {
-        return Collections.emptyMap();
-    }
+    Map<String, Set<String>> concealedPackagesToOpen();
 
     /**
      * Returns the map of module exported packages to open. The map key is the
      * module name, the value is the set of exported packages to open.
      */
-    public static Map<String, Set<String>> exportedPackagesToOpen() {
-        return Collections.emptyMap();
-    }
+    Map<String, Set<String>> exportedPackagesToOpen();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/SystemModulesMap.java	Thu Aug 24 16:34:30 2017 +0200
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.module;
+
+/**
+ * This class is generated/overridden at link time to return the names of the
+ * SystemModules classes generated at link time.
+ *
+ * @see SystemModuleFinders
+ * @see jdk.tools.jlink.internal.plugins.SystemModulesPlugin
+ */
+
+class SystemModulesMap {
+
+    /**
+     * Returns the SystemModules object to reconstitute all modules or null
+     * if this is an exploded build.
+     */
+    static SystemModules allSystemModules() {
+        return null;
+    }
+
+    /**
+     * Returns the SystemModules object to reconstitute default modules or null
+     * if this is an exploded build.
+     */
+    static SystemModules defaultSystemModules() {
+        return null;
+    }
+
+    /**
+     * Returns the array of initial module names identified at link time.
+     */
+    static String[] moduleNames() {
+        return new String[0];
+    }
+
+    /**
+     * Returns the array of of SystemModules class names. The elements
+     * correspond to the elements in the array returned by moduleNames().
+     */
+    static String[] classNames() {
+        return new String[0];
+    }
+}
\ No newline at end of file
--- a/jdk/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java	Thu Aug 24 16:34:30 2017 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,10 +26,18 @@
 package sun.nio.ch;
 
 import java.io.IOException;
-import java.nio.channels.*;
-import java.nio.channels.spi.*;
 import java.net.SocketException;
-import java.util.*;
+import java.nio.channels.ClosedSelectorException;
+import java.nio.channels.IllegalSelectorException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.spi.AbstractSelectableChannel;
+import java.nio.channels.spi.AbstractSelector;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
 
 
 /**
@@ -54,23 +62,18 @@
         super(sp);
         keys = new HashSet<>();
         selectedKeys = new HashSet<>();
-        if (Util.atBugLevel("1.4")) {
-            publicKeys = keys;
-            publicSelectedKeys = selectedKeys;
-        } else {
-            publicKeys = Collections.unmodifiableSet(keys);
-            publicSelectedKeys = Util.ungrowableSet(selectedKeys);
-        }
+        publicKeys = Collections.unmodifiableSet(keys);
+        publicSelectedKeys = Util.ungrowableSet(selectedKeys);
     }
 
     public Set<SelectionKey> keys() {
-        if (!isOpen() && !Util.atBugLevel("1.4"))
+        if (!isOpen())
             throw new ClosedSelectorException();
         return publicKeys;
     }
 
     public Set<SelectionKey> selectedKeys() {
-        if (!isOpen() && !Util.atBugLevel("1.4"))
+        if (!isOpen())
             throw new ClosedSelectorException();
         return publicSelectedKeys;
     }
--- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java	Thu Aug 24 16:34:30 2017 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,13 +25,16 @@
 
 package sun.nio.ch;
 
-import java.lang.reflect.*;
 import java.io.FileDescriptor;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.nio.ByteBuffer;
 import java.nio.MappedByteBuffer;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.*;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
 import jdk.internal.misc.Unsafe;
 import sun.security.action.GetPropertyAction;
 
@@ -456,21 +459,4 @@
         }
         return dbb;
     }
-
-
-    // -- Bug compatibility --
-
-    private static volatile String bugLevel;
-
-    static boolean atBugLevel(String bl) {              // package-private
-        if (bugLevel == null) {
-            if (!jdk.internal.misc.VM.isBooted())
-                return false;
-            String value = GetPropertyAction
-                    .privilegedGetProperty("sun.nio.ch.bugLevel");
-            bugLevel = (value != null) ? value : "";
-        }
-        return bugLevel.equals(bl);
-    }
-
 }
--- a/jdk/src/java.base/share/classes/sun/security/provider/KeyProtector.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/provider/KeyProtector.java	Thu Aug 24 16:34:30 2017 +0200
@@ -35,6 +35,7 @@
 import java.security.UnrecoverableKeyException;
 import java.util.*;
 
+import jdk.internal.ref.CleanerFactory;
 import sun.security.pkcs.PKCS8Key;
 import sun.security.pkcs.EncryptedPrivateKeyInfo;
 import sun.security.x509.AlgorithmId;
@@ -141,18 +142,10 @@
             passwdBytes[j++] = (byte)(password[i] >> 8);
             passwdBytes[j++] = (byte)password[i];
         }
-    }
-
-    /**
-     * Ensures that the password bytes of this key protector are
-     * set to zero when there are no more references to it.
-     */
-    @SuppressWarnings("deprecation")
-    protected void finalize() {
-        if (passwdBytes != null) {
-            Arrays.fill(passwdBytes, (byte)0x00);
-            passwdBytes = null;
-        }
+        // Use the cleaner to zero the password when no longer referenced
+        final byte[] k = this.passwdBytes;
+        CleanerFactory.cleaner().register(this,
+                () -> java.util.Arrays.fill(k, (byte)0x00));
     }
 
     /*
--- a/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketTransportService.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketTransportService.java	Thu Aug 24 16:34:30 2017 +0200
@@ -275,6 +275,12 @@
             sa = new InetSocketAddress(localaddress, port);
         }
         ServerSocket ss = new ServerSocket();
+        if (port == 0) {
+            // Only need SO_REUSEADDR if we're using a fixed port. If we
+            // start seeing EADDRINUSE due to collisions in free ports
+            // then we should retry the bind() a few times.
+            ss.setReuseAddress(false);
+        }
         ss.bind(sa);
         return new SocketListenKey(ss);
     }
--- a/jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c	Thu Aug 24 16:34:30 2017 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -119,8 +119,26 @@
     return (char *)dbgsysTlsGet(tlsIndex);
 }
 
+/* Set options common to client and server sides */
 static jdwpTransportError
-setOptions(int fd)
+setOptionsCommon(int fd)
+{
+    jvalue dontcare;
+    int err;
+
+    dontcare.i = 0;  /* keep compiler happy */
+
+    err = dbgsysSetSocketOption(fd, TCP_NODELAY, JNI_TRUE, dontcare);
+    if (err < 0) {
+        RETURN_IO_ERROR("setsockopt TCPNODELAY failed");
+    }
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+/* Set the SO_REUSEADDR option */
+static jdwpTransportError
+setReuseAddrOption(int fd)
 {
     jvalue dontcare;
     int err;
@@ -132,11 +150,6 @@
         RETURN_IO_ERROR("setsockopt SO_REUSEADDR failed");
     }
 
-    err = dbgsysSetSocketOption(fd, TCP_NODELAY, JNI_TRUE, dontcare);
-    if (err < 0) {
-        RETURN_IO_ERROR("setsockopt TCPNODELAY failed");
-    }
-
     return JDWPTRANSPORT_ERROR_NONE;
 }
 
@@ -350,10 +363,21 @@
         RETURN_IO_ERROR("socket creation failed");
     }
 
-    err = setOptions(serverSocketFD);
+    err = setOptionsCommon(serverSocketFD);
     if (err) {
         return err;
     }
+    if (sa.sin_port != 0) {
+        /*
+         * Only need SO_REUSEADDR if we're using a fixed port. If we
+         * start seeing EADDRINUSE due to collisions in free ports
+         * then we should retry the dbgsysBind() a few times.
+         */
+        err = setReuseAddrOption(serverSocketFD);
+        if (err) {
+            return err;
+        }
+    }
 
     err = dbgsysBind(serverSocketFD, (struct sockaddr *)&sa, sizeof(sa));
     if (err < 0) {
@@ -510,12 +534,18 @@
         RETURN_IO_ERROR("unable to create socket");
     }
 
-    err = setOptions(socketFD);
+    err = setOptionsCommon(socketFD);
     if (err) {
         return err;
     }
 
     /*
+     * We don't call setReuseAddrOption() for the non-server socket
+     * case. If we start seeing EADDRINUSE due to collisions in free
+     * ports then we should retry the dbgsysConnect() a few times.
+     */
+
+    /*
      * To do a timed connect we make the socket non-blocking
      * and poll with a timeout;
      */
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java	Thu Aug 24 16:34:30 2017 +0200
@@ -28,6 +28,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.module.Configuration;
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleDescriptor.Exports;
 import java.lang.module.ModuleDescriptor.Opens;
@@ -37,12 +38,15 @@
 import java.lang.module.ModuleFinder;
 import java.lang.module.ModuleReader;
 import java.lang.module.ModuleReference;
+import java.lang.module.ResolvedModule;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -50,18 +54,20 @@
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.function.IntSupplier;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 import jdk.internal.module.Checks;
 import jdk.internal.module.ClassFileAttributes;
 import jdk.internal.module.ClassFileConstants;
+import jdk.internal.module.DefaultRoots;
 import jdk.internal.module.IllegalAccessMaps;
 import jdk.internal.module.ModuleHashes;
 import jdk.internal.module.ModuleInfo.Attributes;
 import jdk.internal.module.ModuleInfoExtender;
+import jdk.internal.module.ModuleReferenceImpl;
 import jdk.internal.module.ModuleResolution;
 import jdk.internal.module.ModuleTarget;
-import jdk.internal.module.SystemModules;
 import jdk.internal.org.objectweb.asm.Attribute;
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.ClassVisitor;
@@ -72,33 +78,42 @@
 import static jdk.internal.org.objectweb.asm.Opcodes.*;
 
 import jdk.tools.jlink.internal.ModuleSorter;
+import jdk.tools.jlink.plugin.Plugin;
 import jdk.tools.jlink.plugin.PluginException;
 import jdk.tools.jlink.plugin.ResourcePool;
-import jdk.tools.jlink.plugin.Plugin;
 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
 import jdk.tools.jlink.plugin.ResourcePoolEntry;
 
 /**
- * Jlink plugin to reconstitute module descriptors for system modules.
- * It will extend module-info.class with ModulePackages attribute,
- * if not present. It also determines the number of packages of
- * the boot layer at link time.
+ * Jlink plugin to reconstitute module descriptors and other attributes for system
+ * modules. The plugin generates implementations of SystemModules to avoid parsing
+ * module-info.class files at startup. It also generates SystemModulesMap to return
+ * the SystemModules implementation for a specific initial module.
  *
- * This plugin will override jdk.internal.module.SystemModules class
+ * As a side effect, the plugin adds the ModulePackages class file attribute to the
+ * module-info.class files that don't have the attribute.
  *
- * @see jdk.internal.module.SystemModuleFinder
- * @see SystemModules
+ * @see jdk.internal.module.SystemModuleFinders
+ * @see jdk.internal.module.SystemModules
  */
+
 public final class SystemModulesPlugin implements Plugin {
     private static final String NAME = "system-modules";
     private static final String DESCRIPTION =
-        PluginsResourceBundle.getDescription(NAME);
+            PluginsResourceBundle.getDescription(NAME);
+    private static final String SYSTEM_MODULES_MAP_CLASS =
+            "jdk/internal/module/SystemModulesMap";
+    private static final String SYSTEM_MODULES_CLASS_PREFIX =
+            "jdk/internal/module/SystemModules$";
+    private static final String ALL_SYSTEM_MODULES_CLASS =
+            SYSTEM_MODULES_CLASS_PREFIX + "all";
+    private static final String DEFAULT_SYSTEM_MODULES_CLASS =
+            SYSTEM_MODULES_CLASS_PREFIX + "default";
 
     private boolean enabled;
-    private boolean retainModuleTarget;
+
     public SystemModulesPlugin() {
         this.enabled = true;
-        this.retainModuleTarget = false;
     }
 
     @Override
@@ -131,11 +146,7 @@
     public void configure(Map<String, String> config) {
         String arg = config.get(NAME);
         if (arg != null) {
-            if (arg.equals("retainModuleTarget")) {
-                retainModuleTarget = true;
-            } else {
-                throw new IllegalArgumentException(NAME + ": " + arg);
-            }
+            throw new IllegalArgumentException(NAME + ": " + arg);
         }
     }
 
@@ -145,25 +156,56 @@
             throw new PluginException(NAME + " was set");
         }
 
-        SystemModulesClassGenerator generator =
-            new SystemModulesClassGenerator(retainModuleTarget);
+        // validate, transform (if needed), and add the module-info.class files
+        List<ModuleInfo> moduleInfos = transformModuleInfos(in, out);
+
+        // generate and add the SystemModuleMap and SystemModules classes
+        Set<String> generated = genSystemModulesClasses(moduleInfos, out);
+
+        // pass through all other resources
+        in.entries()
+            .filter(data -> !data.path().endsWith("/module-info.class")
+                    && !generated.contains(data.path()))
+            .forEach(data -> out.add(data));
 
-        // generate the byte code to create ModuleDescriptors
-        // such that the modules linked in the image would skip parsing
-        // of module-info.class and also skip name check
+        return out.build();
+    }
+
+    /**
+     * Validates and transforms the module-info.class files in the modules, adding
+     * the ModulePackages class file attribute if needed.
+     *
+     * @return the list of ModuleInfo objects, the first element is java.base
+     */
+    List<ModuleInfo> transformModuleInfos(ResourcePool in, ResourcePoolBuilder out) {
+        List<ModuleInfo> moduleInfos = new ArrayList<>();
 
         // Sort modules in the topological order so that java.base is always first.
         new ModuleSorter(in.moduleView()).sorted().forEach(module -> {
             ResourcePoolEntry data = module.findEntry("module-info.class").orElseThrow(
-                // automatic module not supported yet
+                // automatic modules not supported
                 () ->  new PluginException("module-info.class not found for " +
-                    module.name() + " module")
+                        module.name() + " module")
             );
 
             assert module.name().equals(data.moduleName());
+
             try {
-                // validate the module and add to system modules
-                data = generator.buildModuleInfo(data, module.packages());
+                byte[] content = data.contentBytes();
+                Set<String> packages = module.packages();
+                ModuleInfo moduleInfo = new ModuleInfo(content, packages);
+
+                // link-time validation
+                moduleInfo.validateNames();
+
+                // check if any exported or open package is not present
+                moduleInfo.validatePackages();
+
+                // module-info.class may be overridden to add ModulePackages
+                if (moduleInfo.shouldRewrite()) {
+                    data = data.copyWithContent(moduleInfo.getBytes());
+                }
+                moduleInfos.add(moduleInfo);
 
                 // add resource pool entry
                 out.add(data);
@@ -172,37 +214,134 @@
             }
         });
 
-        // Generate the new class
-        ClassWriter cwriter = generator.getClassWriter();
-        in.entries().forEach(data -> {
-            if (data.path().endsWith("module-info.class"))
-                return;
-            if (generator.isOverriddenClass(data.path())) {
-                byte[] bytes = cwriter.toByteArray();
-                ResourcePoolEntry ndata = data.copyWithContent(bytes);
-                out.add(ndata);
-            } else {
-                out.add(data);
+        return moduleInfos;
+    }
+
+    /**
+     * Generates the SystemModules classes (at least one) and the SystemModulesMap
+     * class to map initial modules to a SystemModules class.
+     *
+     * @return the resource names of the resources added to the pool
+     */
+    private Set<String> genSystemModulesClasses(List<ModuleInfo> moduleInfos,
+                                                ResourcePoolBuilder out) {
+        int moduleCount = moduleInfos.size();
+        ModuleFinder finder = finderOf(moduleInfos);
+        assert finder.findAll().size() == moduleCount;
+
+        // map of initial module name to SystemModules class name
+        Map<String, String> map = new LinkedHashMap<>();
+
+        // the names of resources written to the pool
+        Set<String> generated = new HashSet<>();
+
+        // generate the SystemModules implementation to reconstitute all modules
+        Set<String> allModuleNames = moduleInfos.stream()
+                .map(ModuleInfo::moduleName)
+                .collect(Collectors.toSet());
+        String rn = genSystemModulesClass(moduleInfos,
+                                          resolve(finder, allModuleNames),
+                                          ALL_SYSTEM_MODULES_CLASS,
+                                          out);
+        generated.add(rn);
+
+        // generate, if needed, a SystemModules class to reconstitute the modules
+        // needed for the case that the initial module is the unnamed module.
+        String defaultSystemModulesClassName;
+        Configuration cf = resolve(finder, DefaultRoots.compute(finder));
+        if (cf.modules().size() == moduleCount) {
+            // all modules are resolved so no need to generate a class
+            defaultSystemModulesClassName = ALL_SYSTEM_MODULES_CLASS;
+        } else {
+            defaultSystemModulesClassName = DEFAULT_SYSTEM_MODULES_CLASS;
+            rn = genSystemModulesClass(sublist(moduleInfos, cf),
+                                       cf,
+                                       defaultSystemModulesClassName,
+                                       out);
+            generated.add(rn);
+        }
+
+        // Generate a SystemModules class for each module with a main class
+        int suffix = 0;
+        for (ModuleInfo mi : moduleInfos) {
+            if (mi.descriptor().mainClass().isPresent()) {
+                String moduleName = mi.moduleName();
+                cf = resolve(finder, Set.of(moduleName));
+                if (cf.modules().size() == moduleCount) {
+                    // resolves all modules so no need to generate a class
+                    map.put(moduleName, ALL_SYSTEM_MODULES_CLASS);
+                } else {
+                    String cn = SYSTEM_MODULES_CLASS_PREFIX + (suffix++);
+                    rn = genSystemModulesClass(sublist(moduleInfos, cf), cf, cn, out);
+                    map.put(moduleName, cn);
+                    generated.add(rn);
+                }
             }
-        });
+        }
+
+        // generate SystemModulesMap
+        rn = genSystemModulesMapClass(ALL_SYSTEM_MODULES_CLASS,
+                                      defaultSystemModulesClassName,
+                                      map,
+                                      out);
+        generated.add(rn);
+
+        // return the resource names of the generated classes
+        return generated;
+    }
+
+    /**
+     * Resolves a collection of root modules, with service binding, to create
+     * configuration.
+     */
+    private Configuration resolve(ModuleFinder finder, Set<String> roots) {
+        return Configuration.empty().resolveAndBind(finder, ModuleFinder.of(), roots);
+    }
 
-        return out.build();
+    /**
+     * Returns the list of ModuleInfo objects that correspond to the modules in
+     * the given configuration.
+     */
+    private List<ModuleInfo> sublist(List<ModuleInfo> moduleInfos, Configuration cf) {
+        Set<String> names = cf.modules()
+                .stream()
+                .map(ResolvedModule::name)
+                .collect(Collectors.toSet());
+        return moduleInfos.stream()
+                .filter(mi -> names.contains(mi.moduleName()))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Generate a SystemModules implementation class and add it as a resource.
+     *
+     * @return the name of the class resource added to the pool
+     */
+    private String genSystemModulesClass(List<ModuleInfo> moduleInfos,
+                                         Configuration cf,
+                                         String className,
+                                         ResourcePoolBuilder out) {
+        SystemModulesClassGenerator generator
+            = new SystemModulesClassGenerator(className, moduleInfos);
+        byte[] bytes = generator.getClassWriter(cf).toByteArray();
+        String rn = "/java.base/" + className + ".class";
+        ResourcePoolEntry e = ResourcePoolEntry.create(rn, bytes);
+        out.add(e);
+        return rn;
     }
 
     static class ModuleInfo {
-        private final ByteArrayInputStream bain;
+        private final ByteArrayInputStream bais;
         private final Attributes attrs;
         private final Set<String> packages;
-        private final boolean dropModuleTarget;
         private final boolean addModulePackages;
         private ModuleDescriptor descriptor;  // may be different that the original one
 
-        ModuleInfo(byte[] bytes, Set<String> packages, boolean dropModuleTarget)
-            throws IOException
-        {
-            this.bain = new ByteArrayInputStream(bytes);
+        ModuleInfo(byte[] bytes, Set<String> packages) throws IOException {
+            this.bais = new ByteArrayInputStream(bytes);
             this.packages = packages;
-            this.attrs = jdk.internal.module.ModuleInfo.read(bain, null);
+            this.attrs = jdk.internal.module.ModuleInfo.read(bais, null);
+
             // If ModulePackages attribute is present, the packages from this
             // module descriptor returns the packages in that attribute.
             // If it's not present, ModuleDescriptor::packages only contains
@@ -215,14 +354,6 @@
             // add ModulePackages attribute if this module contains some packages
             // and ModulePackages is not present
             this.addModulePackages = packages.size() > 0 && !hasModulePackages();
-
-            // drop target attribute only if any OS property is present
-            ModuleTarget target = attrs.target();
-            if (dropModuleTarget && target != null) {
-                this.dropModuleTarget = (target.targetPlatform() != null);
-            } else {
-                this.dropModuleTarget = false;
-            }
         }
 
         String moduleName() {
@@ -233,7 +364,6 @@
             return descriptor;
         }
 
-
         Set<String> packages() {
             return packages;
         }
@@ -283,7 +413,6 @@
             }
         }
 
-
         /**
          * Validates if exported and open packages are present
          */
@@ -328,17 +457,15 @@
         }
 
         /**
-         * Returns true if module-info.class should be written
-         * 1. add ModulePackages attribute if not present; or
-         * 2. drop ModuleTarget attribute except java.base
+         * Returns true if module-info.class should be rewritten to add the
+         * ModulePackages attribute.
          */
         boolean shouldRewrite() {
-            return addModulePackages || dropModuleTarget;
+            return addModulePackages;
         }
 
         /**
-         * Returns the bytes for the module-info.class with ModulePackages
-         * attribute added and/or with ModuleTarget attribute dropped.
+         * Returns the bytes for the (possibly updated) module-info.class.
          */
         byte[] getBytes() throws IOException {
             try (InputStream in = getInputStream()) {
@@ -347,13 +474,10 @@
                     if (addModulePackages) {
                         rewriter.addModulePackages(packages);
                     }
-                    if (dropModuleTarget) {
-                        rewriter.dropModuleTarget();
-                    }
                     // rewritten module descriptor
                     byte[] bytes = rewriter.getBytes();
-                    try (ByteArrayInputStream bain = new ByteArrayInputStream(bytes)) {
-                        this.descriptor = ModuleDescriptor.read(bain);
+                    try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
+                        this.descriptor = ModuleDescriptor.read(bais);
                     }
                     return bytes;
                 } else {
@@ -366,8 +490,8 @@
          * Returns the input stream of the module-info.class
          */
         InputStream getInputStream() {
-            bain.reset();
-            return bain;
+            bais.reset();
+            return bais;
         }
 
         class ModuleInfoRewriter extends ByteArrayOutputStream {
@@ -383,10 +507,6 @@
                 }
             }
 
-            void dropModuleTarget() {
-                extender.targetPlatform("");
-            }
-
             byte[] getBytes() throws IOException {
                 extender.write(this);
                 return buf;
@@ -395,12 +515,10 @@
     }
 
     /**
-     * ClassWriter of a new jdk.internal.module.SystemModules class
-     * to reconstitute ModuleDescriptor of the system modules.
+     * Generates a SystemModules class to reconstitute the ModuleDescriptor
+     * and other attributes of system modules.
      */
     static class SystemModulesClassGenerator {
-        private static final String CLASSNAME =
-            "jdk/internal/module/SystemModules";
         private static final String MODULE_DESCRIPTOR_BUILDER =
             "jdk/internal/module/Builder";
         private static final String MODULE_DESCRIPTOR_ARRAY_SIGNATURE =
@@ -422,10 +540,6 @@
         private static final String MODULE_RESOLUTIONS_ARRAY_SIGNATURE  =
             "[Ljdk/internal/module/ModuleResolution;";
 
-        // static variables in SystemModules class
-        private static final String MODULE_NAMES = "MODULE_NAMES";
-        private static final String PACKAGE_COUNT = "PACKAGES_IN_BOOT_LAYER";
-
         private static final int MAX_LOCAL_VARS = 256;
 
         private final int BUILDER_VAR    = 0;
@@ -434,14 +548,14 @@
         private final int MH_VAR         = 1;  // variable for ModuleHashes
         private int nextLocalVar         = 2;  // index to next local variable
 
-        private final ClassWriter cw;
-        private boolean dropModuleTarget;
-
         // Method visitor for generating the SystemModules::modules() method
         private MethodVisitor mv;
 
+        // name of class to generate
+        private final String className;
+
         // list of all ModuleDescriptorBuilders, invoked in turn when building.
-        private final List<ModuleInfo> moduleInfos = new ArrayList<>();
+        private final List<ModuleInfo> moduleInfos;
 
         // A builder to create one single Set instance for a given set of
         // names or modifiers to reduce the footprint
@@ -449,10 +563,11 @@
         private final DedupSetBuilder dedupSetBuilder
             = new DedupSetBuilder(this::getNextLocalVar);
 
-        public SystemModulesClassGenerator(boolean retainModuleTarget) {
-            this.cw = new ClassWriter(ClassWriter.COMPUTE_MAXS +
-                                      ClassWriter.COMPUTE_FRAMES);
-            this.dropModuleTarget = !retainModuleTarget;
+        public SystemModulesClassGenerator(String className,
+                                           List<ModuleInfo> moduleInfos) {
+            this.className = className;
+            this.moduleInfos = moduleInfos;
+            moduleInfos.forEach(mi -> dedups(mi.descriptor()));
         }
 
         private int getNextLocalVar() {
@@ -460,105 +575,10 @@
         }
 
         /*
-         * static initializer initializing the static fields
-         *
-         * static Map<String, ModuleDescriptor> map = new HashMap<>();
-         */
-        private void clinit(int numModules, int numPackages,
-                            boolean hasSplitPackages) {
-            cw.visit(Opcodes.V1_8, ACC_PUBLIC+ACC_FINAL+ACC_SUPER, CLASSNAME,
-                     null, "java/lang/Object", null);
-
-            // public static String[] MODULE_NAMES = new String[] {....};
-            cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULE_NAMES,
-                    "[Ljava/lang/String;", null, null)
-                    .visitEnd();
-
-            // public static int PACKAGES_IN_BOOT_LAYER;
-            cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, PACKAGE_COUNT,
-                    "I", null, numPackages)
-                    .visitEnd();
-
-            MethodVisitor clinit =
-                cw.visitMethod(ACC_STATIC, "<clinit>", "()V",
-                               null, null);
-            clinit.visitCode();
-
-            // create the MODULE_NAMES array
-            pushInt(clinit, numModules);
-            clinit.visitTypeInsn(ANEWARRAY, "java/lang/String");
-
-            int index = 0;
-            for (ModuleInfo minfo : moduleInfos) {
-                clinit.visitInsn(DUP);                  // arrayref
-                pushInt(clinit, index++);
-                clinit.visitLdcInsn(minfo.moduleName()); // value
-                clinit.visitInsn(AASTORE);
-            }
-
-            clinit.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULE_NAMES,
-                    "[Ljava/lang/String;");
-
-            clinit.visitInsn(RETURN);
-            clinit.visitMaxs(0, 0);
-            clinit.visitEnd();
-
-            // public static boolean hasSplitPackages();
-            MethodVisitor split =
-                cw.visitMethod(ACC_PUBLIC+ACC_STATIC, "hasSplitPackages",
-                               "()Z", null, null);
-            split.visitCode();
-            split.visitInsn(hasSplitPackages ? ICONST_1 : ICONST_0);
-            split.visitInsn(IRETURN);
-            split.visitMaxs(0, 0);
-            split.visitEnd();
-
-        }
-
-        /*
          * Adds the given ModuleDescriptor to the system module list.
          * It performs link-time validation and prepares mapping from various
          * Sets to SetBuilders to emit an optimized number of sets during build.
          */
-        public ResourcePoolEntry buildModuleInfo(ResourcePoolEntry entry,
-                                                 Set<String> packages)
-            throws IOException
-        {
-            if (moduleInfos.isEmpty() && !entry.moduleName().equals("java.base")) {
-                throw new InternalError("java.base must be the first module to process");
-            }
-
-            ModuleInfo moduleInfo;
-            if (entry.moduleName().equals("java.base")) {
-                moduleInfo = new ModuleInfo(entry.contentBytes(), packages, false);
-                ModuleDescriptor md = moduleInfo.descriptor;
-                // drop ModuleTarget attribute if java.base has all OS properties
-                ModuleTarget target = moduleInfo.target();
-                if (dropModuleTarget && target.targetPlatform() != null) {
-                    dropModuleTarget = true;
-                } else {
-                    dropModuleTarget = false;
-                }
-            } else {
-                moduleInfo = new ModuleInfo(entry.contentBytes(), packages, dropModuleTarget);
-            }
-
-            // link-time validation
-            moduleInfo.validateNames();
-            // check if any exported or open package is not present
-            moduleInfo.validatePackages();
-
-            // module-info.class may be overridden for optimization
-            // 1. update ModuleTarget attribute to drop targetPlartform
-            // 2. add/update ModulePackages attribute
-            if (moduleInfo.shouldRewrite()) {
-                entry = entry.copyWithContent(moduleInfo.getBytes());
-            }
-            moduleInfos.add(moduleInfo);
-            dedups(moduleInfo.descriptor());
-            return entry;
-        }
-
         private void dedups(ModuleDescriptor md) {
             // exports
             for (Exports e : md.exports()) {
@@ -581,47 +601,123 @@
             dedupSetBuilder.stringSet(md.uses());
         }
 
-        /*
-         * Generate bytecode for SystemModules
+        /**
+         * Generate SystemModules class
          */
-        public ClassWriter getClassWriter() {
-            int numModules = moduleInfos.size();
-            Set<String> allPackages = new HashSet<>();
-            int packageCount = 0;
-            for (ModuleInfo minfo : moduleInfos) {
-                allPackages.addAll(minfo.packages);
-                packageCount += minfo.packages.size();
-            }
+        public ClassWriter getClassWriter(Configuration cf) {
+            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
+                                             + ClassWriter.COMPUTE_FRAMES);
+            cw.visit(Opcodes.V1_8,
+                     ACC_FINAL+ACC_SUPER,
+                     className,
+                     null,
+                     "java/lang/Object",
+                     new String[] { "jdk/internal/module/SystemModules" });
 
-            int numPackages = allPackages.size();
-            boolean hasSplitPackages = (numPackages < packageCount);
-            clinit(numModules, numPackages, hasSplitPackages);
+            // generate <init>
+            genConstructor(cw);
+
+            // generate hasSplitPackages
+            genHasSplitPackages(cw);
 
-            // generate SystemModules::descriptors
-            genDescriptorsMethod();
+            // generate hasIncubatorModules
+            genIncubatorModules(cw);
 
-            // generate SystemModules::targets
-            genTargetsMethod();
+            // generate moduleDescriptors
+            genModuleDescriptorsMethod(cw);
+
+            // generate moduleTargets
+            genModuleTargetsMethod(cw);
 
-            // generate SystemModules::hashes
-            genHashesMethod();
+            // generate moduleHashes
+            genModuleHashesMethod(cw);
+
+            // generate moduleResolutions
+            genModuleResolutionsMethod(cw);
 
-            // generate SystemModules::moduleResolutions
-            genModuleResolutionsMethod();
+            // generate moduleReads
+            genModuleReads(cw, cf);
 
-            // generate SystemModules::concealedPackagesToOpen and
-            // SystemModules::exportedPackagesToOpen
-            genXXXPackagesToOpenMethods();
+            // generate concealedPackagesToOpen and exportedPackagesToOpen
+            genXXXPackagesToOpenMethods(cw);
 
             return cw;
         }
 
         /**
-         * Generate bytecode for SystemModules::descriptors method
+         * Generate byteccode for no-arg constructor
+         */
+        private void genConstructor(ClassWriter cw) {
+            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKESPECIAL,
+                               "java/lang/Object",
+                               "<init>",
+                               "()V",
+                               false);
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+        }
+
+        /**
+         * Generate bytecode for hasSplitPackages method
          */
-        private void genDescriptorsMethod() {
-            this.mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
-                                     "descriptors",
+        private void genHasSplitPackages(ClassWriter cw) {
+            boolean distinct = moduleInfos.stream()
+                    .map(ModuleInfo::packages)
+                    .flatMap(Set::stream)
+                    .allMatch(new HashSet<>()::add);
+            boolean hasSplitPackages = !distinct;
+
+            mv = cw.visitMethod(ACC_PUBLIC,
+                                "hasSplitPackages",
+                                "()Z",
+                                "()Z",
+                                null);
+            mv.visitCode();
+            if (hasSplitPackages) {
+                mv.visitInsn(ICONST_1);
+            } else {
+                mv.visitInsn(ICONST_0);
+            }
+            mv.visitInsn(IRETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+        }
+
+        /**
+         * Generate bytecode for hasIncubatorModules method
+         */
+        private void genIncubatorModules(ClassWriter cw) {
+            boolean hasIncubatorModules = moduleInfos.stream()
+                    .map(ModuleInfo::moduleResolution)
+                    .filter(mres -> (mres != null && mres.hasIncubatingWarning()))
+                    .findFirst()
+                    .isPresent();
+
+            mv = cw.visitMethod(ACC_PUBLIC,
+                                "hasIncubatorModules",
+                                "()Z",
+                                "()Z",
+                                null);
+            mv.visitCode();
+            if (hasIncubatorModules) {
+                mv.visitInsn(ICONST_1);
+            } else {
+                mv.visitInsn(ICONST_0);
+            }
+            mv.visitInsn(IRETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+        }
+
+        /**
+         * Generate bytecode for moduleDescriptors method
+         */
+        private void genModuleDescriptorsMethod(ClassWriter cw) {
+            this.mv = cw.visitMethod(ACC_PUBLIC,
+                                     "moduleDescriptors",
                                      "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
                                      "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
                                      null);
@@ -643,11 +739,11 @@
         }
 
         /**
-         * Generate bytecode for SystemModules::targets method
+         * Generate bytecode for moduleTargets method
          */
-        private void genTargetsMethod() {
-            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC+ACC_STATIC,
-                                              "targets",
+        private void genModuleTargetsMethod(ClassWriter cw) {
+            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
+                                              "moduleTargets",
                                               "()" + MODULE_TARGET_ARRAY_SIGNATURE,
                                               "()" + MODULE_TARGET_ARRAY_SIGNATURE,
                                               null);
@@ -656,18 +752,34 @@
             mv.visitTypeInsn(ANEWARRAY, MODULE_TARGET_CLASSNAME);
             mv.visitVarInsn(ASTORE, MT_VAR);
 
-            for (int index=0; index < moduleInfos.size(); index++) {
+
+            // if java.base has a ModuleTarget attribute then generate the array
+            // with one element, all other elements will be null.
+
+            ModuleInfo base = moduleInfos.get(0);
+            if (!base.moduleName().equals("java.base"))
+                throw new InternalError("java.base should be first module in list");
+            ModuleTarget target = base.target();
+
+            int count;
+            if (target != null && target.targetPlatform() != null) {
+                count = 1;
+            } else {
+                count = moduleInfos.size();
+            }
+
+            for (int index = 0; index < count; index++) {
                 ModuleInfo minfo = moduleInfos.get(index);
-                if (minfo.target() != null && !minfo.dropModuleTarget) {
+                if (minfo.target() != null) {
                     mv.visitVarInsn(ALOAD, MT_VAR);
                     pushInt(mv, index);
 
-                    // new ModuleTarget(String, String)
+                    // new ModuleTarget(String)
                     mv.visitTypeInsn(NEW, MODULE_TARGET_CLASSNAME);
                     mv.visitInsn(DUP);
                     mv.visitLdcInsn(minfo.target().targetPlatform());
                     mv.visitMethodInsn(INVOKESPECIAL, MODULE_TARGET_CLASSNAME,
-                        "<init>", "(Ljava/lang/String;)V", false);
+                                       "<init>", "(Ljava/lang/String;)V", false);
 
                     mv.visitInsn(AASTORE);
                 }
@@ -680,12 +792,12 @@
         }
 
         /**
-         * Generate bytecode for SystemModules::hashes method
+         * Generate bytecode for moduleHashes method
          */
-        private void genHashesMethod() {
+        private void genModuleHashesMethod(ClassWriter cw) {
             MethodVisitor hmv =
-                cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
-                               "hashes",
+                cw.visitMethod(ACC_PUBLIC,
+                               "moduleHashes",
                                "()" + MODULE_HASHES_ARRAY_SIGNATURE,
                                "()" + MODULE_HASHES_ARRAY_SIGNATURE,
                                null);
@@ -710,11 +822,11 @@
         }
 
         /**
-         * Generate bytecode for SystemModules::methodResoultions method
+         * Generate bytecode for moduleResolutions method
          */
-        private void genModuleResolutionsMethod() {
+        private void genModuleResolutionsMethod(ClassWriter cw) {
             MethodVisitor mresmv =
-                cw.visitMethod(ACC_PUBLIC+ACC_STATIC,
+                cw.visitMethod(ACC_PUBLIC,
                                "moduleResolutions",
                                "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
                                "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
@@ -746,75 +858,88 @@
         }
 
         /**
-         * Generate SystemModules::concealedPackagesToOpen and
-         * SystemModules::exportedPackagesToOpen methods.
+         * Generate bytecode for moduleReads method
          */
-        private void genXXXPackagesToOpenMethods() {
-            List<ModuleDescriptor> descriptors = moduleInfos.stream()
-                    .map(ModuleInfo::descriptor)
-                    .collect(Collectors.toList());
-            ModuleFinder finder = finderOf(descriptors);
-            IllegalAccessMaps maps = IllegalAccessMaps.generate(finder);
-            generate("concealedPackagesToOpen", maps.concealedPackagesToOpen());
-            generate("exportedPackagesToOpen", maps.exportedPackagesToOpen());
+        private void genModuleReads(ClassWriter cw, Configuration cf) {
+            // module name -> names of modules that it reads
+            Map<String, Set<String>> map = cf.modules().stream()
+                    .collect(Collectors.toMap(
+                            ResolvedModule::name,
+                            m -> m.reads().stream()
+                                    .map(ResolvedModule::name)
+                                    .collect(Collectors.toSet())));
+            generate(cw, "moduleReads", map, true);
         }
 
         /**
-         * Generate SystemModules:XXXPackagesToOpen
+         * Generate concealedPackagesToOpen and exportedPackagesToOpen methods.
          */
-        private void generate(String methodName, Map<String, Set<String>> map) {
-            // Map<String, Set<String>> XXXPackagesToOpen()
-            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC+ACC_STATIC,
+        private void genXXXPackagesToOpenMethods(ClassWriter cw) {
+            ModuleFinder finder = finderOf(moduleInfos);
+            IllegalAccessMaps maps = IllegalAccessMaps.generate(finder);
+            generate(cw, "concealedPackagesToOpen", maps.concealedPackagesToOpen(), false);
+            generate(cw, "exportedPackagesToOpen", maps.exportedPackagesToOpen(), false);
+        }
+
+        /**
+         * Generate method to return {@code Map<String, Set<String>>}.
+         *
+         * If {@code dedup} is true then the values are de-duplicated.
+         */
+        private void generate(ClassWriter cw,
+                              String methodName,
+                              Map<String, Set<String>> map,
+                              boolean dedup) {
+            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
                                               methodName,
                                               "()Ljava/util/Map;",
                                               "()Ljava/util/Map;",
                                               null);
             mv.visitCode();
 
-            // new Map$Entry[moduleCount]
+            // map of Set -> local
+            Map<Set<String>, Integer> locals;
+
+            // generate code to create the sets that are duplicated
+            if (dedup) {
+                Collection<Set<String>> values = map.values();
+                Set<Set<String>> duplicateSets = values.stream()
+                        .distinct()
+                        .filter(s -> Collections.frequency(values, s) > 1)
+                        .collect(Collectors.toSet());
+                locals = new HashMap<>();
+                int index = 1;
+                for (Set<String> s : duplicateSets) {
+                    genImmutableSet(mv, s);
+                    mv.visitVarInsn(ASTORE, index);
+                    locals.put(s, index);
+                    if (++index >= MAX_LOCAL_VARS) {
+                        break;
+                    }
+                }
+            } else {
+                locals = Map.of();
+            }
+
+            // new Map$Entry[size]
             pushInt(mv, map.size());
             mv.visitTypeInsn(ANEWARRAY, "java/util/Map$Entry");
 
             int index = 0;
             for (Map.Entry<String, Set<String>> e : map.entrySet()) {
-                String moduleName = e.getKey();
-                Set<String> packages = e.getValue();
-                int packageCount = packages.size();
+                String name = e.getKey();
+                Set<String> s = e.getValue();
 
                 mv.visitInsn(DUP);
                 pushInt(mv, index);
-                mv.visitLdcInsn(moduleName);
+                mv.visitLdcInsn(name);
 
-                // use Set.of(Object[]) when there are more than 2 packages
-                // use Set.of(Object) or Set.of(Object, Object) when fewer packages
-                if (packageCount > 2) {
-                    pushInt(mv, packageCount);
-                    mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
-                    int i = 0;
-                    for (String pn : packages) {
-                        mv.visitInsn(DUP);
-                        pushInt(mv, i);
-                        mv.visitLdcInsn(pn);
-                        mv.visitInsn(AASTORE);
-                        i++;
-                    }
-                    mv.visitMethodInsn(INVOKESTATIC,
-                                       "java/util/Set",
-                                       "of",
-                                       "([Ljava/lang/Object;)Ljava/util/Set;",
-                                       true);
+                // if de-duplicated then load the local, otherwise generate code
+                Integer varIndex = locals.get(s);
+                if (varIndex == null) {
+                    genImmutableSet(mv, s);
                 } else {
-                    StringBuilder sb = new StringBuilder("(");
-                    for (String pn : packages) {
-                        mv.visitLdcInsn(pn);
-                        sb.append("Ljava/lang/Object;");
-                    }
-                    sb.append(")Ljava/util/Set;");
-                    mv.visitMethodInsn(INVOKESTATIC,
-                                       "java/util/Set",
-                                       "of",
-                                       sb.toString(),
-                                       true);
+                    mv.visitVarInsn(ALOAD, varIndex);
                 }
 
                 String desc = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map$Entry;";
@@ -835,19 +960,42 @@
             mv.visitEnd();
         }
 
-        public boolean isOverriddenClass(String path) {
-            return path.equals("/java.base/" + CLASSNAME + ".class");
-        }
+        /**
+         * Generate code to generate an immutable set.
+         */
+        private void genImmutableSet(MethodVisitor mv, Set<String> set) {
+            int size = set.size();
 
-        void pushInt(MethodVisitor mv, int num) {
-            if (num <= 5) {
-                mv.visitInsn(ICONST_0 + num);
-            } else if (num < Byte.MAX_VALUE) {
-                mv.visitIntInsn(BIPUSH, num);
-            } else if (num < Short.MAX_VALUE) {
-                mv.visitIntInsn(SIPUSH, num);
+            // use Set.of(Object[]) when there are more than 2 elements
+            // use Set.of(Object) or Set.of(Object, Object) when fewer
+            if (size > 2) {
+                pushInt(mv, size);
+                mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
+                int i = 0;
+                for (String element : set) {
+                    mv.visitInsn(DUP);
+                    pushInt(mv, i);
+                    mv.visitLdcInsn(element);
+                    mv.visitInsn(AASTORE);
+                    i++;
+                }
+                mv.visitMethodInsn(INVOKESTATIC,
+                        "java/util/Set",
+                        "of",
+                        "([Ljava/lang/Object;)Ljava/util/Set;",
+                        true);
             } else {
-                throw new IllegalArgumentException("exceed limit: " + num);
+                StringBuilder sb = new StringBuilder("(");
+                for (String element : set) {
+                    mv.visitLdcInsn(element);
+                    sb.append("Ljava/lang/Object;");
+                }
+                sb.append(")Ljava/util/Set;");
+                mv.visitMethodInsn(INVOKESTATIC,
+                        "java/util/Set",
+                        "of",
+                        sb.toString(),
+                        true);
             }
         }
 
@@ -1564,18 +1712,159 @@
         }
     }
 
-    static ModuleFinder finderOf(Iterable<ModuleDescriptor> descriptors) {
+    /**
+     * Generate SystemModulesMap and add it as a resource.
+     *
+     * @return the name of the class resource added to the pool
+     */
+    private String genSystemModulesMapClass(String allSystemModulesClassName,
+                                            String defaultSystemModulesClassName,
+                                            Map<String, String> map,
+                                            ResourcePoolBuilder out) {
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
+                                         + ClassWriter.COMPUTE_FRAMES);
+        cw.visit(Opcodes.V1_8,
+                 ACC_FINAL+ACC_SUPER,
+                 SYSTEM_MODULES_MAP_CLASS,
+                 null,
+                 "java/lang/Object",
+                 null);
+
+        // <init>
+        MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitMethodInsn(INVOKESPECIAL,
+                           "java/lang/Object",
+                           "<init>",
+                           "()V",
+                           false);
+        mv.visitInsn(RETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        // allSystemModules()
+        mv = cw.visitMethod(ACC_STATIC,
+                            "allSystemModules",
+                            "()Ljdk/internal/module/SystemModules;",
+                            "()Ljdk/internal/module/SystemModules;",
+                            null);
+        mv.visitCode();
+        mv.visitTypeInsn(NEW, allSystemModulesClassName);
+        mv.visitInsn(DUP);
+        mv.visitMethodInsn(INVOKESPECIAL,
+                           allSystemModulesClassName,
+                           "<init>",
+                           "()V",
+                           false);
+        mv.visitInsn(ARETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        // defaultSystemModules()
+        mv = cw.visitMethod(ACC_STATIC,
+                            "defaultSystemModules",
+                            "()Ljdk/internal/module/SystemModules;",
+                            "()Ljdk/internal/module/SystemModules;",
+                            null);
+        mv.visitCode();
+        mv.visitTypeInsn(NEW, defaultSystemModulesClassName);
+        mv.visitInsn(DUP);
+        mv.visitMethodInsn(INVOKESPECIAL,
+                           defaultSystemModulesClassName,
+                           "<init>",
+                           "()V",
+                           false);
+        mv.visitInsn(ARETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        // moduleNames()
+        mv = cw.visitMethod(ACC_STATIC,
+                            "moduleNames",
+                            "()[Ljava/lang/String;",
+                            "()[Ljava/lang/String;",
+                            null);
+        mv.visitCode();
+        pushInt(mv, map.size());
+        mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
+
+        int index = 0;
+        for (String moduleName : map.keySet()) {
+            mv.visitInsn(DUP);                  // arrayref
+            pushInt(mv, index);
+            mv.visitLdcInsn(moduleName);
+            mv.visitInsn(AASTORE);
+            index++;
+        }
+
+        mv.visitInsn(ARETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        // classNames()
+        mv = cw.visitMethod(ACC_STATIC,
+                            "classNames",
+                            "()[Ljava/lang/String;",
+                            "()[Ljava/lang/String;",
+                            null);
+        mv.visitCode();
+        pushInt(mv, map.size());
+        mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
+
+        index = 0;
+        for (String className : map.values()) {
+            mv.visitInsn(DUP);                  // arrayref
+            pushInt(mv, index);
+            mv.visitLdcInsn(className.replace('/', '.'));
+            mv.visitInsn(AASTORE);
+            index++;
+        }
+
+        mv.visitInsn(ARETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        // write the class file to the pool as a resource
+        String rn = "/java.base/" + SYSTEM_MODULES_MAP_CLASS + ".class";
+        ResourcePoolEntry e = ResourcePoolEntry.create(rn, cw.toByteArray());
+        out.add(e);
+
+        return rn;
+    }
+
+    /**
+     * Pushes an int constant
+     */
+    private static void pushInt(MethodVisitor mv, int value) {
+        if (value <= 5) {
+            mv.visitInsn(ICONST_0 + value);
+        } else if (value < Byte.MAX_VALUE) {
+            mv.visitIntInsn(BIPUSH, value);
+        } else if (value < Short.MAX_VALUE) {
+            mv.visitIntInsn(SIPUSH, value);
+        } else {
+            throw new IllegalArgumentException("exceed limit: " + value);
+        }
+    }
+
+    /**
+     * Returns a module finder that finds all modules in the given list
+     */
+    private static ModuleFinder finderOf(Collection<ModuleInfo> moduleInfos) {
+        Supplier<ModuleReader> readerSupplier = () -> null;
         Map<String, ModuleReference> namesToReference = new HashMap<>();
-        for (ModuleDescriptor descriptor : descriptors) {
-            String name = descriptor.name();
-            URI uri = URI.create("module:/" + name);
-            ModuleReference mref = new ModuleReference(descriptor, uri) {
-                @Override
-                public ModuleReader open() {
-                    throw new UnsupportedOperationException();
-                }
-            };
-            namesToReference.putIfAbsent(name, mref);
+        for (ModuleInfo mi : moduleInfos) {
+            String name = mi.moduleName();
+            ModuleReference mref
+                = new ModuleReferenceImpl(mi.descriptor(),
+                                          URI.create("jrt:/" + name),
+                                          readerSupplier,
+                                          null,
+                                          mi.target(),
+                                          null,
+                                          null,
+                                          mi.moduleResolution());
+            namesToReference.put(name, mref);
         }
 
         return new ModuleFinder() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/crypto/provider/Cipher/DES/DESKeyCleanupTest.java	Thu Aug 24 16:34:30 2017 +0200
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules java.base/com.sun.crypto.provider:+open
+ * @run main/othervm DESKeyCleanupTest
+ * @summary Verify that key storage is cleared
+ */
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/**
+ * Test that the array holding the key bytes is cleared when it is
+ * no longer referenced by the key.
+ */
+
+public class DESKeyCleanupTest {
+
+    private final static String SunJCEProvider = "SunJCE";
+
+    public static void main(String[] args) throws Exception {
+        testCleanupSecret("DES");
+        testCleanupSecret("DESede");
+    }
+
+    static void testCleanupSecret(String algorithm) throws Exception {
+        KeyGenerator desGen = KeyGenerator.getInstance(algorithm, SunJCEProvider);
+        SecretKey key = desGen.generateKey();
+
+        // Break into the implementation to observe the key byte array.
+        Class<?> keyClass = key.getClass();
+        Field keyField = keyClass.getDeclaredField("key");
+        keyField.setAccessible(true);
+        byte[] array = (byte[])keyField.get(key);
+
+        byte[] zeros = new byte[array.length];
+        do {
+            // Wait for array to be cleared;  if not cleared test will timeout
+            System.out.printf("%s array: %s%n", algorithm, Arrays.toString(array));
+            key = null;
+            System.gc();        // attempt to reclaim the key
+        } while (Arrays.compare(zeros, array) != 0);
+        System.out.printf("%s array: %s%n", algorithm, Arrays.toString(array));
+
+        Reference.reachabilityFence(key); // Keep key alive
+        Reference.reachabilityFence(array); // Keep array alive
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/crypto/provider/Cipher/PBE/PBEKeyCleanupTest.java	Thu Aug 24 16:34:30 2017 +0200
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules java.base/com.sun.crypto.provider:+open
+ * @run main/othervm PBEKeyCleanupTest
+ * @summary Verify that key storage is cleared
+ */
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Random;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
+/**
+ * Test that the array holding the key bytes is cleared when it is
+ * no longer referenced by the key.
+ */
+public class PBEKeyCleanupTest {
+
+    private final static String SunJCEProvider = "SunJCE";
+
+    private static final String PASS_PHRASE = "some hidden string";
+    private static final int ITERATION_COUNT = 1000;
+    private static final int KEY_SIZE = 128;
+
+    public static void main(String[] args) throws Exception {
+        testPBESecret("PBEWithMD5AndDES");
+        testPBKSecret("PBKDF2WithHmacSHA1");
+    }
+
+    private static void testPBESecret(String algorithm) throws Exception {
+        char[] password = new char[] {'f', 'o', 'o'};
+        PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
+        SecretKeyFactory keyFac =
+                SecretKeyFactory.getInstance(algorithm, SunJCEProvider);
+
+        testCleanupSecret(algorithm, keyFac.generateSecret(pbeKeySpec));
+    }
+
+    private static void testPBKSecret(String algorithm) throws Exception {
+        byte[] salt = new byte[8];
+        new Random().nextBytes(salt);
+        char[] password = new char[] {'f', 'o', 'o'};
+        PBEKeySpec pbeKeySpec = new PBEKeySpec(PASS_PHRASE.toCharArray(), salt,
+                ITERATION_COUNT, KEY_SIZE);
+        SecretKeyFactory keyFac =
+                SecretKeyFactory.getInstance(algorithm, SunJCEProvider);
+
+        testCleanupSecret(algorithm, keyFac.generateSecret(pbeKeySpec));
+    }
+
+    static void testCleanupSecret(String algorithm, SecretKey key) throws Exception {
+
+        // Break into the implementation to observe the key byte array.
+        Class<?> keyClass = key.getClass();
+        Field keyField = keyClass.getDeclaredField("key");
+        keyField.setAccessible(true);
+        byte[] array = (byte[])keyField.get(key);
+
+        byte[] zeros = new byte[array.length];
+        do {
+            // Wait for array to be cleared;  if not cleared test will timeout
+            System.out.printf("%s array: %s%n", algorithm, Arrays.toString(array));
+            key = null;
+            System.gc();        // attempt to reclaim the key
+        } while (Arrays.compare(zeros, array) != 0);
+        System.out.printf("%s array: %s%n", algorithm, Arrays.toString(array));
+
+        Reference.reachabilityFence(key); // Keep key alive
+        Reference.reachabilityFence(array); // Keep array alive
+    }
+}
+
+
+
--- a/jdk/test/java/lang/ClassLoader/getResource/GetResource.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/lang/ClassLoader/getResource/GetResource.java	Thu Aug 24 16:34:30 2017 +0200
@@ -125,10 +125,9 @@
         return new Object[][] {
             new Object[] { List.of("-Xbootclasspath/a:."), "a"},
 
-            // "b" is the expected result when JDK-8185540 is resolved
-            new Object[] { List.of("-Xbootclasspath/a:" + dirB), "a"},
+            new Object[] { List.of("-Xbootclasspath/a:" + dirB), "b"},
             // empty path in first element
-            new Object[] { List.of("-Xbootclasspath/a:" + File.pathSeparator + dirB), "a"},
+            new Object[] { List.of("-Xbootclasspath/a:" + File.pathSeparator + dirB), "b"},
 
             new Object[] { List.of("-cp", File.pathSeparator), "a"},
             new Object[] { List.of("-cp", dirB), "b"},
--- a/jdk/test/java/net/httpclient/security/0.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/0.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -23,6 +23,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/1.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/1.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -23,6 +23,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/10.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/10.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -22,6 +22,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/11.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/11.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -24,6 +24,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/12.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/12.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -24,6 +24,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/14.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/14.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -23,6 +23,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/15.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/15.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -26,6 +26,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/2.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/2.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -23,6 +23,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/3.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/3.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -23,6 +23,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/4.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/4.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -24,6 +24,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/5.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/5.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -23,6 +23,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/6.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/6.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -23,6 +23,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/7.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/7.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -23,6 +23,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/8.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/8.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -23,6 +23,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/9.policy	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/9.policy	Thu Aug 24 16:34:30 2017 +0200
@@ -23,6 +23,7 @@
 
 grant codeBase "jrt:/jdk.incubator.httpclient" {
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
     permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
 
--- a/jdk/test/java/net/httpclient/security/Driver.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/net/httpclient/security/Driver.java	Thu Aug 24 16:34:30 2017 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,7 +34,7 @@
  * @compile ../ProxyServer.java
  * @build Security
  *
- * @run driver/timeout=60 Driver
+ * @run driver/timeout=90 Driver
  */
 
 /**
--- a/jdk/test/java/nio/channels/Selector/KeySets.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/java/nio/channels/Selector/KeySets.java	Thu Aug 24 16:34:30 2017 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,34 +26,25 @@
  * @summary Check various properties of key and selected-key sets
  *
  * @run main KeySets
- * @run main/othervm -Dsun.nio.ch.bugLevel=1.4 KeySets
  */
 
 import java.io.*;
 import java.nio.channels.*;
 import java.util.*;
 
-
 public class KeySets {
 
-    static boolean compat;
-
     static abstract class Catch {
         abstract void go() throws Exception;
         Catch(Class xc) throws Exception {
             try {
                 go();
             } catch (Exception x) {
-                if (compat)
-                    throw new Exception("Exception thrown", x);
                 if (xc.isInstance(x))
                     return;
                 throw new Exception("Wrong exception", x);
             }
-            if (compat)
-                return;
-            throw new Exception("Not thrown as expected: "
-                                + xc.getName());
+            throw new Exception("Not thrown as expected: " + xc.getName());
         }
     }
 
@@ -74,7 +65,6 @@
                 void go() throws Exception {
                     sel.selectedKeys();
                 }};
-
     }
 
     static void testNoAddition(final Set s) throws Exception {
@@ -174,14 +164,10 @@
         sel.selectedKeys().clear();
         if (!sel.selectedKeys().isEmpty())
             throw new Exception("clear failed");
-
     }
 
     public static void main(String[] args) throws Exception {
-        String bl = System.getProperty("sun.nio.ch.bugLevel");
-        compat = (bl != null) && bl.equals("1.4");
         testClose();
         testMutability();
     }
-
 }
--- a/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java	Thu Aug 24 16:34:30 2017 +0200
@@ -111,16 +111,10 @@
 
     private void checkAttributes(ModuleReference modRef) {
         try {
-            if (modRef.descriptor().name().equals("java.base")) {
-                ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.read(modRef);
-                String[] values = mt.targetPlatform().split("-");
-                assertTrue(checkOSName(values[0]));
-                assertTrue(checkOSArch(values[1]));
-            } else {
-                // target platform attribute is dropped by jlink plugin for other modules
-                ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.read(modRef);
-                assertTrue(mt == null || mt.targetPlatform() == null);
-            }
+            ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.read(modRef);
+            String[] values = mt.targetPlatform().split("-");
+            assertTrue(checkOSName(values[0]));
+            assertTrue(checkOSArch(values[1]));
         } catch (IOException exp) {
             throw new UncheckedIOException(exp);
         }
--- a/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java	Thu Aug 24 16:34:30 2017 +0200
@@ -284,7 +284,6 @@
         Set<String> modules = Set.of("m1", "m4");
         assertTrue(JLINK_TOOL.run(System.out, System.out,
             "--output", dir.toString(),
-            "--system-modules", "retainModuleTarget",
             "--exclude-resources", "m4/p4/dummy/*",
             "--add-modules", modules.stream().collect(Collectors.joining(",")),
             "--module-path", mp) == 0);
--- a/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m4/p4/Main.java	Fri Aug 04 23:29:04 2017 +0000
+++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m4/p4/Main.java	Thu Aug 24 16:34:30 2017 +0200
@@ -32,7 +32,7 @@
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.Collections;
+import java.util.Map;
 import java.util.Set;
 
 import jdk.internal.module.ClassFileAttributes;
@@ -67,8 +67,7 @@
     }
 
     private static boolean hasModuleTarget(String modName) throws IOException {
-        FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"),
-                                                  Collections.emptyMap());
+        FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Map.of());
         Path path = fs.getPath("/", "modules", modName, "module-info.class");
         try (InputStream in = Files.newInputStream(path)) {
             return hasModuleTarget(in);
@@ -86,8 +85,8 @@
             expectModuleTarget = true;
         }
 
-        // java.base is packaged with osName/osArch/osVersion
-        if (! hasModuleTarget("java.base")) {
+        // java.base is packaged with ModuleTarget
+        if (!hasModuleTarget("java.base")) {
             throw new RuntimeException("ModuleTarget absent for java.base");
         }
 
@@ -109,8 +108,7 @@
         }
 
         // verify ModuleDescriptor from module-info.class read from jimage
-        FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"),
-            Collections.emptyMap());
+        FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Map.of());
         Path path = fs.getPath("/", "modules", mn, "module-info.class");
         checkModuleDescriptor(ModuleDescriptor.read(Files.newInputStream(path)), packages);
     }
@@ -121,16 +119,9 @@
             throw new RuntimeException(md.mainClass().toString());
         }
 
-        if (expectModuleTarget) {
-            // ModuleTarget attribute is retained
-            if (! hasModuleTarget(md.name())) {
-                throw new RuntimeException("ModuleTarget missing for " + md.name());
-            }
-        } else {
-            // by default ModuleTarget attribute is dropped
-            if (hasModuleTarget(md.name())) {
-                throw new RuntimeException("ModuleTarget present for " + md.name());
-            }
+        // ModuleTarget attribute should be present
+        if (!hasModuleTarget(md.name())) {
+            throw new RuntimeException("ModuleTarget missing for " + md.name());
         }
 
         Set<String> pkgs = md.packages();
--- a/jdk/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModules.java	Fri Aug 04 23:29:04 2017 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.internal.module;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-
-/*
- * Test --patch-module java.base=jdk/modules/java.base to override
- * java.base with an exploded image
- */
-public final class SystemModules {
-    public static final String[] MODULE_NAMES = new String[0];
-
-    public static int PACKAGES_IN_BOOT_LAYER = 1024;
-
-    public static boolean hasSplitPackages() {
-        return true;
-    }
-
-    public static Map<String, Set<String>> concealedPackagesToOpen() {
-        return Collections.emptyMap();
-    }
-
-    public static Map<String, Set<String>> exportedPackagesToOpen() {
-        return Collections.emptyMap();
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModulesMap.java	Thu Aug 24 16:34:30 2017 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.module;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/*
+ * Test --patch-module java.base=jdk/modules/java.base to override
+ * java.base with an exploded image
+ */
+class SystemModulesMap {
+    static SystemModules allSystemModules() {
+        return null;
+    }
+    static SystemModules defaultSystemModules() {
+        return null;
+    }
+    static String[] moduleNames() {
+        return new String[0];
+    }
+    static String[] classNames() {
+        return new String[0];
+    }
+}
--- a/make/Docs.gmk	Fri Aug 04 23:29:04 2017 +0000
+++ b/make/Docs.gmk	Thu Aug 24 16:34:30 2017 +0200
@@ -101,9 +101,9 @@
 JAVADOC_DISABLED_DOCLINT := accessibility html missing syntax reference
 
 # The initial set of options for javadoc
-JAVADOC_OPTIONS := -XDignore.symbol.file=true -use -keywords -notimestamp \
-    -serialwarn -encoding ISO-8859-1 -breakiterator -splitIndex --system none \
-    -html5 -javafx --expand-requires transitive
+JAVADOC_OPTIONS := -use -keywords -notimestamp \
+    -serialwarn -encoding ISO-8859-1 -docencoding UTF-8 -breakiterator \
+    -splitIndex --system none -html5 -javafx --expand-requires transitive
 
 # Should we add DRAFT stamps to the generated javadoc?
 ifeq ($(VERSION_IS_GA), true)