Merge jdk-10+19
authorduke
Thu, 24 Aug 2017 16:34:44 +0200
changeset 46111 14df107500cc
parent 46110 e256651a360a (diff)
parent 46086 1f790472232c (current diff)
child 46112 2aacae7f3bb5
child 46131 2c9e8f34aaf7
child 46134 845978afb5cd
child 46138 5982afcbf9dc
child 46162 9d46bc427b7b
child 46166 46e5f55d9d67
child 46171 7784475da8e2
child 46177 f46dd035db30
child 46181 d2564540ba65
child 46241 3a86e1e929c7
child 46776 6f3aae38bad3
child 47074 f302067f1b7a
Merge
--- a/.hgtags-top-repo	Thu Aug 24 16:34:07 2017 +0200
+++ b/.hgtags-top-repo	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/corba/.hgtags	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/hotspot/.hgtags	Thu Aug 24 16:34:44 2017 +0200
@@ -600,3 +600,4 @@
 9d032191f82fca5ba0aac98682f69c4ff0f1283d jdk-9+178
 d2661aa42bff322badbe6c1337fc638d2e0f5730 jdk-9+179
 73e2cb8700bfa51304bd4b02f224620859a3f600 jdk-10+17
+c9d3317623d48da3327232c81e3f8cfc0d29d888 jdk-10+18
--- a/jaxp/.hgtags	Thu Aug 24 16:34:07 2017 +0200
+++ b/jaxp/.hgtags	Thu Aug 24 16:34:44 2017 +0200
@@ -440,3 +440,4 @@
 0983b2dbe17ba4fed3af34e0512ca77a9845fe8a jdk-9+178
 87243a3131f79e8b3903eaca6b629abc48f08ace jdk-9+179
 97d6f14334cfd766f57c296a5a707c8a709aeff0 jdk-10+17
+7ba7ebbc304a4817e05b72efa6b45ed635839b98 jdk-10+18
--- a/jaxws/.hgtags	Thu Aug 24 16:34:07 2017 +0200
+++ b/jaxws/.hgtags	Thu Aug 24 16:34:44 2017 +0200
@@ -443,3 +443,4 @@
 d0190aaf1816081d9b2e0577b65b793804896d1e jdk-9+178
 56ac1831ac5924b5092a53a85d6fc68749501fb8 jdk-9+179
 4c07d366c2e177edba7aa54c4b015e4dbf12bc83 jdk-10+17
+6859ffbe2c510c930f88983743578d8186cf6dbd jdk-10+18
--- a/jdk/.hgtags	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/.hgtags	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/DESKey.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/DESedeKey.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Class.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Module.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/System.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/module/Configuration.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/module/Resolver.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/net/URL.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Thu Aug 24 16:34:44 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:44 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:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferenceImpl.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ /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:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java	Thu Aug 24 16:34:44 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:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/security/provider/KeyProtector.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketTransportService.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java	Thu Aug 24 16:34:44 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:44 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:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/lang/ClassLoader/getResource/GetResource.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/0.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/1.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/10.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/11.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/12.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/14.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/15.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/2.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/3.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/4.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/5.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/6.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/7.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/8.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/9.policy	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/net/httpclient/security/Driver.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/java/nio/channels/Selector/KeySets.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m4/p4/Main.java	Thu Aug 24 16:34:44 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	Thu Aug 24 16:34:07 2017 +0200
+++ /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:44 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/langtools/.hgtags	Thu Aug 24 16:34:07 2017 +0200
+++ b/langtools/.hgtags	Thu Aug 24 16:34:44 2017 +0200
@@ -440,3 +440,4 @@
 849e366ef175012e6dedc3ca151da416716e0ea9 jdk-9+178
 b653b1b2ea883593596bc18e9af73a9b369eeb0a jdk-9+179
 41028d8c0a71c6beaf1886ca095e703fbb1513ec jdk-10+17
+bc2a67fe6b7fcec3a33e3912e27efec83ec6b52c jdk-10+18
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java	Thu Aug 24 16:34:07 2017 +0200
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java	Thu Aug 24 16:34:44 2017 +0200
@@ -31,7 +31,7 @@
 
 /**
  * A Snippet represents a snippet of Java source code as passed to
- * {@link jdk.jshell.JShell#eval}.  It is associated only with the
+ * {@link jdk.jshell.JShell#eval(java.lang.String)}.  It is associated only with the
  * {@link jdk.jshell.JShell JShell} instance that created it.
  * An instance of Snippet (including its subclasses) is immutable: an access to
  * any of its methods will always return the same result.
@@ -52,7 +52,7 @@
      * It is accessed with {@link jdk.jshell.Snippet#kind()}.
      * The {@code Kind} can be used to determine which
      * subclass of Snippet it is. For example,
-     * {@link jdk.jshell.JShell#eval eval("int three() { return 3; }")} will
+     * {@link jdk.jshell.JShell#eval(java.lang.String) eval("int three() { return 3; }")} will
      * return a snippet creation event.  The {@code Kind} of that Snippet
      * will be {@code METHOD}, from which you know that the subclass
      * of {@code Snippet} is {@code MethodSnippet} and it can be
@@ -178,7 +178,7 @@
     /**
      * The detailed variety of a snippet.  This is a sub-classification of the
      * Kind.  The Kind of a SubKind is accessible with
-     * {@link jdk.jshell.Snippet.SubKind#kind}.
+     * {@link jdk.jshell.Snippet.SubKind#kind()}.
      */
     public enum SubKind {
 
@@ -460,7 +460,7 @@
         /**
          * The snippet is inactive because it has been replaced by a new
          * snippet.  This occurs when the new snippet added with
-         * {@link jdk.jshell.JShell#eval} matches a previous snippet.
+         * {@link jdk.jshell.JShell#eval(java.lang.String) eval} matches a previous snippet.
          * A {@code TypeDeclSnippet} will match another
          * {@code TypeDeclSnippet} if the names match.
          * For example {@code class X { }} will overwrite
--- a/langtools/test/jdk/javadoc/doclet/testModules/TestModuleServicesLink.java	Thu Aug 24 16:34:07 2017 +0200
+++ b/langtools/test/jdk/javadoc/doclet/testModules/TestModuleServicesLink.java	Thu Aug 24 16:34:44 2017 +0200
@@ -25,7 +25,9 @@
  * @test
  * @bug 8185151
  * @summary test that navigation summary links are not linked when there are no dependencies
- * @modules jdk.javadoc/jdk.javadoc.internal.api
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.javadoc/jdk.javadoc.internal.api
  *          jdk.javadoc/jdk.javadoc.internal.tool
  * @library ../lib /tools/lib
  * @build toolbox.ToolBox toolbox.ModuleBuilder JavadocTester
--- a/make/Docs.gmk	Thu Aug 24 16:34:07 2017 +0200
+++ b/make/Docs.gmk	Thu Aug 24 16:34:44 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)
--- a/nashorn/.hgtags	Thu Aug 24 16:34:07 2017 +0200
+++ b/nashorn/.hgtags	Thu Aug 24 16:34:44 2017 +0200
@@ -431,3 +431,4 @@
 7497ad85759ff010f44344b553223d1647fb6eba jdk-9+178
 3adfb547e3e49e304ffc82d8c6489cb830b74d62 jdk-9+179
 6ac0ca441ccb9ccc49c5007248dc1f3af8076a71 jdk-10+17
+e7fbd9c8637ff362e5a1801e3c89c579f23279f7 jdk-10+18