changeset 17621:69eb6d4c5a69

Merge
author mchung
date Thu, 08 Sep 2016 17:42:08 -0700
parents 02d65bf86352 2e46f14e2012
children 6d6b2eb8cfa9
files .hgtags src/java.base/share/classes/java/lang/invoke/MethodHandles.java src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java src/java.base/share/native/libjli/java.c src/java.base/share/native/libjli/java.h src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java src/java.desktop/share/classes/java/awt/Component.java src/java.desktop/share/classes/sun/applet/AppletPanel.java src/java.desktop/share/classes/sun/print/ServiceDialog.java src/java.management/share/classes/javax/management/remote/rmi/RMIConnector.java src/java.management/share/classes/module-info.java src/java.management/share/classes/sun/management/Agent.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties test/ProblemList.txt test/TEST.groups test/com/sun/management/HotSpotDiagnosticMXBean/DumpHeap.java test/java/lang/Class/GetModuleTest.java test/java/lang/ClassLoader/GetSystemPackage.java test/java/lang/instrument/NativeMethodPrefixAgent.java test/java/lang/management/GarbageCollectorMXBean/GcInfoCompositeType.java test/java/net/URLPermission/nstest/lookup.sh test/java/util/regex/PatternStreamTest.java test/javax/management/remote/mandatory/notif/DeadListenerTest.java test/lib/testlibrary/jdk/testlibrary/OutputAnalyzer.java test/lib/testlibrary/jdk/testlibrary/ProcessTools.java test/sun/jvmstat/monitor/MonitoredVm/TestPollingInterval.java test/sun/tools/jmap/BasicJMapTest.java test/sun/tools/jps/TestJpsSanity.java
diffstat 59 files changed, 2568 insertions(+), 815 deletions(-) [+]
line wrap: on
line diff
--- a/.jcheck/conf	Thu Sep 08 21:11:54 2016 +0000
+++ b/.jcheck/conf	Thu Sep 08 17:42:08 2016 -0700
@@ -1,1 +1,4 @@
 project=jdk9
+comments=lax
+tags=lax
+bugids=dup
--- a/make/src/classes/build/tools/module/GenModuleInfoSource.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/make/src/classes/build/tools/module/GenModuleInfoSource.java	Thu Sep 08 17:42:08 2016 -0700
@@ -155,15 +155,25 @@
                 String keyword = s[0].trim();
                 int nextIndex = keyword.length();
                 String exp = null;
+                String modifier = "";
                 int n = l.length();
                 switch (keyword) {
                     case "exports":
                         boolean inExportsTo = false;
-                        // assume package name immediately after exports
-                        exp = s[1].trim();
-                        if (s.length >= 3) {
+                        // exports <package-name> [to <target-module>]
+                        // exports dynamic <package-name> [to <target-module>]
+                        String token = s[1].trim();
+                        int nextTokenPos = 2;
+                        if (token.equals("dynamic") && s.length >= 3) {
+                            modifier = token;
+                            exp = s[2].trim();
+                            nextTokenPos = 3;
+                        } else {
+                            exp = token;
+                        }
+                        if (s.length > nextTokenPos) {
                             nextIndex = l.indexOf(exp, nextIndex) + exp.length();
-                            if (s[2].trim().equals("to")) {
+                            if (s[nextTokenPos].trim().equals("to")) {
                                 inExportsTo = true;
                                 n = l.indexOf("to", nextIndex) + "to".length();
                             } else {
@@ -199,24 +209,24 @@
         }
     }
 
-    private String injectExportTargets(String pn, String exp, int pos) {
+    private String injectExportTargets(String pn, String exports, int pos) {
         Set<String> targets = exportsTo.remove(pn);
         if (targets != null) {
             StringBuilder sb = new StringBuilder();
             // inject the extra targets after the given pos
-            sb.append(exp.substring(0, pos))
+            sb.append(exports.substring(0, pos))
               .append("\n\t")
               .append(targets.stream()
                              .collect(Collectors.joining(",", "", ",")))
               .append(" /* injected */");
-            if (pos < exp.length()) {
+            if (pos < exports.length()) {
                 // print the remaining statement followed "to"
                 sb.append("\n\t")
-                  .append(exp.substring(pos+1, exp.length()));
+                  .append(exports.substring(pos+1, exports.length()));
             }
             return sb.toString();
         } else {
-            return exp;
+            return exports;
         }
     }
 
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/intrinsic.properties	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/com/sun/java/util/jar/pack/intrinsic.properties	Thu Sep 08 17:42:08 2016 -0700
@@ -15,7 +15,7 @@
 pack.class.attribute.CompilationID = RUH
 
 # Module attributes, supported by the tool and not JSR-200
-pack.class.attribute.Module = NH[RUHFH]NH[RUHNH[RUH]]NH[RCH]NH[RCHRCH]
+pack.class.attribute.Module = NH[RUHFH]NH[RUHFHNH[RUH]]NH[RCH]NH[RCHRCH]
 pack.class.attribute.ConcealedPackages = NH[RUH]
 pack.class.attribute.Version = RUH
 pack.class.attribute.MainClass = RUH
--- a/src/java.base/share/classes/java/lang/Class.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/Class.java	Thu Sep 08 17:42:08 2016 -0700
@@ -442,21 +442,10 @@
 
         PrivilegedAction<ClassLoader> pa = module::getClassLoader;
         ClassLoader cl = AccessController.doPrivileged(pa);
-        if (module.isNamed() && cl != null) {
-            return cl.loadLocalClass(module, name);
-        }
-
-        final Class<?> c;
         if (cl != null) {
-            c = cl.loadLocalClass(name);
+            return cl.loadClass(module, name);
         } else {
-            c = BootLoader.loadClassOrNull(name);
-        }
-
-        if (c != null && c.getModule() == module) {
-            return c;
-        } else {
-            return null;
+            return BootLoader.loadClass(module, name);
         }
     }
 
@@ -2368,9 +2357,8 @@
 
     /**
      * Finds a resource with a given name. If this class is in a named {@link
-     * Module Module}, and the caller of this method is in the same module,
-     * then this method will attempt to find the resource in that module.
-     * Otherwise, the rules for searching resources
+     * Module Module} then this method will attempt to find the resource in
+     * that module. Otherwise, the rules for searching resources
      * associated with a given class are implemented by the defining
      * {@linkplain ClassLoader class loader} of the class.  This method
      * delegates to this object's class loader.  If this object was loaded by
@@ -2400,40 +2388,34 @@
      * </ul>
      *
      * @param  name name of the desired resource
-     * @return  A {@link java.io.InputStream} object or {@code null} if
-     *          no resource with this name is found
+     * @return  A {@link java.io.InputStream} object; {@code null} if no
+     *          resource with this name is found or access to the resource is
+     *          denied by the security manager.
      * @throws  NullPointerException If {@code name} is {@code null}
      * @since  1.1
      */
-    @CallerSensitive
     public InputStream getResourceAsStream(String name) {
         name = resolveName(name);
 
-        // if this Class and the caller are in the same named module
-        // then attempt to get an input stream to the resource in the
-        // module
         Module module = getModule();
         if (module.isNamed()) {
-            Class<?> caller = Reflection.getCallerClass();
-            if (caller != null && caller.getModule() == module) {
-                ClassLoader cl = getClassLoader0();
-                String mn = module.getName();
-                try {
-
-                    // special-case built-in class loaders to avoid the
-                    // need for a URL connection
-                    if (cl == null) {
-                        return BootLoader.findResourceAsStream(mn, name);
-                    } else if (cl instanceof BuiltinClassLoader) {
-                        return ((BuiltinClassLoader) cl).findResourceAsStream(mn, name);
-                    } else {
-                        URL url = cl.findResource(mn, name);
-                        return (url != null) ? url.openStream() : null;
-                    }
-
-                } catch (IOException | SecurityException e) {
-                    return null;
+            String mn = module.getName();
+            ClassLoader cl = getClassLoader0();
+            try {
+
+                // special-case built-in class loaders to avoid the
+                // need for a URL connection
+                if (cl == null) {
+                    return BootLoader.findResourceAsStream(mn, name);
+                } else if (cl instanceof BuiltinClassLoader) {
+                    return ((BuiltinClassLoader) cl).findResourceAsStream(mn, name);
+                } else {
+                    URL url = cl.findResource(mn, name);
+                    return (url != null) ? url.openStream() : null;
                 }
+
+            } catch (IOException | SecurityException e) {
+                return null;
             }
         }
 
@@ -2448,9 +2430,8 @@
 
     /**
      * Finds a resource with a given name. If this class is in a named {@link
-     * Module Module}, and the caller of this method is in the same module,
-     * then this method will attempt to find the resource in that module.
-     * Otherwise, the rules for searching resources
+     * Module Module} then this method will attempt to find the resource in
+     * that module. Otherwise, the rules for searching resources
      * associated with a given class are implemented by the defining
      * {@linkplain ClassLoader class loader} of the class.  This method
      * delegates to this object's class loader. If this object was loaded by
@@ -2479,32 +2460,26 @@
      * </ul>
      *
      * @param  name name of the desired resource
-     * @return A {@link java.net.URL} object; {@code null} if no
-     *         resource with this name is found or the resource cannot
-     *         be located by a URL.
+     * @return A {@link java.net.URL} object; {@code null} if no resource with
+     *         this name is found, the resource cannot be located by a URL, or
+     *         access to the resource is denied by the security manager.
      * @since  1.1
      */
-    @CallerSensitive
     public URL getResource(String name) {
         name = resolveName(name);
 
-        // if this Class and the caller are in the same named module
-        // then attempt to get URL to the resource in the module
         Module module = getModule();
         if (module.isNamed()) {
-            Class<?> caller = Reflection.getCallerClass();
-            if (caller != null && caller.getModule() == module) {
-                String mn = getModule().getName();
-                ClassLoader cl = getClassLoader0();
-                try {
-                    if (cl == null) {
-                        return BootLoader.findResource(mn, name);
-                    } else {
-                        return cl.findResource(mn, name);
-                    }
-                } catch (IOException ioe) {
-                    return null;
+            String mn = getModule().getName();
+            ClassLoader cl = getClassLoader0();
+            try {
+                if (cl == null) {
+                    return BootLoader.findResource(mn, name);
+                } else {
+                    return cl.findResource(mn, name);
                 }
+            } catch (IOException ioe) {
+                return null;
             }
         }
 
--- a/src/java.base/share/classes/java/lang/ClassLoader.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/ClassLoader.java	Thu Sep 08 17:42:08 2016 -0700
@@ -523,7 +523,7 @@
      * @return The resulting {@code Class} object in a module defined by
      *         this class loader, or {@code null} if the class could not be found.
      */
-    final Class<?> loadLocalClass(Module module, String name) {
+    final Class<?> loadClass(Module module, String name) {
         synchronized (getClassLoadingLock(name)) {
             // First, check if the class has already been loaded
             Class<?> c = findLoadedClass(name);
@@ -539,34 +539,6 @@
     }
 
     /**
-     * Loads the class with the specified <a href="#name">binary name</a>
-     * defined by this class loader.  This method returns {@code null}
-     * if the class could not be found.
-     *
-     * @apiNote This method does not delegate to the parent class loader.
-     *
-     * @param  name
-     *         The <a href="#name">binary name</a> of the class
-     *
-     * @return The resulting {@code Class} object in a module defined by
-     *         this class loader, or {@code null} if the class could not be found.
-     */
-    final Class<?> loadLocalClass(String name) {
-        synchronized (getClassLoadingLock(name)) {
-            // First, check if the class has already been loaded
-            Class<?> c = findLoadedClass(name);
-            if (c == null) {
-                try {
-                    return findClass(name);
-                } catch (ClassNotFoundException e) {
-                    // ignore
-                }
-            }
-            return c;
-        }
-    }
-
-    /**
      * Returns the lock object for class loading operations.
      * For backward compatibility, the default implementation of this method
      * behaves as follows. If this ClassLoader object is registered as
@@ -667,12 +639,17 @@
      * should override this method.
      *
      * @apiNote This method returns {@code null} rather than throwing
-     *          {@code ClassNotFoundException} if the class could not be found
+     *          {@code ClassNotFoundException} if the class could not be found.
      *
-     * @implSpec The default implementation returns {@code null}.
+     * @implSpec The default implementation attempts to find the class by
+     * invoking {@link #findClass(String)} when the {@code moduleName} is
+     * {@code null}. It otherwise returns {@code null}.
      *
      * @param  moduleName
-     *         The module name
+     *         The module name; or {@code null} to find the class in the
+     *         {@linkplain #getUnnamedModule() unnamed module} for this
+     *         class loader
+
      * @param  name
      *         The <a href="#name">binary name</a> of the class
      *
@@ -682,6 +659,11 @@
      * @since 9
      */
     protected Class<?> findClass(String moduleName, String name) {
+        if (moduleName == null) {
+            try {
+                return findClass(name);
+            } catch (ClassNotFoundException ignore) { }
+        }
         return null;
     }
 
@@ -1239,10 +1221,14 @@
      * Class loader implementations that support the loading from modules
      * should override this method.
      *
-     * @implSpec The default implementation returns {@code null}.
+     * @implSpec The default implementation attempts to find the resource by
+     * invoking {@link #findResource(String)} when the {@code moduleName} is
+     * {@code null}. It otherwise returns {@code null}.
      *
      * @param  moduleName
-     *         The module name
+     *         The module name; or {@code null} to find a resource in the
+     *         {@linkplain #getUnnamedModule() unnamed module} for this
+     *         class loader
      * @param  name
      *         The resource name
      *
@@ -1259,7 +1245,11 @@
      * @since 9
      */
     protected URL findResource(String moduleName, String name) throws IOException {
-        return null;
+        if (moduleName == null) {
+            return findResource(name);
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -1267,9 +1257,6 @@
      * (images, audio, text, etc) that can be accessed by class code in a way
      * that is independent of the location of the code.
      *
-     * Resources in a named module are private to that module. This method does
-     * not find resource in named modules.
-     *
      * <p> The name of a resource is a '<tt>/</tt>'-separated path name that
      * identifies the resource.
      *
@@ -1278,16 +1265,21 @@
      * built-in to the virtual machine is searched.  That failing, this method
      * will invoke {@link #findResource(String)} to find the resource.  </p>
      *
-     * @apiNote When overriding this method it is recommended that an
-     * implementation ensures that any delegation is consistent with the {@link
+     * @apiNote Where several modules are defined to the same class loader,
+     * and where more than one module contains a resource with the given name,
+     * then the ordering that modules are searched is not specified and may be
+     * very unpredictable.
+     * When overriding this method it is recommended that an implementation
+     * ensures that any delegation is consistent with the {@link
      * #getResources(java.lang.String) getResources(String)} method.
      *
      * @param  name
      *         The resource name
      *
-     * @return  A <tt>URL</tt> object for reading the resource, or
-     *          <tt>null</tt> if the resource could not be found or the invoker
-     *          doesn't have adequate  privileges to get the resource.
+     * @return  {@code URL} object for reading the resource; {@code null} if
+     *          the resource could not be found, a {@code URL} could not be
+     *          constructed to locate the resource, or access to the resource
+     *          is denied by the security manager.
      *
      * @since  1.1
      */
@@ -1309,16 +1301,16 @@
      * (images, audio, text, etc) that can be accessed by class code in a way
      * that is independent of the location of the code.
      *
-     * Resources in a named module are private to that module. This method does
-     * not find resources in named modules.
-     *
-     * <p>The name of a resource is a <tt>/</tt>-separated path name that
+     * <p> The name of a resource is a <tt>/</tt>-separated path name that
      * identifies the resource.
      *
-     * <p> The search order is described in the documentation for {@link
-     * #getResource(String)}.  </p>
+     * <p> The delegation order for searching is described in the documentation
+     * for {@link #getResource(String)}.  </p>
      *
-     * @apiNote When overriding this method it is recommended that an
+     * @apiNote Where several modules are defined to the same class loader,
+     * and where more than one module contains a resource with the given name,
+     * then the ordering is not specified and may be very unpredictable.
+     * When overriding this method it is recommended that an
      * implementation ensures that any delegation is consistent with the {@link
      * #getResource(java.lang.String) getResource(String)} method. This should
      * ensure that the first element returned by the Enumeration's
@@ -1329,9 +1321,10 @@
      *         The resource name
      *
      * @return  An enumeration of {@link java.net.URL <tt>URL</tt>} objects for
-     *          the resource.  If no resources could  be found, the enumeration
-     *          will be empty.  Resources that the class loader doesn't have
-     *          access to will not be in the enumeration.
+     *          the resource. If no resources could  be found, the enumeration
+     *          will be empty. Resources for which a {@code URL} cannot be
+     *          constructed, or where access to the resource is denied by the
+     *          security manager, are not returned in the enumeration.
      *
      * @throws  IOException
      *          If I/O errors occur
@@ -1357,9 +1350,6 @@
      * Finds the resource with the given name. Class loader implementations
      * should override this method to specify where to find resources.
      *
-     * Resources in a named module are private to that module. This method does
-     * not find resources in named modules defined to this class loader.
-     *
      * @param  name
      *         The resource name
      *
@@ -1378,9 +1368,6 @@
      * implementations should override this method to specify where to load
      * resources from.
      *
-     * Resources in a named module are private to that module. This method does
-     * not find resources in named modules defined to this class loader.
-     *
      * @param  name
      *         The resource name
      *
@@ -1393,7 +1380,7 @@
      * @since  1.2
      */
     protected Enumeration<URL> findResources(String name) throws IOException {
-        return java.util.Collections.emptyEnumeration();
+        return Collections.emptyEnumeration();
     }
 
     /**
@@ -1425,14 +1412,13 @@
      * classes.  This method locates the resource through the system class
      * loader (see {@link #getSystemClassLoader()}).
      *
-     * Resources in a named module are private to that module. This method does
-     * not find resources in named modules.
-     *
      * @param  name
      *         The resource name
      *
-     * @return  A {@link java.net.URL <tt>URL</tt>} object for reading the
-     *          resource, or <tt>null</tt> if the resource could not be found
+     * @return  A {@link java.net.URL <tt>URL</tt>} to the resource; {@code
+     *          null} if the resource could not be found, a URL could not be
+     *          constructed to locate the resource, or access to the resource
+     *          is denied by the security manager.
      *
      * @since  1.1
      */
@@ -1450,17 +1436,17 @@
      * {@link java.util.Enumeration <tt>Enumeration</tt>} of {@link
      * java.net.URL <tt>URL</tt>} objects.
      *
-     * Resources in a named module are private to that module. This method does
-     * not find resources in named modules.
-     *
      * <p> The search order is described in the documentation for {@link
      * #getSystemResource(String)}.  </p>
      *
      * @param  name
      *         The resource name
      *
-     * @return  An enumeration of resource {@link java.net.URL <tt>URL</tt>}
-     *          objects
+     * @return  An enumeration of {@link java.net.URL <tt>URL</tt>} objects for
+     *          the resource. If no resources could be found, the enumeration
+     *          will be empty. Resources for which a {@code URL} cannot be
+     *          constructed, or where access to the resource is denied by the
+     *          security manager, are not returned in the enumeration.
      *
      * @throws  IOException
      *          If I/O errors occur
@@ -1480,17 +1466,15 @@
     /**
      * Returns an input stream for reading the specified resource.
      *
-     * Resources in a named module are private to that module. This method does
-     * not find resources in named modules.
-     *
      * <p> The search order is described in the documentation for {@link
      * #getResource(String)}.  </p>
      *
      * @param  name
      *         The resource name
      *
-     * @return  An input stream for reading the resource, or <tt>null</tt>
-     *          if the resource could not be found
+     * @return  An input stream for reading the resource; {@code null}
+     *          if the resource could not be found or access to the resource
+     *          is denied by the security manager.
      *
      * @since  1.1
      */
@@ -1508,14 +1492,12 @@
      * used to load classes.  This method locates the resource through the
      * system class loader (see {@link #getSystemClassLoader()}).
      *
-     * Resources in a named module are private to that module. This method does
-     * not find resources in named modules.
-     *
      * @param  name
      *         The resource name
      *
-     * @return  An input stream for reading the resource, or <tt>null</tt>
-     *          if the resource could not be found
+     * @return  An input stream for reading the resource; {@code null}
+     *          if the resource could not be found or access to the resource
+     *          is denied by the security manager.
      *
      * @since  1.1
      */
--- a/src/java.base/share/classes/java/lang/Deprecated.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/Deprecated.java	Thu Sep 08 17:42:08 2016 -0700
@@ -40,6 +40,11 @@
  * annotation on a local variable declaration or on a parameter declaration
  * or a package declaration has no effect on the warnings issued by a compiler.
  *
+ * <p>When a module is deprecated, the use of that module in {@code
+ * requires} or {@code exports} clauses causes a warning to be
+ * issued. A module being deprecated does <em>not</em> cause warnings
+ * to be issued for uses of types within the module.
+ *
  * <p>This annotation type has a string-valued element {@code since}. The value
  * of this element indicates the version in which the annotated program element
  * was first deprecated.
@@ -74,7 +79,7 @@
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
-@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
+@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
 public @interface Deprecated {
     /**
      * Returns the version in which the annotated element became deprecated.
--- a/src/java.base/share/classes/java/lang/Package.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/Package.java	Thu Sep 08 17:42:08 2016 -0700
@@ -395,10 +395,16 @@
         if (packageInfo == null) {
             // find package-info.class defined by loader
             String cn = packageName() + ".package-info";
-            PrivilegedAction<ClassLoader> pa = module()::getClassLoader;
+            Module module = module();
+            PrivilegedAction<ClassLoader> pa = module::getClassLoader;
             ClassLoader loader = AccessController.doPrivileged(pa);
-            Class<?> c = loader != null ? loader.loadLocalClass(cn)
-                                        : BootLoader.loadClassOrNull(cn);
+            Class<?> c;
+            if (loader != null) {
+                c = loader.loadClass(module, cn);
+            } else {
+                c = BootLoader.loadClass(module, cn);
+            }
+
             if (c != null) {
                 packageInfo = c;
             } else {
--- a/src/java.base/share/classes/java/lang/SuppressWarnings.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/SuppressWarnings.java	Thu Sep 08 17:42:08 2016 -0700
@@ -35,6 +35,9 @@
  * a superset of the warnings suppressed in all containing elements.  For
  * example, if you annotate a class to suppress one warning and annotate a
  * method to suppress another, both warnings will be suppressed in the method.
+ * However, note that if a warning is suppressed in a {@code
+ * module-info} file, the suppression applies to element within the
+ * file and <em>not</em> to types contained within the module.
  *
  * <p>As a matter of style, programmers should always use this annotation
  * on the most deeply nested element where it is effective.  If you want to
@@ -49,7 +52,7 @@
  * @jls 5.5.2 Checked Casts and Unchecked Casts
  * @jls 9.6.4.5 @SuppressWarnings
  */
-@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
+@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
 @Retention(RetentionPolicy.SOURCE)
 public @interface SuppressWarnings {
     /**
--- a/src/java.base/share/classes/java/lang/annotation/ElementType.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/annotation/ElementType.java	Thu Sep 08 17:42:08 2016 -0700
@@ -37,10 +37,10 @@
  * <em>type contexts</em> , where annotations apply to types used in
  * declarations and expressions.
  *
- * <p>The constants {@link #ANNOTATION_TYPE} , {@link #CONSTRUCTOR} , {@link
- * #FIELD} , {@link #LOCAL_VARIABLE} , {@link #METHOD} , {@link #PACKAGE} ,
- * {@link #PARAMETER} , {@link #TYPE} , and {@link #TYPE_PARAMETER} correspond
- * to the declaration contexts in JLS 9.6.4.1.
+ * <p>The constants {@link #ANNOTATION_TYPE}, {@link #CONSTRUCTOR}, {@link
+ * #FIELD}, {@link #LOCAL_VARIABLE}, {@link #METHOD}, {@link #PACKAGE}, {@link
+ * #MODULE}, {@link #PARAMETER}, {@link #TYPE}, and {@link #TYPE_PARAMETER}
+ * correspond to the declaration contexts in JLS 9.6.4.1.
  *
  * <p>For example, an annotation whose type is meta-annotated with
  * {@code @Target(ElementType.FIELD)} may only be written as a modifier for a
@@ -107,5 +107,12 @@
      *
      * @since 1.8
      */
-    TYPE_USE
+    TYPE_USE,
+
+    /**
+     * Module declaration.
+     *
+     * @since 9
+     */
+    MODULE
 }
--- a/src/java.base/share/classes/java/lang/module/Dependence.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/Dependence.java	Thu Sep 08 17:42:08 2016 -0700
@@ -25,8 +25,9 @@
 
 package java.lang.module;
 
-import java.util.*;
-import java.util.stream.*;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 
 class Dependence {
--- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Thu Sep 08 17:42:08 2016 -0700
@@ -97,6 +97,12 @@
             PUBLIC,
 
             /**
+             * The dependence is mandatory in the static phase, during compilation,
+             * but is optional in the dynamic phase, during execution.
+             */
+            STATIC,
+
+            /**
              * The dependence was not explicitly or implicitly declared in the
              * source of the module declaration.
              */
@@ -114,16 +120,15 @@
         private final String name;
 
         private Requires(Set<Modifier> ms, String mn) {
-            this(ms, mn, true);
+            this.mods = ms.isEmpty()
+                    ? Collections.emptySet()
+                    : Collections.unmodifiableSet(EnumSet.copyOf(ms));
+            this.name = requireModuleName(mn);
         }
-        private Requires(Set<Modifier> ms, String mn, boolean check) {
-            if (ms == null || ms.isEmpty()) {
-                mods = Collections.emptySet();
-            } else {
-                mods = check ? Collections.unmodifiableSet(EnumSet.copyOf(ms))
-                             : ms;
-            }
-            this.name = check ? requireModuleName(mn) : mn;
+
+        private Requires(Set<Modifier> ms, String mn, boolean unused) {
+            this.mods = ms;
+            this.name = mn;
         }
 
         /**
@@ -238,39 +243,92 @@
 
     public final static class Exports {
 
+        /**
+         * A modifier on a module export.
+         *
+         * @since 9
+         */
+        public static enum Modifier {
+
+            /**
+             * The package is concealed in the static phase, during compilation,
+             * but is exported in the dynamic phase, during execution.
+             */
+            DYNAMIC,
+
+            /**
+             * The export was not explicitly or implicitly declared in the
+             * source of the module declaration.
+             */
+            SYNTHETIC,
+
+            /**
+             * The export was implicitly declared in the source of the module
+             * declaration.
+             */
+            MANDATED;
+
+        }
+
+        private final Set<Modifier> mods;
         private final String source;
         private final Set<String> targets;  // empty if unqualified export
 
         /**
          * Constructs a qualified export.
          */
-        private Exports(String source, Set<String> targets) {
-            this(source, targets, true);
+        private Exports(Set<Modifier> ms, String source, Set<String> targets) {
+            if (ms.isEmpty()) {
+                this.mods = Collections.emptySet();
+            } else {
+                this.mods = Collections.unmodifiableSet(EnumSet.copyOf(ms));
+            }
+            this.source = requirePackageName(source);
+            if (targets.isEmpty()) {
+                throw new IllegalArgumentException("Empty target set");
+            }
+            targets.stream().forEach(Checks::requireModuleName);
+            this.targets = Collections.unmodifiableSet(new HashSet<>(targets));
         }
 
-        private Exports(String source, Set<String> targets, boolean check) {
-            this.source = check ? requirePackageName(source) : source;
-            targets = check ? Collections.unmodifiableSet(new HashSet<>(targets))
-                            : Collections.unmodifiableSet(targets);
-            if (targets.isEmpty())
-                throw new IllegalArgumentException("Empty target set");
-            if (check)
-                targets.stream().forEach(Checks::requireModuleName);
+        private Exports(Set<Modifier> ms,
+                        String source,
+                        Set<String> targets,
+                        boolean unused) {
+            this.mods = ms;
+            this.source = source;
             this.targets = targets;
         }
 
         /**
          * Constructs an unqualified export.
          */
-        private Exports(String source) {
-            this(source, true);
+        private Exports(Set<Modifier> ms, String source) {
+            if (ms.isEmpty()) {
+                this.mods = Collections.emptySet();
+            } else {
+                this.mods = Collections.unmodifiableSet(EnumSet.copyOf(ms));
+            }
+            this.source = requirePackageName(source);
+            this.targets = Collections.emptySet();
         }
-        private Exports(String source, boolean check) {
-            this.source = check ? requirePackageName(source) : source;
+
+        private Exports(Set<Modifier> ms, String source, boolean unused) {
+            this.mods = ms;
+            this.source = source;
             this.targets = Collections.emptySet();
         }
 
         /**
+         * Returns the set of modifiers.
+         *
+         * @return A possibly-empty unmodifiable set of modifiers
+         */
+        public Set<Modifier> modifiers() {
+            return mods;
+        }
+
+        /**
          * Returns {@code true} if this is a qualified export.
          *
          * @return {@code true} if this is a qualified export
@@ -348,10 +406,11 @@
          */
         @Override
         public String toString() {
+            String s = Dependence.toString(mods, source);
             if (targets.isEmpty())
-                return source;
+                return s;
             else
-                return source + " to " + targets;
+                return s + " to " + targets;
         }
 
     }
@@ -371,18 +430,16 @@
         private final Set<String> providers;
 
         private Provides(String service, Set<String> providers) {
-            this(service, providers, true);
+            this.service = requireServiceTypeName(service);
+            if (providers.isEmpty())
+                throw new IllegalArgumentException("Empty providers set");
+            providers.forEach(Checks::requireServiceProviderName);
+            this.providers =
+                Collections.unmodifiableSet(new LinkedHashSet<>(providers));
         }
 
-        private Provides(String service, Set<String> providers, boolean check) {
-            this.service = check ? requireServiceTypeName(service) : service;
-            providers = check
-                ? Collections.unmodifiableSet(new LinkedHashSet<>(providers))
-                : Collections.unmodifiableSet(providers);
-            if (providers.isEmpty())
-                throw new IllegalArgumentException("Empty providers set");
-            if (check)
-                providers.forEach(Checks::requireServiceProviderName);
+        private Provides(String service, Set<String> providers, boolean unused) {
+            this.service = service;
             this.providers = providers;
         }
 
@@ -865,8 +922,8 @@
     }
 
     /**
-     * Creates a module descriptor from its components. This method is intended
-     * for use by the jlink plugin.
+     * Creates a module descriptor from its components.
+     * The arguments are pre-validated and sets are unmodifiable sets.
      */
     ModuleDescriptor(String name,
                      boolean automatic,
@@ -885,11 +942,11 @@
         this.name = name;
         this.automatic = automatic;
         this.synthetic = synthetic;
-        this.requires = Collections.unmodifiableSet(requires);
-        this.exports = Collections.unmodifiableSet(exports);
-        this.uses = Collections.unmodifiableSet(uses);
-        this.provides = Collections.unmodifiableMap(provides);
-        this.packages = Collections.unmodifiableSet(packages);
+        this.requires = requires;
+        this.exports = exports;
+        this.uses = uses;
+        this.provides = provides;
+        this.packages = packages;
 
         this.version = version;
         this.mainClass = mainClass;
@@ -1086,8 +1143,7 @@
      *         .build();
      * }</pre>
      *
-     * @apiNote A {@code Builder} cannot be used to create an {@link
-     * ModuleDescriptor#isAutomatic() automatic} or a {@link
+     * @apiNote A {@code Builder} cannot be used to create a {@link
      * ModuleDescriptor#isSynthetic() synthetic} module.
      *
      * @since 9
@@ -1130,7 +1186,7 @@
          *
          * @see ModuleDescriptor#isAutomatic()
          */
-        /* package */ Builder automatic() {
+        public Builder automatic() {
             this.automatic = true;
             return this;
         }
@@ -1207,26 +1263,6 @@
             return requires(EnumSet.noneOf(Requires.Modifier.class), mn);
         }
 
-        /**
-         * Adds a dependence on a module with the given modifier.
-         *
-         * @param  mod
-         *         The modifier
-         * @param  mn
-         *         The module name
-         *
-         * @return This builder
-         *
-         * @throws IllegalArgumentException
-         *         If the module name is {@code null}, is not a legal Java
-         *         identifier, or is equal to the module name that this builder
-         *         was initialized to build
-         * @throws IllegalStateException
-         *         If the dependence on the module has already been declared
-         */
-        public Builder requires(Requires.Modifier mod, String mn) {
-            return requires(EnumSet.of(mod), mn);
-        }
 
         /**
          * Adds a service dependence.
@@ -1283,6 +1319,61 @@
         }
 
         /**
+         * Adds an export, with the given (and possibly empty) set of modifiers,
+         * to a set of target modules.
+         *
+         * @param  mods
+         *         The set of modifiers
+         * @param  pn
+         *         The package name
+         * @param  targets
+         *         The set of target modules names
+         *
+         * @return This builder
+         *
+         * @throws IllegalArgumentException
+         *         If the package name or any of the target modules is {@code
+         *         null} or is not a legal Java identifier, or the set of
+         *         targets is empty
+         * @throws IllegalStateException
+         *         If the package is already declared as an exported or
+         *         concealed package
+         */
+        public Builder exports(Set<Exports.Modifier> mods,
+                               String pn,
+                               Set<String> targets)
+        {
+            ensureNotExportedOrConcealed(pn);
+            exports.put(pn, new Exports(mods, pn, targets));
+            return this;
+        }
+
+        /**
+         * Adds an export with the given (and possibly empty) set of modifiers.
+         *
+         * @param  mods
+         *         The set of modifiers
+         * @param  pn
+         *         The package name
+         *
+         * @return This builder
+         *
+         * @throws IllegalArgumentException
+         *         If the package name is {@code null} or is not a legal Java
+         *         identifier
+         * @throws IllegalStateException
+         *         If the package is already declared as an exported or
+         *         concealed package
+         */
+        public Builder exports(Set<Exports.Modifier> mods,
+                               String pn)
+        {
+            ensureNotExportedOrConcealed(pn);
+            exports.put(pn, new Exports(mods, pn));
+            return this;
+        }
+
+        /**
          * Adds an export to a set of target modules.
          *
          * @param  pn
@@ -1302,7 +1393,8 @@
          */
         public Builder exports(String pn, Set<String> targets) {
             ensureNotExportedOrConcealed(pn);
-            exports.put(pn, new Exports(pn, targets)); // checks pn and targets
+            // checks package name and targets
+            exports.put(pn, new Exports(Collections.emptySet(), pn, targets));
             return this;
         }
 
@@ -1344,7 +1436,7 @@
          */
         public Builder exports(String pn) {
             ensureNotExportedOrConcealed(pn);
-            exports.put(pn, new Exports(pn)); // checks pn
+            exports.put(pn, new Exports(Collections.emptySet(), pn));
             return this;
         }
 
@@ -1917,22 +2009,25 @@
 
                 @Override
                 public Requires newRequires(Set<Requires.Modifier> ms, String mn) {
-                    return new Requires(ms, mn, false);
+                    return new Requires(ms, mn, true);
                 }
 
                 @Override
-                public Exports newExports(String source, Set<String> targets) {
-                    return new Exports(source, targets, false);
+                public Exports newExports(Set<Exports.Modifier> ms,
+                                          String source,
+                                          Set<String> targets) {
+                    return new Exports(ms, source, targets, true);
                 }
 
                 @Override
-                public Exports newExports(String source) {
-                    return new Exports(source, false);
+                public Exports newExports(Set<Exports.Modifier> ms,
+                                          String source) {
+                    return new Exports(ms, source, true);
                 }
 
                 @Override
                 public Provides newProvides(String service, Set<String> providers) {
-                    return new Provides(service, providers, false);
+                    return new Provides(service, providers, true);
                 }
 
                 @Override
--- a/src/java.base/share/classes/java/lang/module/ModuleInfo.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/ModuleInfo.java	Thu Sep 08 17:42:08 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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
@@ -31,7 +31,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UncheckedIOException;
-import java.lang.module.ModuleDescriptor.Requires.Modifier;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.lang.module.ModuleDescriptor.Exports;
 import java.nio.ByteBuffer;
 import java.nio.BufferUnderflowException;
 import java.util.Collections;
@@ -281,17 +282,19 @@
             int index = in.readUnsignedShort();
             int flags = in.readUnsignedShort();
             String dn = cpool.getUtf8(index);
-            Set<Modifier> mods;
+            Set<Requires.Modifier> mods;
             if (flags == 0) {
                 mods = Collections.emptySet();
             } else {
                 mods = new HashSet<>();
-                if ((flags & ACC_PUBLIC) != 0)
-                    mods.add(Modifier.PUBLIC);
+                if ((flags & ACC_TRANSITIVE) != 0)
+                    mods.add(Requires.Modifier.PUBLIC);
+                if ((flags & ACC_STATIC_PHASE) != 0)
+                    mods.add(Requires.Modifier.STATIC);
                 if ((flags & ACC_SYNTHETIC) != 0)
-                    mods.add(Modifier.SYNTHETIC);
+                    mods.add(Requires.Modifier.SYNTHETIC);
                 if ((flags & ACC_MANDATED) != 0)
-                    mods.add(Modifier.MANDATED);
+                    mods.add(Requires.Modifier.MANDATED);
             }
             builder.requires(mods, dn);
             if (dn.equals("java.base"))
@@ -312,6 +315,21 @@
             for (int i=0; i<exports_count; i++) {
                 int index = in.readUnsignedShort();
                 String pkg = cpool.getUtf8(index).replace('/', '.');
+
+                Set<Exports.Modifier> mods;
+                int flags = in.readUnsignedShort();
+                if (flags == 0) {
+                    mods = Collections.emptySet();
+                } else {
+                    mods = new HashSet<>();
+                    if ((flags & ACC_DYNAMIC_PHASE) != 0)
+                        mods.add(Exports.Modifier.DYNAMIC);
+                    if ((flags & ACC_SYNTHETIC) != 0)
+                        mods.add(Exports.Modifier.SYNTHETIC);
+                    if ((flags & ACC_MANDATED) != 0)
+                        mods.add(Exports.Modifier.MANDATED);
+                }
+
                 int exports_to_count = in.readUnsignedShort();
                 if (exports_to_count > 0) {
                     Set<String> targets = new HashSet<>(exports_to_count);
@@ -319,9 +337,9 @@
                         int exports_to_index = in.readUnsignedShort();
                         targets.add(cpool.getUtf8(exports_to_index));
                     }
-                    builder.exports(pkg, targets);
+                    builder.exports(mods, pkg, targets);
                 } else {
-                    builder.exports(pkg);
+                    builder.exports(mods, pkg);
                 }
             }
         }
@@ -477,8 +495,6 @@
                     "LineNumberTable",
                     "LocalVariableTable",
                     "LocalVariableTypeTable",
-                    "RuntimeVisibleAnnotations",
-                    "RuntimeInvisibleAnnotations",
                     "RuntimeVisibleParameterAnnotations",
                     "RuntimeInvisibleParameterAnnotations",
                     "RuntimeVisibleTypeAnnotations",
--- a/src/java.base/share/classes/java/lang/module/Resolver.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/Resolver.java	Thu Sep 08 17:42:08 2016 -0700
@@ -125,6 +125,11 @@
 
             // process dependences
             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
+
+                // only required at compile-time
+                if (requires.modifiers().contains(Modifier.STATIC))
+                    continue;
+
                 String dn = requires.name();
 
                 // find dependence
@@ -519,6 +524,7 @@
                     // parent configuration
                     m2 = parent.findModule(dn).orElse(null);
                     if (m2 == null) {
+                        assert requires.modifiers().contains(Modifier.STATIC);
                         continue;
                     }
                 }
--- a/src/java.base/share/classes/java/lang/reflect/Layer.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/reflect/Layer.java	Thu Sep 08 17:42:08 2016 -0700
@@ -27,7 +27,6 @@
 
 import java.lang.module.Configuration;
 import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleDescriptor.Provides;
 import java.lang.module.ResolvedModule;
 import java.util.Collections;
 import java.util.HashMap;
@@ -43,7 +42,6 @@
 import jdk.internal.loader.LoaderPool;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.module.ServicesCatalog;
-import jdk.internal.module.ServicesCatalog.ServiceProvider;
 import sun.security.util.SecurityConstants;
 
 
@@ -572,39 +570,12 @@
         if (servicesCatalog != null)
             return servicesCatalog;
 
-        Map<String, Set<ServiceProvider>> map = new HashMap<>();
-        for (Module m : nameToModule.values()) {
-            ModuleDescriptor descriptor = m.getDescriptor();
-            for (Provides provides : descriptor.provides().values()) {
-                String service = provides.service();
-                Set<ServiceProvider> providers
-                    = map.computeIfAbsent(service, k -> new HashSet<>());
-                for (String pn : provides.providers()) {
-                    providers.add(new ServiceProvider(m, pn));
-                }
-            }
-        }
-
-        ServicesCatalog catalog = new ServicesCatalog() {
-            @Override
-            public void register(Module module) {
-                throw new UnsupportedOperationException();
-            }
-            @Override
-            public Set<ServiceProvider> findServices(String service) {
-                Set<ServiceProvider> providers = map.get(service);
-                if (providers == null) {
-                    return Collections.emptySet();
-                } else {
-                    return Collections.unmodifiableSet(providers);
-                }
-            }
-        };
-
         synchronized (this) {
             servicesCatalog = this.servicesCatalog;
             if (servicesCatalog == null) {
-                this.servicesCatalog = servicesCatalog = catalog;
+                servicesCatalog = ServicesCatalog.create();
+                nameToModule.values().forEach(servicesCatalog::register);
+                this.servicesCatalog = servicesCatalog;
             }
         }
 
--- a/src/java.base/share/classes/java/lang/reflect/Module.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/lang/reflect/Module.java	Thu Sep 08 17:42:08 2016 -0700
@@ -27,6 +27,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.annotation.Annotation;
 import java.lang.module.Configuration;
 import java.lang.module.ModuleReference;
 import java.lang.module.ModuleDescriptor;
@@ -36,6 +37,8 @@
 import java.lang.module.ResolvedModule;
 import java.net.URI;
 import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -49,9 +52,16 @@
 
 import jdk.internal.loader.BuiltinClassLoader;
 import jdk.internal.loader.BootLoader;
+import jdk.internal.misc.JavaLangAccess;
 import jdk.internal.misc.JavaLangReflectModuleAccess;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.module.ServicesCatalog;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Opcodes;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;
 import sun.security.util.SecurityConstants;
@@ -83,7 +93,7 @@
  * @see java.lang.Class#getModule
  */
 
-public final class Module {
+public final class Module implements AnnotatedElement {
 
     // the layer that contains this module, can be null
     private final Layer layer;
@@ -252,12 +262,11 @@
 
     // -- readability --
 
-    // the modules that this module permanently reads
-    // (will be final when the modules are defined in reverse topology order)
+    // 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> transientReads
+    private static final WeakPairMap<Module, Module, Boolean> reflectivelyReads
         = new WeakPairMap<>();
 
 
@@ -293,13 +302,13 @@
         }
 
         // check if this module reads the other module reflectively
-        if (transientReads.containsKeyPair(this, other))
+        if (reflectivelyReads.containsKeyPair(this, other))
             return true;
 
         // if other is an unnamed module then check if this module reads
         // all unnamed modules
         if (!other.isNamed()
-            && transientReads.containsKeyPair(this, ALL_UNNAMED_MODULE))
+            && reflectivelyReads.containsKeyPair(this, ALL_UNNAMED_MODULE))
             return true;
 
         return false;
@@ -381,20 +390,19 @@
         }
 
         // add reflective read
-        transientReads.putIfAbsent(this, other, Boolean.TRUE);
+        reflectivelyReads.putIfAbsent(this, other, Boolean.TRUE);
     }
 
 
     // -- exports --
 
-    // the packages that are permanently exported
-    // (will be final when the modules are defined in reverse topology order)
+    // the packages that are exported
     private volatile Map<String, Set<Module>> exports;
 
     // additional exports added at run-time
     // this module (1st key), other module (2nd key), exported packages (value)
     private static final WeakPairMap<Module, Module, Map<String, Boolean>>
-        transientExports = new WeakPairMap<>();
+        reflectivelyExports = new WeakPairMap<>();
 
 
     /**
@@ -451,7 +459,7 @@
             return true;
 
         // exported via module declaration/descriptor
-        if (isExportedPermanently(pn, other))
+        if (isExportedStatically(pn, other))
             return true;
 
         // exported via addExports
@@ -463,10 +471,10 @@
     }
 
     /**
-     * Returns {@code true} if this module permanently exports the given
+     * Returns {@code true} if this module statically exports the given
      * package to the given module.
      */
-    private boolean isExportedPermanently(String pn, Module other) {
+    private boolean isExportedStatically(String pn, Module other) {
         Map<String, Set<Module>> exports = this.exports;
         if (exports != null) {
             Set<Module> targets = exports.get(pn);
@@ -484,20 +492,20 @@
      */
     private boolean isExportedReflectively(String pn, Module other) {
         // exported to all modules
-        Map<String, ?> exports = transientExports.get(this, EVERYONE_MODULE);
+        Map<String, ?> exports = reflectivelyExports.get(this, EVERYONE_MODULE);
         if (exports != null && exports.containsKey(pn))
             return true;
 
         if (other != EVERYONE_MODULE) {
 
             // exported to other
-            exports = transientExports.get(this, other);
+            exports = reflectivelyExports.get(this, other);
             if (exports != null && exports.containsKey(pn))
                 return true;
 
             // other is an unnamed module && exported to all unnamed
             if (!other.isNamed()) {
-                exports = transientExports.get(this, ALL_UNNAMED_MODULE);
+                exports = reflectivelyExports.get(this, ALL_UNNAMED_MODULE);
                 if (exports != null && exports.containsKey(pn))
                     return true;
             }
@@ -604,10 +612,10 @@
             }
         }
 
-        // add package name to transientExports if absent
-        transientExports
+        // add package name to reflectivelyExports if absent
+        reflectivelyExports
             .computeIfAbsent(this, other,
-                             (_this, _other) -> new ConcurrentHashMap<>())
+                             (m1, m2) -> new ConcurrentHashMap<>())
             .putIfAbsent(pn, Boolean.TRUE);
     }
 
@@ -646,22 +654,28 @@
         Objects.requireNonNull(st);
 
         if (isNamed()) {
-
             Module caller = Reflection.getCallerClass().getModule();
             if (caller != this) {
                 throw new IllegalStateException(caller + " != " + this);
             }
-
-            if (!canUse(st)) {
-                transientUses.putIfAbsent(this, st, Boolean.TRUE);
-            }
-
+            implAddUses(st);
         }
 
         return this;
     }
 
     /**
+     * Update this module to add a service dependence on the given service
+     * type.
+     */
+    void implAddUses(Class<?> st) {
+        if (!canUse(st)) {
+            transientUses.putIfAbsent(this, st, Boolean.TRUE);
+        }
+    }
+
+
+    /**
      * Indicates if this module has a service dependence on the given service
      * type. This method always returns {@code true} when invoked on an unnamed
      * module.
@@ -965,6 +979,130 @@
     }
 
 
+    // -- annotations --
+
+    /**
+     * {@inheritDoc}
+     * This method returns {@code null} when invoked on an unnamed module.
+     */
+    @Override
+    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+        return moduleInfoClass().getDeclaredAnnotation(annotationClass);
+    }
+
+    /**
+     * {@inheritDoc}
+     * This method returns an empty array when invoked on an unnamed module.
+     */
+    @Override
+    public Annotation[] getAnnotations() {
+        return moduleInfoClass().getAnnotations();
+    }
+
+    /**
+     * {@inheritDoc}
+     * This method returns an empty array when invoked on an unnamed module.
+     */
+    @Override
+    public Annotation[] getDeclaredAnnotations() {
+        return moduleInfoClass().getDeclaredAnnotations();
+    }
+
+    // cached class file with annotations
+    private volatile Class<?> moduleInfoClass;
+
+    private Class<?> moduleInfoClass() {
+        Class<?> clazz = this.moduleInfoClass;
+        if (clazz != null)
+            return clazz;
+
+        synchronized (this) {
+            clazz = this.moduleInfoClass;
+            if (clazz == null) {
+                if (isNamed()) {
+                    PrivilegedAction<Class<?>> pa = this::loadModuleInfoClass;
+                    clazz = AccessController.doPrivileged(pa);
+                }
+                if (clazz == null) {
+                    class DummyModuleInfo { }
+                    clazz = DummyModuleInfo.class;
+                }
+                this.moduleInfoClass = clazz;
+            }
+            return clazz;
+        }
+    }
+
+    private Class<?> loadModuleInfoClass() {
+        Class<?> clazz = null;
+        try (InputStream in = getResourceAsStream("module-info.class")) {
+            if (in != null)
+                clazz = loadModuleInfoClass(in);
+        } catch (Exception ignore) { }
+        return clazz;
+    }
+
+    /**
+     * Loads module-info.class as a package-private interface in a class loader
+     * that is a child of this module's class loader.
+     */
+    private Class<?> loadModuleInfoClass(InputStream in) throws IOException {
+        final String MODULE_INFO = "module-info";
+
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
+                                         + ClassWriter.COMPUTE_FRAMES);
+
+        ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw) {
+            @Override
+            public void visit(int version,
+                              int access,
+                              String name,
+                              String signature,
+                              String superName,
+                              String[] interfaces) {
+                cw.visit(version,
+                        Opcodes.ACC_INTERFACE
+                            + Opcodes.ACC_ABSTRACT
+                            + Opcodes.ACC_SYNTHETIC,
+                        MODULE_INFO,
+                        null,
+                        "java/lang/Object",
+                        null);
+            }
+            @Override
+            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+                // keep annotations
+                return super.visitAnnotation(desc, visible);
+            }
+            @Override
+            public void visitAttribute(Attribute attr) {
+                // drop non-annotation attributes
+            }
+        };
+
+        ClassReader cr = new ClassReader(in);
+        cr.accept(cv, 0);
+        byte[] bytes = cw.toByteArray();
+
+        ClassLoader cl = new ClassLoader(loader) {
+            @Override
+            protected Class<?> findClass(String cn)throws ClassNotFoundException {
+                if (cn.equals(MODULE_INFO)) {
+                    return super.defineClass(cn, bytes, 0, bytes.length);
+                } else {
+                    throw new ClassNotFoundException(cn);
+                }
+            }
+        };
+
+        try {
+            return cl.loadClass(MODULE_INFO);
+        } catch (ClassNotFoundException e) {
+            throw new InternalError(e);
+        }
+    }
+
+
     // -- misc --
 
 
@@ -975,11 +1113,6 @@
      * The {@code name} is a {@code '/'}-separated path name that identifies
      * the resource.
      *
-     * <p> If this module is an unnamed module, and the {@code ClassLoader} for
-     * this module is not {@code null}, then this method is equivalent to
-     * invoking the {@link ClassLoader#getResourceAsStream(String)
-     * getResourceAsStream} method on the class loader for this module.
-     *
      * @param  name
      *         The resource name
      *
@@ -993,33 +1126,18 @@
     public InputStream getResourceAsStream(String name) throws IOException {
         Objects.requireNonNull(name);
 
-        URL url = null;
+        String mn = this.name;
 
-        if (isNamed()) {
-            String mn = this.name;
-
-            // special-case built-in class loaders to avoid URL connection
-            if (loader == null) {
-                return BootLoader.findResourceAsStream(mn, name);
-            } else if (loader instanceof BuiltinClassLoader) {
-                return ((BuiltinClassLoader) loader).findResourceAsStream(mn, name);
-            }
-
-            // use SharedSecrets to invoke protected method
-            url = SharedSecrets.getJavaLangAccess().findResource(loader, mn, name);
-
-        } else {
-
-            // unnamed module
-            if (loader == null) {
-                url = BootLoader.findResource(name);
-            } else {
-                return loader.getResourceAsStream(name);
-            }
-
+        // special-case built-in class loaders to avoid URL connection
+        if (loader == null) {
+            return BootLoader.findResourceAsStream(mn, name);
+        } else if (loader instanceof BuiltinClassLoader) {
+            return ((BuiltinClassLoader) loader).findResourceAsStream(mn, name);
         }
 
-        // fallthrough to URL case
+        // locate resource in module
+        JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
+        URL url = jla.findResource(loader, mn, name);
         if (url != null) {
             try {
                 return url.openStream();
@@ -1109,6 +1227,10 @@
                     m.implAddExports(pn, Module.ALL_UNNAMED_MODULE, true);
                 }
                 @Override
+                public void addUses(Module m, Class<?> service) {
+                    m.implAddUses(service);
+                }
+                @Override
                 public void addPackage(Module m, String pn) {
                     m.implAddPackage(pn, true);
                 }
--- a/src/java.base/share/classes/java/util/ResourceBundle.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/java/util/ResourceBundle.java	Thu Sep 08 17:42:08 2016 -0700
@@ -62,6 +62,7 @@
 import java.util.spi.ResourceBundleControlProvider;
 import java.util.spi.ResourceBundleProvider;
 
+import jdk.internal.loader.BootLoader;
 import jdk.internal.misc.JavaUtilResourceBundleAccess;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.reflect.CallerSensitive;
@@ -1206,9 +1207,9 @@
      * resource file using the generated properties file name.  It generates a
      * path name from the candidate bundle name by replacing all "." characters
      * with "/" and appending the string ".properties".  It attempts to find a
-     * "resource" with this name using {@link
-     * java.lang.ClassLoader#getResource(java.lang.String)
-     * ClassLoader.getResource}.  (Note that a "resource" in the sense of
+     * "resource" with this name from
+     * {@linkplain ClassLoader#getUnnamedModule() unnamed modules}.
+     * (Note that a "resource" in the sense of
      * <code>getResource</code> has nothing to do with the contents of a
      * resource bundle, it is just a container of data, such as a file.)  If it
      * finds a "resource", it attempts to create a new {@link
@@ -2437,19 +2438,17 @@
      *                 String bundleName = toBundleName(baseName, locale);
      *                 String resourceName = toResourceName(bundleName, format);
      *                 InputStream stream = null;
-     *                 if (reload) {
-     *                     URL url = loader.getResource(resourceName);
-     *                     if (url != null) {
-     *                         URLConnection connection = url.openConnection();
-     *                         if (connection != null) {
+     *                 URL url = loader.getResource(resourceName);
+     *                 if (url != null) {
+     *                     URLConnection connection = url.openConnection();
+     *                     if (connection != null) {
+     *                         if (reload) {
      *                             // Disable caches to get fresh data for
      *                             // reloading.
      *                             connection.setUseCaches(false);
-     *                             stream = connection.getInputStream();
      *                         }
+     *                         stream = connection.getInputStream();
      *                     }
-     *                 } else {
-     *                     stream = loader.getResourceAsStream(resourceName);
      *                 }
      *                 if (stream != null) {
      *                     BufferedInputStream bis = new BufferedInputStream(stream);
@@ -3041,19 +3040,18 @@
          * <li>If <code>format</code> is <code>"java.properties"</code>,
          * {@link #toResourceName(String, String) toResourceName(bundlename,
          * "properties")} is called to get the resource name.
-         * If <code>reload</code> is <code>true</code>, {@link
-         * ClassLoader#getResource(String) load.getResource} is called
-         * to get a {@link URL} for creating a {@link
-         * URLConnection}. This <code>URLConnection</code> is used to
+         * It will first search
+         * {@linkplain ClassLoader#findResource(String, String)
+         * the resource in an unnamed module} defined in this class loader and
+         * get a {@link URL} for creating a {@link URLConnection}.
+         * If not found, it will search the parent class loader for the resource.
+         * If <code>reload</code> is <code>true</code>,
+         * this <code>URLConnection</code> is used to
          * {@linkplain URLConnection#setUseCaches(boolean) disable the
-         * caches} of the underlying resource loading layers,
-         * and to {@linkplain URLConnection#getInputStream() get an
-         * <code>InputStream</code>}.
-         * Otherwise, {@link ClassLoader#getResourceAsStream(String)
-         * loader.getResourceAsStream} is called to get an {@link
-         * InputStream}. Then, a {@link
-         * PropertyResourceBundle} is constructed with the
-         * <code>InputStream</code>.</li>
+         * caches} of the underlying resource loading layers.
+         * Then {@link URLConnection#getInputStream()} is called to get an
+         * <code>InputStream</code> and construct a {@link PropertyResourceBundle}
+         * with the resulting <code>InputStream</code>.</li>
          *
          * <li>If <code>format</code> is neither <code>"java.class"</code>
          * nor <code>"java.properties"</code>, an
@@ -3167,7 +3165,7 @@
                     stream = AccessController.doPrivileged(
                         new PrivilegedExceptionAction<>() {
                             public InputStream run() throws IOException {
-                                URL url = loader.getResource(resourceName);
+                                URL url = getResourceInUnnamedModule(loader, resourceName);
                                 if (url == null) return null;
 
                                 URLConnection connection = url.openConnection();
@@ -3195,6 +3193,66 @@
             return bundle;
         }
 
+
+        /**
+         * Returns a URL to a resource in
+         * {@linkplain ClassLoader#getUnnamedModule() unnamed module}
+         * for this class loader and its ancestors.
+         *
+         * <p>
+         * This method will first invoke {@link ClassLoader#findResource(String, String)
+         * findResource(null, name)} to find a resource in an unnamed module
+         * defined in this class loader.  If not found, this method will search
+         * the parent class loader for the resource; if the parent is
+         * {@code null} the path of the class loader built-in to the
+         * virtual machine is searched.
+         *
+         * @apiNote
+         * This method does not search parent class loader first, as
+         * {@link ClassLoader#getResource(String)} does; instead, it finds
+         * a resource in the search path local in this class loader,
+         * as resources are typically private to a module.
+         *
+         * @param  name
+         *         The resource name
+         *
+         * @throws IOException
+         *         If I/O errors occur
+         *
+         * @return  A URL to the resource; {@code null} if
+         *          the resource could not be found, a {@code URL} could not be
+         *          constructed to locate the resource, or access to the resource
+         *          is denied by the security manager.
+         *
+         */
+        private URL getResourceInUnnamedModule(ClassLoader loader, String name)
+            throws IOException
+        {
+            Objects.requireNonNull(loader);
+
+            // locate resource defined in the given loader
+            URL url = SharedSecrets.getJavaLangAccess()
+                                   .findResource(loader, null, name);
+            if (url != null)
+                return url;
+
+            // search parent class loaders
+            Deque<ClassLoader> ancestors = new LinkedList<>();
+            ClassLoader ld = loader;
+            while ((ld = ld.getParent()) != null) {
+                ancestors.push(ld);
+            }
+
+            // search from the boot loader and other ancestors
+            url = BootLoader.findResource(null, name);
+
+            while (url == null && (ld = ancestors.pop()) != null) {
+                url = SharedSecrets.getJavaLangAccess()
+                                   .findResource(ld, null, name);
+            }
+            return url;
+        }
+
         /**
          * Returns the time-to-live (TTL) value for resource bundles that
          * are loaded under this
--- a/src/java.base/share/classes/jdk/internal/loader/BootLoader.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/loader/BootLoader.java	Thu Sep 08 17:42:08 2016 -0700
@@ -71,8 +71,8 @@
     private static final ServicesCatalog SERVICES_CATALOG = ServicesCatalog.create();
 
     // ClassLoaderValue map for boot class loader
-    private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP =
-        new ConcurrentHashMap<>();
+    private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP
+        = new ConcurrentHashMap<>();
 
     /**
      * Returns the unnamed module for the boot loader.
@@ -111,14 +111,27 @@
     }
 
     /**
-     * Returns a URL to a resource in a named module defined to the boot loader.
+     * Loads the Class object with the given name in the given module
+     * defined to the boot loader. Returns {@code null} if not found.
+     */
+    public static Class<?> loadClass(Module module, String name) {
+        Class<?> c = loadClassOrNull(name);
+        if (c != null && c.getModule() == module) {
+            return c;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns a URL to a resource in a module defined to the boot loader.
      */
     public static URL findResource(String mn, String name) throws IOException {
         return ClassLoaders.bootLoader().findResource(mn, name);
     }
 
     /**
-     * Returns an input stream to a resource in a named module defined to the
+     * Returns an input stream to a resource in a module defined to the
      * boot loader.
      */
     public static InputStream findResourceAsStream(String mn, String name)
@@ -128,9 +141,8 @@
     }
 
     /**
-     * Returns the URL to the given resource if the resource can be located
-     * on the boot class path. This method does not locate a resource in any
-     * of the named modules defined to the boot loader.
+     * Returns the URL to the given resource in any of the modules
+     * defined to the boot loader and the boot class path.
      */
     public static URL findResource(String name) {
         return ClassLoaders.bootLoader().findResource(name);
@@ -138,8 +150,7 @@
 
     /**
      * Returns an Iterator to iterate over the resources of the given name
-     * on the boot class path. This method does not locate resources in any
-     * of the named modules defined to the boot loader.
+     * in any of the modules defined to the boot loader.
      */
     public static Enumeration<URL> findResources(String name) throws IOException {
         return ClassLoaders.bootLoader().findResources(name);
--- a/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Thu Sep 08 17:42:08 2016 -0700
@@ -90,7 +90,7 @@
 {
     static {
         if (!ClassLoader.registerAsParallelCapable())
-            throw new InternalError();
+            throw new InternalError("Unable to register as parallel capable");
     }
 
     // parent ClassLoader
@@ -194,31 +194,29 @@
      */
     @Override
     public URL findResource(String mn, String name) throws IOException {
+        if (mn == null) {
+            // search class path
+            return findResourceOnClassPath(name);
+        }
+
         ModuleReference mref = nameToModule.get(mn);
         if (mref == null)
             return null;   // not defined to this class loader
 
         URL url;
-
         try {
             url = AccessController.doPrivileged(
                 new PrivilegedExceptionAction<URL>() {
                     @Override
                     public URL run() throws IOException {
-                        URI u = moduleReaderFor(mref).find(name).orElse(null);
-                        if (u != null) {
-                            try {
-                                return u.toURL();
-                            } catch (MalformedURLException e) { }
-                        }
-                        return null;
+                        return findResource(mref, name);
                     }
                 });
         } catch (PrivilegedActionException pae) {
             throw (IOException) pae.getCause();
         }
 
-        // check access to the URL
+        // check access before returning
         return checkURL(url);
     }
 
@@ -232,8 +230,9 @@
         // Need URL to resource when running with a security manager so that
         // the right permission check is done.
         SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
 
+        // or search class path for unnamed module
+        if (sm != null || mn == null) {
             URL url = findResource(mn, name);
             return (url != null) ? url.openStream() : null;
 
@@ -258,42 +257,152 @@
     }
 
     /**
-     * Finds the resource with the given name on the class path of this class
-     * loader.
+     * Finds the resource with the given name in the modules defined to this
+     * class loader or its class path.
      */
     @Override
     public URL findResource(String name) {
+
+        // for .class resources then locate in module if possible
+        URL url = findClassResourceOrNull(name);
+
+        // search all modules
+        if (url == null) {
+            url = AccessController.doPrivileged(
+                new PrivilegedAction<URL>() {
+                    @Override
+                    public URL run() {
+                        for (ModuleReference mref : nameToModule.values()) {
+                            URL url = findResourceOrNull(mref, name);
+                            if (url != null) return url;
+                        }
+                        return null;
+                    }
+                });
+        }
+
+        if (url != null) {
+            // check access before returning
+            return checkURL(url);
+        }
+
+        // search class path
+        return findResourceOnClassPath(name);
+    }
+
+    /**
+     * Returns an enumeration of URL objects to all the resources with the
+     * given name in modules defined to this class loader or on the class
+     * path of this loader.
+     */
+    @Override
+    public Enumeration<URL> findResources(String name) throws IOException {
+        List<URL> checked = new ArrayList<>();
+
+        // for .class resources then locate in module if possible
+        URL url = findClassResourceOrNull(name);
+
+        if (url != null) {
+            // check access
+            if (checkURL(url) != null) {
+                checked.add(url);
+            }
+        } else {
+            List<URL> result = AccessController.doPrivileged(
+                new PrivilegedAction<List<URL>>() {
+                    @Override
+                    public List<URL> run() {
+                        List<URL> result = new ArrayList<>();
+                        for (ModuleReference mref : nameToModule.values()) {
+                            URL url = findResourceOrNull(mref, name);
+                            if (url != null) result.add(url);
+                        }
+                        return result;
+                    }
+                });
+
+                // check access
+                for (URL u : result) {
+                    if (checkURL(u) != null) {
+                        checked.add(u);
+                    }
+                }
+        }
+
         if (ucp != null) {
-            PrivilegedAction<URL> pa = () -> ucp.findResource(name, false);
-            URL url = AccessController.doPrivileged(pa);
-            return checkURL(url);
-        } else {
+            PrivilegedAction<Enumeration<URL>> pa = () -> ucp.findResources(name, false);
+            Enumeration<URL> e = AccessController.doPrivileged(pa);
+            while (e.hasMoreElements()) {
+                url = checkURL(e.nextElement());
+                if (url != null) {
+                    checked.add(url);
+                }
+            }
+        }
+
+        return Collections.enumeration(checked); // checked URLs
+    }
+
+
+    /**
+     * Returns the URL to the given reference in the given module.
+     */
+    private URL findResource(ModuleReference mref, String name) throws IOException {
+        URI u = moduleReaderFor(mref).find(name).orElse(null);
+        if (u != null) {
+            try {
+                return u.toURL();
+            } catch (MalformedURLException e) { }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the URL to the given reference in the given module. Returns
+     * {@code null} if not found or an I/O error occurs.
+     */
+    private URL findResourceOrNull(ModuleReference mref, String name) {
+        try {
+            return findResource(mref, name);
+        } catch (IOException ignore) {
             return null;
         }
     }
 
     /**
-     * Returns an enumeration of URL objects to all the resources with the
-     * given name on the class path of this class loader.
+     * Returns the URL to a .class file in a module. Returns {@code null} if not
+     * a .class file in a module or an I/O error occurs.
      */
-    @Override
-    public Enumeration<URL> findResources(String name) throws IOException {
-        if (ucp != null) {
-            List<URL> result = new ArrayList<>();
-            PrivilegedAction<Enumeration<URL>> pa = () -> ucp.findResources(name, false);
-            Enumeration<URL> e = AccessController.doPrivileged(pa);
-            while (e.hasMoreElements()) {
-                URL url = checkURL(e.nextElement());
-                if (url != null) {
-                    result.add(url);
+    private URL findClassResourceOrNull(String name) {
+        int len = name.length();
+        if (len > 6 && name.endsWith(".class")) {
+            int index = len - 6;
+            String cn = name.substring(0, index).replace("/", ".");
+            LoadedModule module = findLoadedModule(cn);
+            if (module != null) {
+                try {
+                    return findResource(module.name(), name);
+                } catch (IOException ioe) {
+                    // ignore
                 }
             }
-            return Collections.enumeration(result); // checked URLs
-        } else {
-            return Collections.emptyEnumeration();
         }
+        return null;
     }
 
+    /**
+     * Returns a URL to a resource of the given name on the class path.
+     */
+    private URL findResourceOnClassPath(String name) {
+        if (ucp == null)
+            return null;
+
+        PrivilegedAction<URL> pa = () -> ucp.findResource(name, false);
+        URL url = AccessController.doPrivileged(pa);
+
+        // check access before returning
+        return checkURL(url);
+    }
 
     // -- finding/loading classes
 
@@ -334,24 +443,30 @@
     }
 
     /**
-     * Finds the class with the specified binary name in a given module.
-     * This method returns {@code null} if the class cannot be found.
+     * Finds the class with the specified binary name in a module.
+     * This method returns {@code null} if the class cannot be found
+     * or not defined in the specified module.
      */
     @Override
     protected Class<?> findClass(String mn, String cn) {
-        ModuleReference mref = nameToModule.get(mn);
-        if (mref == null)
-            return null;   // not defined to this class loader
+        if (mn != null) {
+            // find the candidate module for this class
+            LoadedModule loadedModule = findLoadedModule(mn, cn);
+            if (loadedModule == null) {
+                return null;
+            }
 
-        // find the candidate module for this class
-        LoadedModule loadedModule = findLoadedModule(cn);
-        if (loadedModule == null || !loadedModule.name().equals(mn)) {
-            return null;   // module name does not match
+            // attempt to load class in module defined to this loader
+            assert loadedModule.loader() == this;
+            return findClassInModuleOrNull(loadedModule, cn);
         }
 
-        // attempt to load class in module defined to this loader
-        assert loadedModule.loader() == this;
-        return findClassInModuleOrNull(loadedModule, cn);
+        // search class path
+        if (ucp != null) {
+            return findClassOnClassPathOrNull(cn);
+        }
+
+        return null;
     }
 
     /**
@@ -440,6 +555,21 @@
     }
 
     /**
+     * Find the candidate loaded module for the given class name
+     * in the named module.  Returns {@code null} if the named module
+     * is not defined to this class loader or does not contain
+     * the API package for the class.
+     */
+    private LoadedModule findLoadedModule(String mn, String cn) {
+        LoadedModule loadedModule = findLoadedModule(cn);
+        if (loadedModule != null && mn.equals(loadedModule.name())) {
+            return loadedModule;
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Finds the class with the specified binary name if in a module
      * defined to this ClassLoader.
      *
@@ -457,20 +587,20 @@
      */
     private Class<?> findClassOnClassPathOrNull(String cn) {
         return AccessController.doPrivileged(
-            new PrivilegedAction<Class<?>>() {
-                public Class<?> run() {
-                    String path = cn.replace('.', '/').concat(".class");
-                    Resource res = ucp.getResource(path, false);
-                    if (res != null) {
-                        try {
-                            return defineClass(cn, res);
-                        } catch (IOException ioe) {
-                            // TBD on how I/O errors should be propagated
+                new PrivilegedAction<Class<?>>() {
+                    public Class<?> run() {
+                        String path = cn.replace('.', '/').concat(".class");
+                        Resource res = ucp.getResource(path, false);
+                        if (res != null) {
+                            try {
+                                return defineClass(cn, res);
+                            } catch (IOException ioe) {
+                                // TBD on how I/O errors should be propagated
+                            }
                         }
+                        return null;
                     }
-                    return null;
-                }
-            });
+                });
     }
 
     /**
--- a/src/java.base/share/classes/jdk/internal/loader/Loader.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/loader/Loader.java	Thu Sep 08 17:42:08 2016 -0700
@@ -48,8 +48,12 @@
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 import java.security.SecureClassLoader;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
@@ -295,12 +299,14 @@
      */
     @Override
     protected URL findResource(String mn, String name) throws IOException {
-        ModuleReference mref = nameToModule.get(mn);
+        ModuleReference mref = (mn != null) ? nameToModule.get(mn) : null;
         if (mref == null)
             return null;   // not defined to this class loader
 
+        // locate resource
+        URL url = null;
         try {
-            return AccessController.doPrivileged(
+            url = AccessController.doPrivileged(
                 new PrivilegedExceptionAction<URL>() {
                     @Override
                     public URL run() throws IOException {
@@ -312,12 +318,53 @@
                         }
                         return null;
                     }
-                }, acc);
+                });
         } catch (PrivilegedActionException pae) {
             throw (IOException) pae.getCause();
-        } catch (SecurityException se) {
-            return null;
         }
+
+        // check access with permissions restricted by ACC
+        if (url != null && System.getSecurityManager() != null) {
+            try {
+                URL urlToCheck = url;
+                url = AccessController.doPrivileged(
+                    new PrivilegedExceptionAction<URL>() {
+                        @Override
+                        public URL run() throws IOException {
+                            return URLClassPath.checkURL(urlToCheck);
+                        }
+                    }, acc);
+            } catch (PrivilegedActionException pae) {
+                url = null;
+            }
+        }
+
+        return url;
+    }
+
+    @Override
+    public URL findResource(String name) {
+        for (ModuleReference mref : nameToModule.values()) {
+            try {
+                URL url = findResource(mref.descriptor().name(), name);
+                if (url != null)
+                    return url;
+            } catch (IOException ioe) { }
+        }
+        return null;
+    }
+
+    @Override
+    public Enumeration<URL> findResources(String name) throws IOException {
+        List<URL> urls = new ArrayList<>();
+        for (ModuleReference mref : nameToModule.values()) {
+            try {
+                URL url = findResource(mref.descriptor().name(), name);
+                if (url != null)
+                    urls.add(url);
+            } catch (IOException ioe) { }
+        }
+        return Collections.enumeration(urls);
     }
 
 
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Thu Sep 08 17:42:08 2016 -0700
@@ -51,22 +51,25 @@
 public interface JavaLangModuleAccess {
 
     /**
-     * Returns {@code ModuleDescriptor.Requires} of the given modifier
+     * Returns a {@code ModuleDescriptor.Requires} of the given modifiers
      * and module name.
      */
     Requires newRequires(Set<Requires.Modifier> ms, String mn);
 
     /**
      * Returns an unqualified {@code ModuleDescriptor.Exports}
-     * of the given package name.
+     * of the given modifiers and package name source.
      */
-    Exports newExports(String source);
+    Exports newExports(Set<Exports.Modifier> ms,
+                       String source);
 
     /**
      * Returns a qualified {@code ModuleDescriptor.Exports}
-     * of the given package name and targets.
+     * of the given modifiers, package name source and targets.
      */
-    Exports newExports(String source, Set<String> targets);
+    Exports newExports(Set<Exports.Modifier> ms,
+                       String source,
+                       Set<String> targets);
 
     /**
      * Returns a {@code ModuleDescriptor.Provides}
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java	Thu Sep 08 17:42:08 2016 -0700
@@ -81,6 +81,11 @@
     void addExportsToAllUnnamed(Module m, String pkg);
 
     /**
+     * Updates a module m to use a service.
+     */
+    void addUses(Module m, Class<?> service);
+
+    /**
      * Add a package to the given module.
      */
     void addPackage(Module m, String pkg);
--- a/src/java.base/share/classes/jdk/internal/module/Builder.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/Builder.java	Thu Sep 08 17:42:08 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -40,7 +40,7 @@
 
 /**
  * This builder is optimized for reconstituting ModuleDescriptor
- * for installed modules.  The validation should be done at jlink time.
+ * for system modules.  The validation should be done at jlink time.
  *
  * 1. skip name validation
  * 2. ignores dependency hashes.
@@ -53,11 +53,6 @@
     private static final JavaLangModuleAccess jlma =
         SharedSecrets.getJavaLangModuleAccess();
 
-    private static final Set<Requires.Modifier> MANDATED =
-        Collections.singleton(Requires.Modifier.MANDATED);
-    private static final Set<Requires.Modifier> PUBLIC =
-        Collections.singleton(Requires.Modifier.PUBLIC);
-
     // Static cache of the most recently seen Version to cheaply deduplicate
     // most Version objects.  JDK modules have the same version.
     static Version cachedVersion;
@@ -90,29 +85,7 @@
      * of modifiers.
      */
     public Builder requires(Set<Requires.Modifier> mods, String mn) {
-        requires.add(jlma.newRequires(Collections.unmodifiableSet(mods), mn));
-        return this;
-    }
-
-    /**
-     * Adds a module dependence with an empty set of modifiers.
-     */
-    public Builder requires(String mn) {
-        requires.add(jlma.newRequires(Collections.emptySet(), mn));
-        return this;
-    }
-
-    /**
-     * Adds a module dependence with the given modifier.
-     */
-    public Builder requires(Requires.Modifier mod, String mn) {
-        if (mod == Requires.Modifier.MANDATED) {
-            requires.add(jlma.newRequires(MANDATED, mn));
-        } else if (mod == Requires.Modifier.PUBLIC) {
-            requires.add(jlma.newRequires(PUBLIC, mn));
-        } else {
-            requires.add(jlma.newRequires(Collections.singleton(mod), mn));
-        }
+        requires.add(jlma.newRequires(mods, mn));
         return this;
     }
 
@@ -125,25 +98,21 @@
     }
 
     /**
-     * Adds an export to a set of target modules.
+     * Adds a qualified export to a set of target modules with a given set of
+     * modifiers.
      */
-    public Builder exports(String pn, Set<String> targets) {
-        exports.add(jlma.newExports(pn, targets));
+    public Builder exports(Set<Exports.Modifier> ms,
+                           String pn,
+                           Set<String> targets) {
+        exports.add(jlma.newExports(ms, pn, targets));
         return this;
     }
 
     /**
-     * Adds an export to a target module.
+     * Adds an unqualified export with a given set of modifiers.
      */
-    public Builder exports(String pn, String target) {
-        return exports(pn, Collections.singleton(target));
-    }
-
-    /**
-     * Adds an export.
-     */
-    public Builder exports(String pn) {
-        exports.add(jlma.newExports(pn));
+    public Builder exports(Set<Exports.Modifier> ms, String pn) {
+        exports.add(jlma.newExports(ms, pn));
         return this;
     }
 
@@ -270,6 +239,20 @@
         ModuleHashes moduleHashes =
             hashes != null ? new ModuleHashes(algorithm, hashes) : null;
 
+        // Make those collections we build dynamically unmodifiable
+        Map<String, Provides> provides = this.provides;
+        if (!provides.isEmpty()) {
+            provides = Collections.unmodifiableMap(this.provides);
+        }
+        Set<Exports> exports = this.exports;
+        if (!exports.isEmpty()) {
+            exports = Collections.unmodifiableSet(this.exports);
+        }
+        Set<Requires> requires = this.requires;
+        if (!requires.isEmpty()) {
+            requires = Collections.unmodifiableSet(this.requires);
+        }
+
         return jlma.newModuleDescriptor(name,
                                         false,    // automatic
                                         false,    // assume not synthetic for now
--- a/src/java.base/share/classes/jdk/internal/module/Checks.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/Checks.java	Thu Sep 08 17:42:08 2016 -0700
@@ -25,7 +25,6 @@
 
 package jdk.internal.module;
 
-
 public final class Checks {
 
     private Checks() { }
--- a/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java	Thu Sep 08 17:42:08 2016 -0700
@@ -27,7 +27,6 @@
 
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleDescriptor.Requires;
-import java.lang.module.ModuleDescriptor.Requires.Modifier;
 import java.lang.module.ModuleDescriptor.Exports;
 import java.lang.module.ModuleDescriptor.Provides;
 import java.lang.module.ModuleDescriptor.Version;
@@ -51,7 +50,7 @@
  * class file attributes in a module-info class file.
  */
 
-class ClassFileAttributes {
+public final class ClassFileAttributes {
 
     private ClassFileAttributes() { }
 
@@ -60,16 +59,16 @@
      *   // See lang-vm.html for details.
      * }
      */
-    static class ModuleAttribute extends Attribute {
+    public static class ModuleAttribute extends Attribute {
 
         private ModuleDescriptor descriptor;
 
-        ModuleAttribute(ModuleDescriptor descriptor) {
+        public ModuleAttribute(ModuleDescriptor descriptor) {
             super(MODULE);
             this.descriptor = descriptor;
         }
 
-        ModuleAttribute() {
+        public ModuleAttribute() {
             super(MODULE);
         }
 
@@ -91,17 +90,19 @@
             for (int i=0; i<requires_count; i++) {
                 String dn = cr.readUTF8(off, buf);
                 int flags = cr.readUnsignedShort(off + 2);
-                Set<Modifier> mods;
+                Set<Requires.Modifier> mods;
                 if (flags == 0) {
                     mods = Collections.emptySet();
                 } else {
                     mods = new HashSet<>();
-                    if ((flags & ACC_PUBLIC) != 0)
-                        mods.add(Modifier.PUBLIC);
+                    if ((flags & ACC_TRANSITIVE) != 0)
+                        mods.add(Requires.Modifier.PUBLIC);
+                    if ((flags & ACC_STATIC_PHASE) != 0)
+                        mods.add(Requires.Modifier.STATIC);
                     if ((flags & ACC_SYNTHETIC) != 0)
-                        mods.add(Modifier.SYNTHETIC);
+                        mods.add(Requires.Modifier.SYNTHETIC);
                     if ((flags & ACC_MANDATED) != 0)
-                        mods.add(Modifier.MANDATED);
+                        mods.add(Requires.Modifier.MANDATED);
                 }
                 builder.requires(mods, dn);
                 off += 4;
@@ -113,8 +114,25 @@
             if (exports_count > 0) {
                 for (int i=0; i<exports_count; i++) {
                     String pkg = cr.readUTF8(off, buf).replace('/', '.');
-                    int exports_to_count = cr.readUnsignedShort(off+2);
-                    off += 4;
+                    off += 2;
+
+                    int flags = cr.readUnsignedShort(off);
+                    off += 2;
+                    Set<Exports.Modifier> mods;
+                    if (flags == 0) {
+                        mods = Collections.emptySet();
+                    } else {
+                        mods = new HashSet<>();
+                        if ((flags & ACC_DYNAMIC_PHASE) != 0)
+                            mods.add(Exports.Modifier.DYNAMIC);
+                        if ((flags & ACC_SYNTHETIC) != 0)
+                            mods.add(Exports.Modifier.SYNTHETIC);
+                        if ((flags & ACC_MANDATED) != 0)
+                            mods.add(Exports.Modifier.MANDATED);
+                    }
+
+                    int exports_to_count = cr.readUnsignedShort(off);
+                    off += 2;
                     if (exports_to_count > 0) {
                         Set<String> targets = new HashSet<>();
                         for (int j=0; j<exports_to_count; j++) {
@@ -122,9 +140,9 @@
                             off += 2;
                             targets.add(t);
                         }
-                        builder.exports(pkg, targets);
+                        builder.exports(mods, pkg, targets);
                     } else {
-                        builder.exports(pkg);
+                        builder.exports(mods, pkg);
                     }
                 }
             }
@@ -176,11 +194,13 @@
             for (Requires md : descriptor.requires()) {
                 String dn = md.name();
                 int flags = 0;
-                if (md.modifiers().contains(Modifier.PUBLIC))
-                    flags |= ACC_PUBLIC;
-                if (md.modifiers().contains(Modifier.SYNTHETIC))
+                if (md.modifiers().contains(Requires.Modifier.PUBLIC))
+                    flags |= ACC_TRANSITIVE;
+                if (md.modifiers().contains(Requires.Modifier.STATIC))
+                    flags |= ACC_STATIC_PHASE;
+                if (md.modifiers().contains(Requires.Modifier.SYNTHETIC))
                     flags |= ACC_SYNTHETIC;
-                if (md.modifiers().contains(Modifier.MANDATED))
+                if (md.modifiers().contains(Requires.Modifier.MANDATED))
                     flags |= ACC_MANDATED;
                 int index = cw.newUTF8(dn);
                 attr.putShort(index);
@@ -195,6 +215,16 @@
                 for (Exports e : descriptor.exports()) {
                     String pkg = e.source().replace('.', '/');
                     attr.putShort(cw.newUTF8(pkg));
+
+                    int flags = 0;
+                    if (e.modifiers().contains(Exports.Modifier.DYNAMIC))
+                        flags |= ACC_DYNAMIC_PHASE;
+                    if (e.modifiers().contains(Exports.Modifier.SYNTHETIC))
+                        flags |= ACC_SYNTHETIC;
+                    if (e.modifiers().contains(Exports.Modifier.MANDATED))
+                        flags |= ACC_MANDATED;
+                    attr.putShort(flags);
+
                     if (e.isQualified()) {
                         Set<String> ts = e.targets();
                         attr.putShort(ts.size());
@@ -288,15 +318,15 @@
      *
      * }</pre>
      */
-    static class ConcealedPackagesAttribute extends Attribute {
+    public static class ConcealedPackagesAttribute extends Attribute {
         private final Set<String> packages;
 
-        ConcealedPackagesAttribute(Set<String> packages) {
+        public ConcealedPackagesAttribute(Set<String> packages) {
             super(CONCEALED_PACKAGES);
             this.packages = packages;
         }
 
-        ConcealedPackagesAttribute() {
+        public ConcealedPackagesAttribute() {
             this(null);
         }
 
@@ -364,15 +394,15 @@
      *
      * } </pre>
      */
-    static class VersionAttribute extends Attribute {
+    public static class VersionAttribute extends Attribute {
         private final Version version;
 
-        VersionAttribute(Version version) {
+        public VersionAttribute(Version version) {
             super(VERSION);
             this.version = version;
         }
 
-        VersionAttribute() {
+        public VersionAttribute() {
             this(null);
         }
 
@@ -419,15 +449,15 @@
      *
      * } </pre>
      */
-    static class MainClassAttribute extends Attribute {
+    public static class MainClassAttribute extends Attribute {
         private final String mainClass;
 
-        MainClassAttribute(String mainClass) {
+        public MainClassAttribute(String mainClass) {
             super(MAIN_CLASS);
             this.mainClass = mainClass;
         }
 
-        MainClassAttribute() {
+        public MainClassAttribute() {
             this(null);
         }
 
@@ -478,19 +508,19 @@
      *
      * } </pre>
      */
-    static class TargetPlatformAttribute extends Attribute {
+    public static class TargetPlatformAttribute extends Attribute {
         private final String osName;
         private final String osArch;
         private final String osVersion;
 
-        TargetPlatformAttribute(String osName, String osArch, String osVersion) {
+        public TargetPlatformAttribute(String osName, String osArch, String osVersion) {
             super(TARGET_PLATFORM);
             this.osName = osName;
             this.osArch = osArch;
             this.osVersion = osVersion;
         }
 
-        TargetPlatformAttribute() {
+        public TargetPlatformAttribute() {
             this(null, null, null);
         }
 
--- a/src/java.base/share/classes/jdk/internal/module/ClassFileConstants.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ClassFileConstants.java	Thu Sep 08 17:42:08 2016 -0700
@@ -44,10 +44,12 @@
     public static final String TARGET_PLATFORM    = "TargetPlatform";
     public static final String HASHES             = "Hashes";
 
-    // access and requires flags
-    public static final int ACC_MODULE       = 0x8000;
-    public static final int ACC_PUBLIC       = 0x0020;
-    public static final int ACC_SYNTHETIC    = 0x1000;
-    public static final int ACC_MANDATED     = 0x8000;
+    // access, requires, and exports flags
+    public static final int ACC_MODULE        = 0x8000;
+    public static final int ACC_TRANSITIVE    = 0x0010;
+    public static final int ACC_STATIC_PHASE  = 0x0020;
+    public static final int ACC_DYNAMIC_PHASE = 0x0040;
+    public static final int ACC_SYNTHETIC     = 0x1000;
+    public static final int ACC_MANDATED      = 0x8000;
 
 }
--- a/src/java.base/share/classes/jdk/internal/module/Modules.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/Modules.java	Thu Sep 08 17:42:08 2016 -0700
@@ -26,8 +26,11 @@
 package jdk.internal.module;
 
 import java.lang.module.ModuleDescriptor;
+import java.lang.reflect.Layer;
 import java.lang.reflect.Module;
 import java.net.URI;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Set;
 
 import jdk.internal.loader.BootLoader;
@@ -125,6 +128,38 @@
     }
 
     /**
+     * Updates module m to use a service
+     */
+    public static void addUses(Module m, Class<?> service) {
+        JLRMA.addUses(m, service);
+    }
+
+    /**
+     * Updates module m to provide a service
+     */
+    public static void addProvides(Module m, Class<?> service, Class<?> impl) {
+        // update ClassLoader catalog
+        PrivilegedAction<ClassLoader> pa = m::getClassLoader;
+        ClassLoader loader = AccessController.doPrivileged(pa);
+        ServicesCatalog catalog;
+        if (loader == null) {
+            catalog = BootLoader.getServicesCatalog();
+        } else {
+            catalog = SharedSecrets.getJavaLangAccess()
+                                   .createOrGetServicesCatalog(loader);
+        }
+        catalog.addProvider(m, service, impl);
+
+        // update Layer catalog
+        Layer layer = m.getLayer();
+        if (layer != null) {
+            SharedSecrets.getJavaLangReflectModuleAccess()
+                    .getServicesCatalog(layer)
+                    .addProvider(m, service, impl);
+        }
+    }
+
+    /**
      * Adds a package to a module's content.
      *
      * This method is a no-op if the module already contains the package.
@@ -142,5 +177,4 @@
         addReads(m, BootLoader.getUnnamedModule());
         addReads(m, ClassLoaders.appClassLoader().getUnnamedModule());
     }
-
 }
--- a/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java	Thu Sep 08 17:42:08 2016 -0700
@@ -41,7 +41,7 @@
  *
  * @see java.util.ServiceLoader
  */
-public interface ServicesCatalog {
+public final class ServicesCatalog {
 
     /**
      * Represents a service provider in the services catalog.
@@ -78,56 +78,106 @@
         }
     }
 
+    // service name -> service providers
+    private Map<String, Set<ServiceProvider>> map = new ConcurrentHashMap<>();
+
+    private ServicesCatalog() { }
+
+    /**
+     * Returns a unmodifiable set that is the union of the given sets.
+     */
+    private <T> Set<T> union(Set<T> s1, Set<T> s2) {
+        Set<T> result = new HashSet<>(s1);
+        result.addAll(s2);
+        return Collections.unmodifiableSet(result);
+    }
+
+    /**
+     * Adds or replaces an entry in the map.
+     */
+    private void replace(String sn,
+                         Set<ServiceProvider> oldSet,
+                         Set<ServiceProvider> newSet) {
+        boolean replaced;
+        if (oldSet == null) {
+            replaced = (map.putIfAbsent(sn, newSet) == null);
+        } else {
+            replaced = map.replace(sn, oldSet, newSet);
+        }
+        if (replaced) {
+            // added or replaced
+            return;
+        }
+        synchronized (this) {
+            oldSet = map.get(sn); // re-read
+            assert oldSet != null;
+            map.put(sn, union(oldSet, newSet));
+        }
+    }
+
+    /**
+     * Creates a ServicesCatalog that supports concurrent registration and
+     * and lookup
+     */
+    public static ServicesCatalog create() {
+        return new ServicesCatalog();
+    }
+
     /**
      * Registers the providers in the given module in this services catalog.
      *
      * @throws UnsupportedOperationException
      *         If this services catalog is immutable
      */
-    void register(Module module);
+    public void register(Module module) {
+        ModuleDescriptor descriptor = module.getDescriptor();
+        for (Provides provides : descriptor.provides().values()) {
+            String sn = provides.service();
+            Set<String> providerNames = provides.providers();
+
+            Set<ServiceProvider> oldSet = map.get(sn);
+            Set<ServiceProvider> newSet;
+            if (oldSet == null && providerNames.size() == 1) {
+                String pn = providerNames.iterator().next();
+                newSet = Set.of(new ServiceProvider(module, pn));
+            } else {
+                newSet = new HashSet<>();
+                if (oldSet != null) {
+                    newSet.addAll(oldSet);
+                }
+                for (String pn : providerNames) {
+                    newSet.add(new ServiceProvider(module, pn));
+                }
+            }
+
+            replace(sn, oldSet, newSet);
+        }
+    }
+
+    /**
+     * Add a provider in the given module to this services catalog
+     */
+    public void addProvider(Module module, Class<?> service, Class<?> impl) {
+        String sn = service.getName();
+        ServiceProvider provider = new ServiceProvider(module, impl.getName());
+        Set<ServiceProvider> providers = Set.of(provider);
+
+        Set<ServiceProvider> oldSet = map.get(sn);
+        Set<ServiceProvider> newSet;
+        if (oldSet == null) {
+            newSet = providers;
+        } else {
+            newSet = union(oldSet, providers);
+        }
+
+        replace(sn, oldSet, newSet);
+    }
 
     /**
      * Returns the (possibly empty) set of service providers that implement the
      * given service type.
      */
-    Set<ServiceProvider> findServices(String service);
-
-    /**
-     * Creates a ServicesCatalog that supports concurrent registration and
-     * and lookup.
-     */
-    static ServicesCatalog create() {
-        return new ServicesCatalog() {
-
-            private Map<String, Set<ServiceProvider>> map = new ConcurrentHashMap<>();
-
-            @Override
-            public void register(Module m) {
-                ModuleDescriptor descriptor = m.getDescriptor();
-
-                for (Provides provides : descriptor.provides().values()) {
-                    String service = provides.service();
-                    Set<String> providerNames = provides.providers();
-
-                    // create a new set to replace the existing
-                    Set<ServiceProvider> result = new HashSet<>();
-                    Set<ServiceProvider> providers = map.get(service);
-                    if (providers != null) {
-                        result.addAll(providers);
-                    }
-                    for (String pn : providerNames) {
-                        result.add(new ServiceProvider(m, pn));
-                    }
-                    map.put(service, Collections.unmodifiableSet(result));
-                }
-
-            }
-
-            @Override
-            public Set<ServiceProvider> findServices(String service) {
-                return map.getOrDefault(service, Collections.emptySet());
-            }
-
-        };
+    public Set<ServiceProvider> findServices(String service) {
+        return map.getOrDefault(service, Collections.emptySet());
     }
 }
\ No newline at end of file
--- a/src/java.base/share/classes/module-info.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/module-info.java	Thu Sep 08 17:42:08 2016 -0700
@@ -26,6 +26,7 @@
 /**
  * Defines the foundational APIs of the Java SE Platform.
  */
+@SuppressWarnings("deprecation")
 module java.base {
 
     exports java.io;
@@ -115,7 +116,7 @@
     // see make/gensrc/GenModuleInfo.gmk
 
     // CORBA serialization needs reflective access
-    exports sun.util.calendar to
+    exports dynamic sun.util.calendar to
         java.corba;
 
     exports com.sun.security.ntlm to
--- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java	Thu Sep 08 17:42:08 2016 -0700
@@ -79,6 +79,9 @@
 import java.util.jar.Attributes;
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
 import jdk.internal.misc.VM;
 
 
@@ -934,7 +937,10 @@
                 Set<Exports> exports = new TreeSet<>(Comparator.comparing(Exports::source));
                 exports.addAll(md.exports());
                 for (Exports e : exports) {
-                    ostream.format("  exports %s", e.source());
+                    String modsAndSource = Stream.concat(toStringStream(e.modifiers()),
+                                                         Stream.of(e.source()))
+                                                 .collect(Collectors.joining(" "));
+                    ostream.format("  exports %s", modsAndSource);
                     if (e.isQualified()) {
                         formatCommaList(ostream, " to", e.targets());
                     } else {
@@ -955,6 +961,10 @@
         }
     }
 
+    static <T> Stream<String> toStringStream(Set<T> s) {
+        return s.stream().map(e -> e.toString().toLowerCase());
+    }
+
     static String midAndLocation(ModuleDescriptor md, Optional<URI> location ) {
         URI loc = location.orElse(null);
         if (loc == null || loc.getScheme().equalsIgnoreCase("jrt"))
--- a/src/java.base/share/native/include/jni.h	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/native/include/jni.h	Thu Sep 08 17:42:08 2016 -0700
@@ -770,12 +770,6 @@
 
     jobject (JNICALL *GetModule)
        (JNIEnv* env, jclass clazz);
-
-    void (JNICALL *AddModuleReads)
-        (JNIEnv* env, jobject m1, jobject m2);
-
-    jboolean (JNICALL *CanReadModule)
-        (JNIEnv* env, jobject m1, jobject m2);
 };
 
 /*
@@ -1874,14 +1868,6 @@
         return functions->GetModule(this, clazz);
     }
 
-    void AddModuleReads(jobject m1, jobject m2) {
-        functions->AddModuleReads(this, m1, m2);
-    }
-
-    jboolean CanReadModule(jobject m1, jobject m2) {
-        return functions->CanReadModule(this, m1, m2);
-    }
-
 #endif /* __cplusplus */
 };
 
--- a/src/java.base/share/native/include/jvmti.h	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.base/share/native/include/jvmti.h	Thu Sep 08 17:42:08 2016 -0700
@@ -348,6 +348,7 @@
     JVMTI_ERROR_INVALID_METHODID = 23,
     JVMTI_ERROR_INVALID_LOCATION = 24,
     JVMTI_ERROR_INVALID_FIELDID = 25,
+    JVMTI_ERROR_INVALID_MODULE = 26,
     JVMTI_ERROR_NO_MORE_FRAMES = 31,
     JVMTI_ERROR_OPAQUE_FRAME = 32,
     JVMTI_ERROR_TYPE_MISMATCH = 34,
@@ -1499,17 +1500,27 @@
     const jthread* request_list,
     jvmtiError* results);
 
-  /*   94 :  RESERVED */
-  void *reserved94;
-
-  /*   95 :  RESERVED */
-  void *reserved95;
-
-  /*   96 :  RESERVED */
-  void *reserved96;
-
-  /*   97 :  RESERVED */
-  void *reserved97;
+  /*   94 : Add Module Reads */
+  jvmtiError (JNICALL *AddModuleReads) (jvmtiEnv* env,
+    jobject module,
+    jobject to_module);
+
+  /*   95 : Add Module Exports */
+  jvmtiError (JNICALL *AddModuleExports) (jvmtiEnv* env,
+    jobject module,
+    const char* pkg_name,
+    jobject to_module);
+
+  /*   96 : Add Module Uses */
+  jvmtiError (JNICALL *AddModuleUses) (jvmtiEnv* env,
+    jobject module,
+    jclass service);
+
+  /*   97 : Add Module Provides */
+  jvmtiError (JNICALL *AddModuleProvides) (jvmtiEnv* env,
+    jobject module,
+    jclass service,
+    jclass impl_class);
 
   /*   98 :  RESERVED */
   void *reserved98;
@@ -2155,6 +2166,28 @@
     return functions->GetNamedModule(this, class_loader, package_name, module_ptr);
   }
 
+  jvmtiError AddModuleReads(jobject module,
+            jobject to_module) {
+    return functions->AddModuleReads(this, module, to_module);
+  }
+
+  jvmtiError AddModuleExports(jobject module,
+            const char* pkg_name,
+            jobject to_module) {
+    return functions->AddModuleExports(this, module, pkg_name, to_module);
+  }
+
+  jvmtiError AddModuleUses(jobject module,
+            jclass service) {
+    return functions->AddModuleUses(this, module, service);
+  }
+
+  jvmtiError AddModuleProvides(jobject module,
+            jclass service,
+            jclass impl_class) {
+    return functions->AddModuleProvides(this, module, service, impl_class);
+  }
+
   jvmtiError GetLoadedClasses(jint* class_count_ptr,
             jclass** classes_ptr) {
     return functions->GetLoadedClasses(this, class_count_ptr, classes_ptr);
--- a/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java	Thu Sep 08 17:42:08 2016 -0700
@@ -27,6 +27,8 @@
 
 import java.lang.reflect.Module;
 import java.security.ProtectionDomain;
+import java.util.Map;
+import java.util.Set;
 import java.util.jar.JarFile;
 
 /*
@@ -660,19 +662,57 @@
     setNativeMethodPrefix(ClassFileTransformer transformer, String prefix);
 
     /**
-     * Updates a module to read another module.
+     * Redefine a module to expand the set of modules that it reads, the set of
+     * packages that it exports, or the services that it uses or provides. This
+     * method facilitates the instrumentation of code in named modules where
+     * that instrumentation requires changes to the set of modules that are read,
+     * the packages that are exported, or the services that are used or provided.
      *
-     * Agents that instrument code in named modules may need to arrange for the
-     * modules to read other modules. This method is equivalent to code in {@code
-     * module} calling {@link Module#addReads(Module) addReads} to read {@code
-     * other}.
+     * <p> This method cannot reduce the set of modules that a module reads, nor
+     * reduce the set of packages that it exports, nor reduce the set of
+     * services that it uses or provides. This method is a no-op when invoked
+     * to redefine an unnamed module. </p>
      *
-     * @param module the module to update
-     * @param other the module to read
-     * @throws NullPointerException if either module is {@code null}
+     * <p> When expanding the services that a module uses or provides then the
+     * onus is on the agent to ensure that the service type will be accessible at
+     * each instrumentation site where the service type is used. This method
+     * does not check if the service type is a member of the module or in a
+     * package exported to the module by another module that it reads. </p>
+     *
+     * <p> The {@code extraExports} parameter is the map of additional packages to
+     * export. The map key is the fully-qualified name of the package as
+     * defined in section 6.5.3 of <cite>The Java&trade; Language Specification
+     * </cite>, for example, {@code "java.lang"}. The map value is the set of
+     * modules that the package should be exported too. If the set is empty then
+     * the package is exported unconditionally. </p>
+     *
+     * <p> The {@code extraProvides} parameter is the additional service providers
+     * for the module to provide. The map key is the service type. The map value
+     * is set of implementation types, each of which is a member of the module
+     * and an implementation of the service. </p>
+     *
+     * <p> This method is safe for concurrent use and so allows multiple agents
+     * to instrument and update the same module at around the same time. </p>
+     *
+     * @param module the module to redefine
+     * @param extraReads the set of additional modules to read
+     * @param extraExports the additional packages to export
+     * @param extraUses the set of additional services to use
+     * @param extraProvides the additional services to provide
+     *
+     * @throws IllegalArgumentException
+     *         If {@code extraExports} contains a key that is not a package in
+     *         the module; if a value in the {@code extraProvides} map contains
+     *         a service provider type that is not a member of the module or an
+     *         implementation of the service
+     * @throws NullPointerException if any of the arguments are {@code null} or
+     *         any of the Sets or Maps contains a {@code null} key or value
      *
      * @since 9
-     * @see Module#canRead(Module)
      */
-    void addModuleReads(Module module, Module other);
+    void redefineModule(Module module,
+                        Set<Module> extraReads,
+                        Map<String, Set<Module>> extraExports,
+                        Set<Class<?>> extraUses,
+                        Map<Class<?>, Set<Class<?>>> extraProvides);
 }
--- a/src/java.instrument/share/classes/java/lang/instrument/package.html	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.instrument/share/classes/java/lang/instrument/package.html	Thu Sep 08 17:42:08 2016 -0700
@@ -270,15 +270,6 @@
 
 <h3>Instrumenting code in modules</h3>
 
-Agents that instrument code in named modules may need to arrange for the
-modules to read other modules. If code is instrumented to invoke a method
-in a support class in another module, then the module of the instrumented
-code should read the module of the supporting class. Furthermore, the
-supporting class will only be accessible to the instrumented code if
-it is <code>public</code> and in a package that is exported by its module.
-Agents can use {@link Instrumentation#addModuleReads addModuleReads} to update
-a module to read another.
-<p>
 As an aid to agents that deploy supporting classes on the search path of the
 bootstrap class loader, or the search path of the class loader that loads
 the main agent class, the Java virtual machine arranges for the module of
--- a/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java	Thu Sep 08 17:42:08 2016 -0700
@@ -29,17 +29,19 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Module;
 import java.lang.reflect.AccessibleObject;
-
 import java.lang.instrument.ClassFileTransformer;
 import java.lang.instrument.ClassDefinition;
 import java.lang.instrument.Instrumentation;
-
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarFile;
 
-import java.util.Objects;
-import java.util.jar.JarFile;
+import jdk.internal.module.Modules;
 
 /*
  * Copyright 2003 Wily Technology, Inc.
@@ -228,10 +230,82 @@
     }
 
     @Override
-    public void addModuleReads(Module module, Module other) {
-        Objects.requireNonNull(module);
-        Objects.requireNonNull(other);
-        jdk.internal.module.Modules.addReads(module, other);
+    public void redefineModule(Module module,
+                               Set<Module> extraReads,
+                               Map<String, Set<Module>> extraExports,
+                               Set<Class<?>> extraUses,
+                               Map<Class<?>, Set<Class<?>>> extraProvides)
+    {
+        if (!module.isNamed())
+            return;
+
+        // copy and check reads
+        extraReads = new HashSet<>(extraReads);
+        if (extraReads.contains(null))
+            throw new NullPointerException("'extraReads' contains null");
+
+        // copy and check exports
+        Set<String> packages = Set.of(module.getPackages());
+        Map<String, Set<Module>> tmpExports = new HashMap<>();
+        for (Map.Entry<String, Set<Module>> e : extraExports.entrySet()) {
+            String pkg = e.getKey();
+            if (pkg == null)
+                throw new NullPointerException("package cannot be null");
+            if (!packages.contains(pkg))
+                throw new IllegalArgumentException(pkg + " not in module");
+            Set<Module> targets = new HashSet<>(e.getValue());
+            if (targets.contains(null))
+                throw new NullPointerException("set of targets cannot include null");
+            tmpExports.put(pkg, targets);
+        }
+        extraExports = tmpExports;
+
+        // copy and check uses
+        extraUses = new HashSet<>(extraUses);
+        if (extraUses.contains(null))
+            throw new NullPointerException("'extraUses' contains null");
+
+        // copy and check provides
+        Map<Class<?>, Set<Class<?>>> tmpProvides = new HashMap<>();
+        for (Map.Entry<Class<?>, Set<Class<?>>> e : extraProvides.entrySet()) {
+            Class<?> service = e.getKey();
+            if (service == null)
+                throw new NullPointerException("'extraProvides' contains null");
+            Set<Class<?>> providers = new HashSet<>(e.getValue());
+            providers.forEach(p -> {
+                if (p.getModule() != module)
+                    throw new IllegalArgumentException(p + " not in " + module);
+                if (!service.isAssignableFrom(p))
+                    throw new IllegalArgumentException(p + " is not a " + service);
+            });
+            tmpProvides.put(service, providers);
+        }
+        extraProvides = tmpProvides;
+
+
+        // update reads
+        extraReads.forEach(m -> Modules.addReads(module, m));
+
+        // update exports
+        for (Map.Entry<String, Set<Module>> e : extraExports.entrySet()) {
+            String pkg = e.getKey();
+            Set<Module> targets = e.getValue();
+            if (targets.isEmpty()) {
+                Modules.addExportsToAll(module, pkg);
+            } else {
+                targets.forEach(m -> Modules.addExports(module, pkg, m));
+            }
+        }
+
+        // update uses
+        extraUses.forEach(service -> Modules.addUses(module, service));
+
+        // update provides
+        for (Map.Entry<Class<?>, Set<Class<?>>> e : extraProvides.entrySet()) {
+            Class<?> service = e.getKey();
+            Set<Class<?>> providers = e.getValue();
+            providers.forEach(p -> Modules.addProvides(module, service, p));
+        }
     }
 
 
--- a/src/java.naming/share/classes/module-info.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.naming/share/classes/module-info.java	Thu Sep 08 17:42:08 2016 -0700
@@ -26,6 +26,7 @@
 /**
  * Defines the Java Naming and Directory Interface (JNDI) API.
  */
+@SuppressWarnings("deprecation")
 module java.naming {
     requires java.security.sasl;
 
--- a/src/java.se.ee/share/classes/module-info.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/java.se.ee/share/classes/module-info.java	Thu Sep 08 17:42:08 2016 -0700
@@ -29,6 +29,7 @@
  * This module requires {@code java.se} and supplements it with modules
  * that define CORBA and Java EE APIs. These modules are upgradeable.
  */
+@SuppressWarnings("deprecation")
 module java.se.ee {
 
     requires public java.se;
--- a/src/jdk.attach/share/classes/module-info.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/jdk.attach/share/classes/module-info.java	Thu Sep 08 17:42:08 2016 -0700
@@ -23,6 +23,9 @@
  * questions.
  */
 
+/**
+ * Defines the attach API.
+ */
 module jdk.attach {
     requires jdk.jvmstat;
 
--- a/src/jdk.internal.le/share/classes/module-info.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/jdk.internal.le/share/classes/module-info.java	Thu Sep 08 17:42:08 2016 -0700
@@ -23,6 +23,9 @@
  * questions.
  */
 
+/**
+ * Internal API for line editing
+ */
 module jdk.internal.le {
     exports jdk.internal.jline to
         jdk.scripting.nashorn.shell,
--- a/src/jdk.internal.opt/share/classes/module-info.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/jdk.internal.opt/share/classes/module-info.java	Thu Sep 08 17:42:08 2016 -0700
@@ -23,6 +23,9 @@
  * questions.
  */
 
+/**
+ * Internal option processing API
+ */
 module jdk.internal.opt {
     exports jdk.internal.joptsimple to jdk.jlink, jdk.jshell;
 }
--- a/src/jdk.jdi/share/classes/module-info.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/jdk.jdi/share/classes/module-info.java	Thu Sep 08 17:42:08 2016 -0700
@@ -23,6 +23,9 @@
  * questions.
  */
 
+/**
+ * Defines the Java Debugger Interface.
+ */
 module jdk.jdi {
     requires jdk.attach;
 
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java	Thu Sep 08 17:42:08 2016 -0700
@@ -118,7 +118,7 @@
                                         null));
         }
 
-        plugins.add(Jlink.newPlugin("installed-modules", Collections.emptyMap(), null));
+        plugins.add(Jlink.newPlugin("system-modules", Collections.emptyMap(), null));
 
         // build the image
         Jlink.PluginsConfiguration pluginConfig = new Jlink.PluginsConfiguration(
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java	Thu Sep 08 17:42:08 2016 -0700
@@ -28,8 +28,8 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleDescriptor.*;
-import java.lang.module.ModuleDescriptor;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
@@ -38,6 +38,7 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.function.IntSupplier;
 import java.util.stream.Collectors;
 
 import jdk.internal.misc.JavaLangModuleAccess;
@@ -53,12 +54,12 @@
 import jdk.tools.jlink.plugin.PluginException;
 import jdk.tools.jlink.plugin.ResourcePool;
 import jdk.tools.jlink.plugin.Plugin;
-import jdk.tools.jlink.internal.plugins.SystemModuleDescriptorPlugin.Builder.*;
+import jdk.tools.jlink.internal.plugins.SystemModuleDescriptorPlugin.SystemModulesClassGenerator.*;
 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
 import jdk.tools.jlink.plugin.ResourcePoolEntry;
 
 /**
- * Jlink plugin to reconstitute module descriptors for installed modules.
+ * Jlink plugin to reconstitute module descriptors for system modules.
  * It will extend module-info.class with ConcealedPackages attribute,
  * if not present. It also determines the number of packages of
  * the boot layer at link time.
@@ -71,11 +72,7 @@
 public final class SystemModuleDescriptorPlugin implements Plugin {
     private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
 
-    // TODO: packager has the dependency on the plugin name
-    // Keep it as "--installed-modules" until packager removes such
-    // dependency (should not need to specify this plugin since it
-    // is enabled by default)
-    private static final String NAME = "installed-modules";
+    private static final String NAME = "system-modules";
     private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
     private boolean enabled;
 
@@ -113,7 +110,7 @@
             throw new PluginException(NAME + " was set");
         }
 
-        Builder builder = new Builder();
+        SystemModulesClassGenerator generator = new SystemModulesClassGenerator();
 
         // generate the byte code to create ModuleDescriptors
         // skip parsing module-info.class and skip name check
@@ -131,7 +128,7 @@
                 ModuleDescriptor md = ModuleDescriptor.read(bain);
                 validateNames(md);
 
-                ModuleDescriptorBuilder mbuilder = builder.module(md, module.packages());
+                ModuleDescriptorBuilder mbuilder = generator.module(md, module.packages());
                 int packages = md.exports().size() + md.conceals().size();
                 if (md.conceals().isEmpty() &&
                         packages != module.packages().size()) {
@@ -149,11 +146,11 @@
         });
 
         // Generate the new class
-        ClassWriter cwriter = builder.build();
+        ClassWriter cwriter = generator.getClassWriter();
         in.entries().forEach(data -> {
             if (data.path().endsWith("module-info.class"))
                 return;
-            if (builder.isOverriddenClass(data.path())) {
+            if (generator.isOverriddenClass(data.path())) {
                 byte[] bytes = cwriter.toByteArray();
                 ResourcePoolEntry ndata = data.copyWithContent(bytes);
                 out.add(ndata);
@@ -222,31 +219,34 @@
     }
 
     /**
-     * Builder of a new jdk.internal.module.SystemModules class
-     * to reconstitute ModuleDescriptor of the installed modules.
+     * ClassWriter of a new jdk.internal.module.SystemModules class
+     * to reconstitute ModuleDescriptor of the system modules.
      */
-    static class Builder {
+    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 =
             "[Ljava/lang/module/ModuleDescriptor;";
+        private static final String REQUIRES_MODIFIER_CLASSNAME =
+            "java/lang/module/ModuleDescriptor$Requires$Modifier";
+        private static final String EXPORTS_MODIFIER_CLASSNAME =
+            "java/lang/module/ModuleDescriptor$Exports$Modifier";
 
         // static variables in SystemModules class
         private static final String MODULE_NAMES = "MODULE_NAMES";
         private static final String MODULES_TO_HASH = "MODULES_TO_HASH";
         private static final String PACKAGE_COUNT = "PACKAGES_IN_BOOT_LAYER";
 
-        private static final int BUILDER_VAR    = 0;
-        private static final int MD_VAR         = 1;   // variable for ModuleDescriptor
-        private static final int MODS_VAR       = 2;   // variable for Set<Modifier>
-        private static final int STRING_SET_VAR = 3;   // variable for Set<String>
         private static final int MAX_LOCAL_VARS = 256;
 
+        private final int BUILDER_VAR    = 0;
+        private final int MD_VAR         = 1;  // variable for ModuleDescriptor
+        private int nextLocalVar         = 2;  // index to next local variable
+
         private final ClassWriter cw;
         private MethodVisitor mv;
-        private int nextLocalVar = 4;
         private int nextModulesIndex = 0;
 
         // list of all ModuleDescriptorBuilders, invoked in turn when building.
@@ -255,12 +255,19 @@
         // module name to hash
         private final Map<String, String> modulesToHash = new HashMap<>();
 
-        // map Set<String> to a specialized builder to allow them to be
-        // deduplicated as they are requested
-        private final Map<Set<String>, StringSetBuilder> stringSets = new HashMap<>();
+        // A builder to create one single Set instance for a given set of
+        // names or modifiers to reduce the footprint
+        // e.g. target modules of qualified exports
+        private final DedupSetBuilder dedupSetBuilder
+            = new DedupSetBuilder(this::getNextLocalVar);
 
-        public Builder() {
-            this.cw = new ClassWriter(ClassWriter.COMPUTE_MAXS+ClassWriter.COMPUTE_FRAMES);
+        public SystemModulesClassGenerator() {
+            this.cw = new ClassWriter(ClassWriter.COMPUTE_MAXS +
+                                      ClassWriter.COMPUTE_FRAMES);
+        }
+
+        private int getNextLocalVar() {
+            return nextLocalVar++;
         }
 
         /*
@@ -270,7 +277,7 @@
          */
         private void clinit(int numModules, int numPackages) {
             cw.visit(Opcodes.V1_8, ACC_PUBLIC+ACC_FINAL+ACC_SUPER, CLASSNAME,
-                    null, "java/lang/Object", null);
+                     null, "java/lang/Object", null);
 
             // public static String[] MODULE_NAMES = new String[] {....};
             cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULE_NAMES,
@@ -333,9 +340,9 @@
         }
 
         /*
-         * Adds the given ModuleDescriptor to the installed module list, and
-         * prepares mapping from Set<String> to StringSetBuilders to emit an
-         * optimized number of string sets during build.
+         * Adds the given ModuleDescriptor to the system module list, and
+         * prepares mapping from various Sets to SetBuilders to emit an
+         * optimized number of sets during build.
          */
         public ModuleDescriptorBuilder module(ModuleDescriptor md, Set<String> packages) {
             ModuleDescriptorBuilder builder = new ModuleDescriptorBuilder(md, packages);
@@ -343,21 +350,22 @@
 
             // exports
             for (ModuleDescriptor.Exports e : md.exports()) {
-                if (e.isQualified()) {
-                    stringSets.computeIfAbsent(e.targets(), s -> new StringSetBuilder(s))
-                              .increment();
-                }
+                dedupSetBuilder.stringSet(e.targets());
+                dedupSetBuilder.exportsModifiers(e.modifiers());
             }
 
-            // provides (preserve iteration order)
+            // provides
             for (ModuleDescriptor.Provides p : md.provides().values()) {
-                stringSets.computeIfAbsent(p.providers(), s -> new StringSetBuilder(s, true))
-                          .increment();
+                dedupSetBuilder.stringSet(p.providers(), true /* preserve iteration order */);
+            }
+
+            // requires
+            for (ModuleDescriptor.Requires r : md.requires()) {
+                dedupSetBuilder.requiresModifiers(r.modifiers());
             }
 
             // uses
-            stringSets.computeIfAbsent(md.uses(), s -> new StringSetBuilder(s))
-                      .increment();
+            dedupSetBuilder.stringSet(md.uses());
 
             // hashes
             JLMA.hashes(md).ifPresent(mh -> modulesToHash.putAll(mh.hashes()));
@@ -368,7 +376,7 @@
         /*
          * Generate bytecode for SystemModules
          */
-        public ClassWriter build() {
+        public ClassWriter getClassWriter() {
             int numModules = builders.size();
             int numPackages = 0;
             for (ModuleDescriptorBuilder builder : builders) {
@@ -411,17 +419,20 @@
         }
 
         class ModuleDescriptorBuilder {
-            static final String REQUIRES_MODIFIER_CLASSNAME =
-                    "java/lang/module/ModuleDescriptor$Requires$Modifier";
-            static final String REQUIRES_MODIFIER_TYPE =
-                "Ljava/lang/module/ModuleDescriptor$Requires$Modifier;";
             static final String BUILDER_TYPE = "Ljdk/internal/module/Builder;";
-            static final String REQUIRES_MODIFIER_STRING_SIG =
-                "(" + REQUIRES_MODIFIER_TYPE + "Ljava/lang/String;)" + BUILDER_TYPE;
+
+            static final String EXPORTS_MODIFIER_SET_STRING_SET_SIG =
+                "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)"
+                    + BUILDER_TYPE;
+            static final String EXPORTS_MODIFIER_SET_STRING_SIG =
+                "(Ljava/util/Set;Ljava/lang/String;)" + BUILDER_TYPE;
+
+            // general type names and signatures
             static final String STRING_SET_SIG =
                 "(Ljava/lang/String;Ljava/util/Set;)" + BUILDER_TYPE;
             static final String SET_STRING_SIG =
                 "(Ljava/util/Set;Ljava/lang/String;)" + BUILDER_TYPE;
+
             static final String SET_SIG =
                 "(Ljava/util/Set;)" + BUILDER_TYPE;
             static final String STRING_SIG = "(Ljava/lang/String;)" + BUILDER_TYPE;
@@ -483,27 +494,12 @@
 
                 // requires
                 for (ModuleDescriptor.Requires req : md.requires()) {
-                    switch (req.modifiers().size()) {
-                        case 0:
-                            requires(req.name());
-                            break;
-                        case 1:
-                            ModuleDescriptor.Requires.Modifier mod =
-                                req.modifiers().iterator().next();
-                            requires(mod, req.name());
-                            break;
-                        default:
-                            requires(req.modifiers(), req.name());
-                    }
+                    requires(req.modifiers(), req.name());
                 }
 
                 // exports
                 for (ModuleDescriptor.Exports e : md.exports()) {
-                    if (e.isQualified()) {
-                        exports(e.source(), e.targets());
-                    } else {
-                        exports(e.source());
-                    }
+                    exports(e.modifiers(), e.source(), e.targets());
                 }
 
                 // uses
@@ -545,49 +541,15 @@
             }
 
             /*
-             * Invoke Builder.requires(String mn)
-             */
-            void requires(String name) {
-                mv.visitVarInsn(ALOAD, BUILDER_VAR);
-                mv.visitLdcInsn(name);
-                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                                   "requires", STRING_SIG, false);
-                mv.visitInsn(POP);
-            }
-
-            /*
-             * Invoke Builder.requires(Modifier mod, String mn)
-             */
-            void requires(ModuleDescriptor.Requires.Modifier mod, String name) {
-                mv.visitVarInsn(ALOAD, BUILDER_VAR);
-                mv.visitFieldInsn(GETSTATIC, REQUIRES_MODIFIER_CLASSNAME, mod.name(),
-                                  REQUIRES_MODIFIER_TYPE);
-                mv.visitLdcInsn(name);
-                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                                   "requires", REQUIRES_MODIFIER_STRING_SIG, false);
-                mv.visitInsn(POP);
-            }
-
-            /*
              * Invoke Builder.requires(Set<Modifier> mods, String mn)
              *
-             * EnumSet<Modifier> mods = EnumSet.of(mod,....);
-             * Buidler.requires(mods, mn);
+             * Set<Modifier> mods = ...
+             * Builder.requires(mods, mn);
              */
             void requires(Set<ModuleDescriptor.Requires.Modifier> mods, String name) {
-                mv.visitVarInsn(ALOAD, MODS_VAR);
-                String signature = "(";
-                for (ModuleDescriptor.Requires.Modifier m : mods) {
-                    mv.visitFieldInsn(GETSTATIC, REQUIRES_MODIFIER_CLASSNAME, m.name(),
-                                      REQUIRES_MODIFIER_TYPE);
-                    signature += "Ljava/util/Enum;";
-                }
-                signature += ")Ljava/util/EnumSet;";
-                mv.visitMethodInsn(INVOKESTATIC, "java/util/EnumSet", "of",
-                                   signature, false);
-                mv.visitVarInsn(ASTORE, MODS_VAR);
+                int varIndex = dedupSetBuilder.indexOfRequiresModifiers(mods);
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
-                mv.visitVarInsn(ALOAD, MODS_VAR);
+                mv.visitVarInsn(ALOAD, varIndex);
                 mv.visitLdcInsn(name);
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                                    "requires", SET_STRING_SIG, false);
@@ -595,33 +557,41 @@
             }
 
             /*
-             * Invoke Builder.exports(String pn)
-             */
-            void exports(String pn) {
-                mv.visitVarInsn(ALOAD, BUILDER_VAR);
-
-                mv.visitLdcInsn(pn);
-                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                        "exports", STRING_SIG, false);
-                mv.visitInsn(POP);
-            }
-
-            /*
-             * Invoke Builder.exports(String pn, Set<String> targets)
+             * Invoke
+             *     Builder.exports(Set<Exports.Modifier> ms, String pn,
+             *                                  Set<String> targets)
+             * or
+             *     Builder.exports(Set<Exports.Modifier> ms, String pn)
              *
              * Set<String> targets = new HashSet<>();
              * targets.add(t);
              * :
              * :
-             * Builder.exports(pn, targets);
+             *
+             * Set<Modifier> mods = ...
+             * Builder.exports(mods, pn, targets);
              */
-            void exports(String pn, Set<String> targets) {
-                int varIndex = stringSets.get(targets).build();
+            void exports(Set<Exports.Modifier> ms,
+                         String pn,
+                         Set<String> targets) {
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
-                mv.visitLdcInsn(pn);
-                mv.visitVarInsn(ALOAD, varIndex);
-                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
-                                   "exports", STRING_SET_SIG, false);
+                int modifiersSetIndex = dedupSetBuilder.indexOfExportsModifiers(ms);
+
+                if (!targets.isEmpty()) {
+                    int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets);
+                    mv.visitVarInsn(ALOAD, modifiersSetIndex);
+                    mv.visitLdcInsn(pn);
+                    mv.visitVarInsn(ALOAD, stringSetIndex);
+                    mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
+                                       "exports",
+                                       EXPORTS_MODIFIER_SET_STRING_SET_SIG, false);
+                } else {
+                    mv.visitVarInsn(ALOAD, modifiersSetIndex);
+                    mv.visitLdcInsn(pn);
+                    mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
+                                       "exports",
+                                       EXPORTS_MODIFIER_SET_STRING_SIG, false);
+                }
                 mv.visitInsn(POP);
             }
 
@@ -629,7 +599,7 @@
              * Invokes Builder.uses(Set<String> uses)
              */
             void uses(Set<String> uses) {
-                int varIndex = stringSets.get(uses).build();
+                int varIndex = dedupSetBuilder.indexOfStringSet(uses);
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitVarInsn(ALOAD, varIndex);
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
@@ -647,7 +617,7 @@
              * Builder.exports(service, providers);
              */
             void provides(String service, Set<String> providers) {
-                int varIndex = stringSets.get(providers).build();
+                int varIndex = dedupSetBuilder.indexOfStringSet(providers);
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitLdcInsn(service);
                 mv.visitVarInsn(ALOAD, varIndex);
@@ -661,8 +631,7 @@
              */
             void packages(Set<String> packages) {
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
-                int varIndex = new StringSetBuilder(packages).build();
-                assert varIndex == STRING_SET_VAR;
+                int varIndex = dedupSetBuilder.newStringSet(packages);
                 mv.visitVarInsn(ALOAD, varIndex);
                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                                    "packages", SET_SIG, false);
@@ -691,6 +660,9 @@
                 mv.visitInsn(POP);
             }
 
+            /*
+             * Invoke Builder.algorithm(String a);
+             */
             void algorithm(String alg) {
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitLdcInsn(alg);
@@ -699,6 +671,9 @@
                 mv.visitInsn(POP);
             }
 
+            /*
+             * Invoke Builder.moduleHash(String name, String hashString);
+             */
             void moduleHash(String name, String hashString) {
                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
                 mv.visitLdcInsn(name);
@@ -710,80 +685,237 @@
         }
 
         /*
-         * StringSetBuilder generates bytecode to create one single instance
-         * of HashSet for a given set of names and assign to a local variable
-         * slot.  When there is only one single reference to a Set<String>,
-         * it will reuse STRING_SET_VAR for reference.  For Set<String> with
-         * multiple references, it will use a new local variable.
+         * Wraps set creation, ensuring identical sets are properly deduplicated.
          */
-        class StringSetBuilder {
-            final Set<String> names;
-            final boolean linked;
-            int refCount;
-            int localVarIndex;
+        class DedupSetBuilder {
+            // map Set<String> to a specialized builder to allow them to be
+            // deduplicated as they are requested
+            final Map<Set<String>, SetBuilder<String>> stringSets = new HashMap<>();
 
-            StringSetBuilder(Set<String> names, boolean linked) {
-                this.names = names;
-                this.linked = linked;
+            // map Set<Requires.Modifier> to a specialized builder to allow them to be
+            // deduplicated as they are requested
+            final Map<Set<Requires.Modifier>, EnumSetBuilder<Requires.Modifier>>
+                requiresModifiersSets = new HashMap<>();
+
+            // map Set<Exports.Modifier> to a specialized builder to allow them to be
+            // deduplicated as they are requested
+            final Map<Set<Exports.Modifier>, EnumSetBuilder<Exports.Modifier>>
+                exportsModifiersSets = new HashMap<>();
+
+            private final int stringSetVar;
+            private final int enumSetVar;
+            private final IntSupplier localVarSupplier;
+
+            DedupSetBuilder(IntSupplier localVarSupplier) {
+                this.stringSetVar = localVarSupplier.getAsInt();
+                this.enumSetVar = localVarSupplier.getAsInt();
+                this.localVarSupplier = localVarSupplier;
             }
 
-            StringSetBuilder(Set<String> names) {
-                this(names, false);
+            /*
+             * Add the given set of names to this builder
+             */
+            void stringSet(Set<String> names) {
+                stringSet(names, false);
             }
 
-            void increment() {
+            /*
+             * Add the given set of names to this builder.
+             *
+             * If preserveIterationOrder is true, the builder creates a set
+             * that preserves the order, for example, LinkedHashSet.
+             */
+            void stringSet(Set<String> names, boolean preserveIterationOrder) {
+                stringSets.computeIfAbsent(names,
+                    s -> new SetBuilder<>(s, stringSetVar, localVarSupplier)
+                ).iterationOrder(preserveIterationOrder).increment();
+            }
+
+            /*
+             * Add the given set of Exports.Modifiers
+             */
+            void exportsModifiers(Set<Exports.Modifier> mods) {
+                exportsModifiersSets.computeIfAbsent(mods, s ->
+                    new EnumSetBuilder<>(s, EXPORTS_MODIFIER_CLASSNAME,
+                                         enumSetVar, localVarSupplier)
+                ).increment();
+            }
+
+            /*
+             * Add the given set of Requires.Modifiers
+             */
+            void requiresModifiers(Set<Requires.Modifier> mods) {
+                requiresModifiersSets.computeIfAbsent(mods, s ->
+                    new EnumSetBuilder<>(s, REQUIRES_MODIFIER_CLASSNAME,
+                                         enumSetVar, localVarSupplier)
+                ).increment();
+            }
+
+            /*
+             * Retrieve the index to the given set of Strings. Emit code to
+             * generate it when SetBuilder::build is called.
+             */
+            int indexOfStringSet(Set<String> names) {
+                return stringSets.get(names).build();
+            }
+
+            /*
+             * Retrieve the index to the given set of Exports.Modifier.
+             * Emit code to generate it when EnumSetBuilder::build is called.
+             */
+            int indexOfExportsModifiers(Set<Exports.Modifier> mods) {
+                return exportsModifiersSets.get(mods).build();
+            }
+
+            /*
+             * Retrieve the index to the given set of Requires.Modifier.
+             * Emit code to generate it when EnumSetBuilder::build is called.
+             */
+            int indexOfRequiresModifiers(Set<Requires.Modifier> mods) {
+                return requiresModifiersSets.get(mods).build();
+            }
+
+            /*
+             * Build a new string set without any attempt to deduplicate it.
+             */
+            int newStringSet(Set<String> names) {
+                int index = new SetBuilder<>(names, stringSetVar, localVarSupplier).build();
+                assert index == stringSetVar;
+                return index;
+            }
+        }
+
+        /*
+         * SetBuilder generates bytecode to create one single instance of Set
+         * for a given set of elements and assign to a local variable slot.
+         * When there is only one single reference to a Set<T>,
+         * it will reuse defaultVarIndex.  For a Set with multiple references,
+         * it will use a new local variable retrieved from the nextLocalVar
+         */
+        class SetBuilder<T> {
+            private final Set<T> elements;
+            private final int defaultVarIndex;
+            private final IntSupplier nextLocalVar;
+            private boolean linked;
+            private int refCount;
+            private int localVarIndex;
+
+            SetBuilder(Set<T> elements,
+                       int defaultVarIndex,
+                       IntSupplier nextLocalVar) {
+                this.elements = elements;
+                this.defaultVarIndex = defaultVarIndex;
+                this.nextLocalVar = nextLocalVar;
+            }
+
+            /*
+             * Marks that the builder should maintain the iteration order of
+             * the elements, i.e., use a LinkedHashSet.
+             */
+            final SetBuilder<T> iterationOrder(boolean preserveOrder) {
+                this.linked = preserveOrder;
+                return this;
+            }
+
+            /*
+             * Increments the number of references to this particular set.
+             */
+            final void increment() {
                 refCount++;
             }
 
+            /**
+             * Generate the appropriate instructions to load an object reference
+             * to the element onto the stack.
+             */
+            void visitElement(T element, MethodVisitor mv) {
+                mv.visitLdcInsn(element);
+            }
+
             /*
-             * Build bytecode for the Set<String> represented by this builder,
+             * Build bytecode for the Set represented by this builder,
              * or get the local variable index of a previously generated set
              * (in the local scope).
              *
              * @return local variable index of the generated set.
              */
-            int build() {
+            final int build() {
                 int index = localVarIndex;
                 if (localVarIndex == 0) {
                     // if non-empty and more than one set reference this builder,
                     // emit to a unique local
-                    index = refCount <= 1 ? STRING_SET_VAR
-                                          : nextLocalVar++;
+                    index = refCount <= 1 ? defaultVarIndex
+                                          : nextLocalVar.getAsInt();
                     if (index < MAX_LOCAL_VARS) {
                         localVarIndex = index;
                     } else {
-                        // overflow: disable optimization and keep localVarIndex = 0
-                        index = STRING_SET_VAR;
+                        // overflow: disable optimization by using localVarIndex = 0
+                        index = defaultVarIndex;
                     }
 
-                    if (names.isEmpty()) {
+                    if (elements.isEmpty()) {
                         mv.visitMethodInsn(INVOKESTATIC, "java/util/Collections",
-                                "emptySet", "()Ljava/util/Set;", false);
+                                           "emptySet",
+                                           "()Ljava/util/Set;",
+                                           false);
                         mv.visitVarInsn(ASTORE, index);
-                    } else if (names.size() == 1) {
-                        mv.visitLdcInsn(names.iterator().next());
+                    } else if (elements.size() == 1) {
+                        visitElement(elements.iterator().next(), mv);
                         mv.visitMethodInsn(INVOKESTATIC, "java/util/Collections",
-                                "singleton", "(Ljava/lang/Object;)Ljava/util/Set;", false);
+                                           "singleton",
+                                           "(Ljava/lang/Object;)Ljava/util/Set;",
+                                           false);
                         mv.visitVarInsn(ASTORE, index);
                     } else {
                         String cn = linked ? "java/util/LinkedHashSet" : "java/util/HashSet";
                         mv.visitTypeInsn(NEW, cn);
                         mv.visitInsn(DUP);
-                        pushInt(initialCapacity(names.size()));
+                        pushInt(initialCapacity(elements.size()));
                         mv.visitMethodInsn(INVOKESPECIAL, cn, "<init>", "(I)V", false);
 
                         mv.visitVarInsn(ASTORE, index);
-                        for (String t : names) {
+                        for (T t : elements) {
                             mv.visitVarInsn(ALOAD, index);
-                            mv.visitLdcInsn(t);
+                            visitElement(t, mv);
                             mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set",
-                                    "add", "(Ljava/lang/Object;)Z", true);
+                                               "add",
+                                               "(Ljava/lang/Object;)Z", true);
                             mv.visitInsn(POP);
                         }
+                        mv.visitVarInsn(ALOAD, index);
+                        mv.visitMethodInsn(INVOKESTATIC, "java/util/Collections",
+                                           "unmodifiableSet",
+                                           "(Ljava/util/Set;)Ljava/util/Set;",
+                                           false);
+                        mv.visitVarInsn(ASTORE, index);
                     }
                 }
                 return index;
             }
         }
+
+        /*
+         * Generates bytecode to create one single instance of EnumSet
+         * for a given set of modifiers and assign to a local variable slot.
+         */
+        class EnumSetBuilder<T> extends SetBuilder<T> {
+
+            private final String className;
+
+            EnumSetBuilder(Set<T> modifiers, String className,
+                           int defaultVarIndex,
+                           IntSupplier nextLocalVar) {
+                super(modifiers, defaultVarIndex, nextLocalVar);
+                this.className = className;
+            }
+
+            /**
+             * Loads a Enum field.
+             */
+            void visitElement(T t, MethodVisitor mv) {
+                mv.visitFieldInsn(GETSTATIC, className, t.toString(),
+                                  "L" + className + ";");
+            }
+        }
     }
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Thu Sep 08 17:42:08 2016 -0700
@@ -75,7 +75,7 @@
 this flag is not specified a default set of classes will be generated. To \n\
 disable pre-generation specify none as the argument
 
-installed-modules.description=Fast loading of module descriptors (always enabled)
+system-modules.description=Fast loading of module descriptors (always enabled)
 
 onoff.argument=<on|off>
 
--- a/src/jdk.naming.dns/share/classes/module-info.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/jdk.naming.dns/share/classes/module-info.java	Thu Sep 08 17:42:08 2016 -0700
@@ -26,8 +26,8 @@
 module jdk.naming.dns {
     requires java.naming;
 
-    exports com.sun.jndi.url.dns to
-        java.naming;
+    // temporary export until NamingManager.getURLContext uses services
+    exports dynamic com.sun.jndi.url.dns to java.naming;
 
     provides javax.naming.spi.InitialContextFactory
         with com.sun.jndi.dns.DnsContextFactory;
--- a/src/jdk.naming.rmi/share/classes/module-info.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/src/jdk.naming.rmi/share/classes/module-info.java	Thu Sep 08 17:42:08 2016 -0700
@@ -30,7 +30,7 @@
         with com.sun.jndi.rmi.registry.RegistryContextFactory;
 
     // temporary export until NamingManager.getURLContext uses services
-    exports com.sun.jndi.url.rmi to java.naming;
-    exports com.sun.jndi.rmi.registry to java.rmi;
+    exports dynamic com.sun.jndi.url.rmi to java.naming;
+    exports dynamic com.sun.jndi.rmi.registry to java.rmi;
 }
 
--- a/test/java/lang/Class/getResource/Main.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/test/java/lang/Class/getResource/Main.java	Thu Sep 08 17:42:08 2016 -0700
@@ -56,7 +56,7 @@
 
         // invoke Class getResource from the unnamed module
         URL url0 = Main.class.getResource("/" + NAME);
-        assertNull(url0);
+        assertNotNull(url0);
 
         // invoke Class getResource from modules m1-m3
         URL url1 = p1.Main.getResource("/" + NAME);
@@ -72,7 +72,7 @@
 
         // invoke Class getResourceAsStream from the unnamed module
         InputStream in0 = Main.class.getResourceAsStream("/" + NAME);
-        assertNull(in0);
+        assertNotNull(in0);
 
         // invoke Class getResourceAsStream from modules m1-m3
         try (InputStream in = p1.Main.getResourceAsStream("/" + NAME)) {
--- a/test/java/lang/ClassLoader/getResource/modules/Main.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/test/java/lang/ClassLoader/getResource/modules/Main.java	Thu Sep 08 17:42:08 2016 -0700
@@ -52,24 +52,20 @@
         assertTrue(Files.notExists(directoryFor("m3").resolve(NAME)));
 
         // invoke ClassLoader getResource from the unnamed module
-        assertNull(Main.class.getClassLoader().getResource("/" + NAME));
+        assertNotNull(Main.class.getClassLoader().getResource("/" + NAME));
 
         // invoke ClassLoader getResource from modules m1-m3
-        // Resources in a named module are private to that module.
-        // ClassLoader.getResource should not find resource in named modules.
-        assertNull(p1.Main.getResourceInClassLoader("/" + NAME));
-        assertNull(p2.Main.getResourceInClassLoader("/" + NAME));
-        assertNull(p3.Main.getResourceInClassLoader("/" + NAME));
+        assertNotNull(p1.Main.getResourceInClassLoader("/" + NAME));
+        assertNotNull(p2.Main.getResourceInClassLoader("/" + NAME));
+        assertNotNull(p3.Main.getResourceInClassLoader("/" + NAME));
 
         // invoke ClassLoader getResourceAsStream from the unnamed module
-        assertNull(Main.class.getClassLoader().getResourceAsStream("/" + NAME));
+        assertNotNull(Main.class.getClassLoader().getResourceAsStream("/" + NAME));
 
         // invoke ClassLoader getResourceAsStream from modules m1-m3
-        // Resources in a named module are private to that module.
-        // ClassLoader.getResourceAsStream should not find resource in named modules.
-        assertNull(p1.Main.getResourceAsStreamInClassLoader("/" + NAME));
-        assertNull(p2.Main.getResourceAsStreamInClassLoader("/" + NAME));
-        assertNull(p3.Main.getResourceAsStreamInClassLoader("/" + NAME));
+        assertNotNull(p1.Main.getResourceAsStreamInClassLoader("/" + NAME));
+        assertNotNull(p2.Main.getResourceAsStreamInClassLoader("/" + NAME));
+        assertNotNull(p3.Main.getResourceAsStreamInClassLoader("/" + NAME));
 
         // SecurityManager case
         System.setSecurityManager(new SecurityManager());
@@ -108,5 +104,9 @@
     static void assertNull(Object o) {
         assertTrue(o == null);
     }
+
+    static void assertNotNull(Object o) {
+        assertTrue(o != null);
+    }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/instrument/RedefineModuleAgent.java	Thu Sep 08 17:42:08 2016 -0700
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+import java.lang.instrument.Instrumentation;
+import java.lang.reflect.Module;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Agent used by RedefineModuleTest
+ */
+
+public class RedefineModuleAgent {
+
+    private static Instrumentation inst;
+
+    public static void premain(String args, Instrumentation inst) throws Exception {
+        RedefineModuleAgent.inst = inst;
+    }
+
+    static void redefineModule(Module module,
+                               Set<Module> extraReads,
+                               Map<String, Set<Module>> extraExports,
+                               Set<Class<?>> extraUses,
+                               Map<Class<?>, Set<Class<?>>> extraProvides) {
+        inst.redefineModule(module, extraReads, extraExports, extraUses, extraProvides);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/instrument/RedefineModuleTest.java	Thu Sep 08 17:42:08 2016 -0700
@@ -0,0 +1,310 @@
+/*
+ * 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.
+ */
+
+/**
+ * @test
+ * @summary Basic test for redefineModule
+ * @modules java.instrument
+ * @build java.base/java.lang.TestProvider
+ *        java.base/jdk.internal.test.TestProviderImpl1
+ *        java.base/jdk.internal.test.TestProviderImpl2
+ * @run shell MakeJAR3.sh RedefineModuleAgent
+ * @run testng/othervm -javaagent:RedefineModuleAgent.jar RedefineModuleTest
+ */
+
+import java.lang.TestProvider;
+import java.lang.instrument.Instrumentation;
+import java.lang.reflect.Layer;
+import java.lang.reflect.Module;
+import java.net.URLStreamHandler;
+import java.net.spi.URLStreamHandlerProvider;
+import java.nio.file.FileSystems;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class RedefineModuleTest {
+
+    static void redefineModule(Module module,
+                               Set<Module> extraReads,
+                               Map<String, Set<Module>> extraExports,
+                               Set<Class<?>> extraUses,
+                               Map<Class<?>, Set<Class<?>>> extraProvides) {
+        RedefineModuleAgent.redefineModule(module,
+                                           extraReads,
+                                           extraExports,
+                                           extraUses,
+                                           extraProvides);
+    }
+
+
+    /**
+     * Use redefineModule to update java.base to read java.instrument
+     */
+    public void testAddReads() {
+        Module baseModule = Object.class.getModule();
+        Module instrumentModule = Instrumentation.class.getModule();
+
+        // pre-conditions
+        assertFalse(baseModule.canRead(instrumentModule));
+
+        // update java.base to read java.instrument
+        redefineModule(baseModule, Set.of(instrumentModule), Map.of(), Set.of(), Map.of());
+        assertTrue(baseModule.canRead(instrumentModule));
+    }
+
+    /**
+     * Use redefineModule to update java.base to export jdk.internal.misc
+     */
+    public void testAddExports() {
+        Module baseModule = Object.class.getModule();
+        Module thisModule = this.getClass().getClassLoader().getUnnamedModule();
+        String pkg = "jdk.internal.misc";
+
+        // pre-conditions
+        assertFalse(baseModule.isExported(pkg));
+        assertFalse(baseModule.isExported(pkg, thisModule));
+
+        // update java.base to export jdk.internal.misc to an unnamed module
+        Map<String, Set<Module>> extraExports = Map.of(pkg, Set.of(thisModule));
+        redefineModule(baseModule, Set.of(), extraExports, Set.of(), Map.of());
+        assertFalse(baseModule.isExported(pkg));
+        assertTrue(baseModule.isExported(pkg, thisModule));
+
+        // update java.base to export jdk.internal.misc unconditionally
+        extraExports = Map.of(pkg, Set.of());
+        redefineModule(baseModule, Set.of(), extraExports, Set.of(), Map.of());
+        assertTrue(baseModule.isExported(pkg));
+        assertTrue(baseModule.isExported(pkg, thisModule));
+    }
+
+    /**
+     * Use redefineModule to update java.base to use TestProvider and
+     * provide implementations of TestProvider.
+     */
+    public void testAddUsesAndProvides() throws Exception {
+        Module baseModule = Object.class.getModule();
+        Class<TestProvider> service = TestProvider.class;
+
+        // pre-conditions
+        assertFalse(baseModule.canUse(service));
+        assertTrue(collect(ServiceLoader.load(service)).isEmpty());
+        assertTrue(collect(ServiceLoader.load(Layer.boot(), service)).isEmpty());
+
+        // update java.base to use TestProvider
+        redefineModule(baseModule, Set.of(), Map.of(), Set.of(service), Map.of());
+        assertTrue(baseModule.canUse(service));
+        assertTrue(collect(ServiceLoader.load(service)).isEmpty());
+        assertTrue(collect(ServiceLoader.load(Layer.boot(), service)).isEmpty());
+
+        // update java.base to provide an implementation of TestProvider
+        Class<?> type1 = Class.forName("jdk.internal.test.TestProviderImpl1");
+        Map<Class<?>, Set<Class<?>>> extraProvides = Map.of(service, Set.of(type1));
+        redefineModule(baseModule, Set.of(), Map.of(), Set.of(), extraProvides);
+
+        // invoke ServiceLoader from java.base to find providers
+        Set<TestProvider> providers = collect(TestProvider.providers());
+        assertTrue(providers.size() == 1);
+        assertTrue(containsInstanceOf(providers, type1));
+
+        // use ServiceLoader to load implementations visible via TCCL
+        providers = collect(ServiceLoader.load(service));
+        assertTrue(collect(providers).size() == 1);
+        assertTrue(containsInstanceOf(collect(providers), type1));
+
+        // use ServiceLoader to load implementations in the boot layer
+        providers = collect(ServiceLoader.load(Layer.boot(), service));
+        assertTrue(collect(providers).size() == 1);
+        assertTrue(containsInstanceOf(collect(providers), type1));
+
+        // update java.base to provide a second implementation of TestProvider
+        Class<?> type2 = Class.forName("jdk.internal.test.TestProviderImpl2");
+        extraProvides = Map.of(service, Set.of(type2));
+        redefineModule(baseModule, Set.of(), Map.of(), Set.of(), extraProvides);
+
+        // invoke ServiceLoader from java.base to find providers
+        providers = collect(TestProvider.providers());
+        assertTrue(providers.size() == 2);
+        assertTrue(containsInstanceOf(providers, type1));
+        assertTrue(containsInstanceOf(providers, type2));
+
+        // use ServiceLoader to load implementations visible via TCCL
+        providers = collect(ServiceLoader.load(service));
+        assertTrue(collect(providers).size() == 2);
+        assertTrue(containsInstanceOf(providers, type1));
+        assertTrue(containsInstanceOf(providers, type2));
+
+        // use ServiceLoader to load implementations in the boot layer
+        providers = collect(ServiceLoader.load(Layer.boot(), service));
+        assertTrue(collect(providers).size() == 2);
+        assertTrue(containsInstanceOf(providers, type1));
+        assertTrue(containsInstanceOf(providers, type2));
+    }
+
+    private <S> Set<S> collect(Iterable<S> sl) {
+        Set<S> providers = new HashSet<>();
+        sl.forEach(providers::add);
+        return providers;
+    }
+
+    private boolean containsInstanceOf(Collection<?> c, Class<?> type) {
+        for (Object o : c) {
+            if (type.isInstance(o)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Test redefineClass by attempting to update java.base to export a package
+     * that it does not contain.
+     */
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testExportPackageNotInModule() {
+        Module baseModule = Object.class.getModule();
+        String pkg = "jdk.doesnotexist";
+
+        // attempt to update java.base to export jdk.doesnotexist
+        Map<String, Set<Module>> extraExports = Map.of(pkg, Set.of());
+        redefineModule(baseModule, Set.of(), extraExports, Set.of(), Map.of());
+    }
+
+    /**
+     * Test redefineClass by attempting to update java.base to provide a service
+     * where the service provider class is not in the module.
+     */
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testProvideServiceNotInModule() {
+        Module baseModule = Object.class.getModule();
+        class MyProvider extends URLStreamHandlerProvider {
+            @Override
+            public URLStreamHandler createURLStreamHandler(String protocol) {
+                return null;
+            }
+        }
+
+        // attempt to update java.base to provide MyProvider
+        Map<Class<?>, Set<Class<?>>> extraProvides
+            = Map.of(URLStreamHandlerProvider.class, Set.of(MyProvider.class));
+        redefineModule(baseModule, Set.of(), Map.of(), Set.of(), extraProvides);
+    }
+
+    /**
+     * Test redefineClass by attempting to update java.base to provide a
+     * service where the service provider class is not a sub-type.
+     */
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testProvideServiceNotSubtype() {
+        Module baseModule = Object.class.getModule();
+
+        Class<?> service = TestProvider.class;
+        Class<?> impl = FileSystems.getDefault().provider().getClass();
+
+        // attempt to update java.base to provide an implementation of TestProvider
+        Map<Class<?>, Set<Class<?>>> extraProvides = Map.of(service, Set.of(impl));
+        redefineModule(baseModule, Set.of(), Map.of(), Set.of(), extraProvides);
+    }
+
+    /**
+     * Test redefineClass with null
+     */
+    public void testNulls() {
+        Module baseModule = Object.class.getModule();
+
+        try {
+            redefineModule(null, Set.of(), Map.of(), Set.of(), Map.of());
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+
+        try {
+            redefineModule(baseModule, null, Map.of(), Set.of(), Map.of());
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+
+        try {
+            redefineModule(baseModule, Set.of(), null, Set.of(), Map.of());
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+
+        try {
+            redefineModule(baseModule, Set.of(), Map.of(), null, Map.of());
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+
+        try {
+            redefineModule(baseModule, Set.of(), Map.of(), Set.of(), null);
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+
+        try {
+            Set<Module> containsNull = new HashSet<>();
+            containsNull.add(null);
+            redefineModule(baseModule, containsNull, Map.of(), Set.of(), Map.of());
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+
+        try {
+            Map<String, Set<Module>> extraExports = new HashMap<>();
+            extraExports.put(null, Set.of());
+            redefineModule(baseModule, Set.of(), extraExports, Set.of(), Map.of());
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+
+        try {
+            Set<Module> containsNull = new HashSet<>();
+            containsNull.add(null);
+            Map<String, Set<Module>> extraExports = Map.of("java.lang", containsNull);
+            redefineModule(baseModule, Set.of(), extraExports, Set.of(), Map.of());
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+
+        try {
+            Set<Class<?>> containsNull = new HashSet<>();
+            containsNull.add(null);
+            redefineModule(baseModule, Set.of(), Map.of(), containsNull, Map.of());
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+
+        try {
+            Map<Class<?>, Set<Class<?>>> extraProvides = new HashMap<>();
+            extraProvides.put(null, Set.of());
+            redefineModule(baseModule, Set.of(), Map.of(), Set.of(), extraProvides);
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+
+        try {
+            Set<Class<?>> containsNull = new HashSet<>();
+            containsNull.add(null);
+            Map<Class<?>, Set<Class<?>>> extraProvides = Map.of(TestProvider.class, containsNull);
+            redefineModule(baseModule, Set.of(), Map.of(), Set.of(), extraProvides);
+            assertTrue(false);
+        } catch (NullPointerException e) { }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/instrument/java.base/java/lang/TestProvider.java	Thu Sep 08 17:42:08 2016 -0700
@@ -0,0 +1,32 @@
+/*
+ * 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 java.lang;
+
+import java.util.ServiceLoader;
+
+public interface TestProvider {
+    public static Iterable<TestProvider> providers() {
+        return ServiceLoader.load(TestProvider.class);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/instrument/java.base/jdk/internal/test/TestProviderImpl1.java	Thu Sep 08 17:42:08 2016 -0700
@@ -0,0 +1,31 @@
+/*
+ * 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.test;
+
+import java.lang.TestProvider;
+
+public class TestProviderImpl1 implements TestProvider {
+    public TestProviderImpl1() { }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/instrument/java.base/jdk/internal/test/TestProviderImpl2.java	Thu Sep 08 17:42:08 2016 -0700
@@ -0,0 +1,31 @@
+/*
+ * 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.test;
+
+import java.lang.TestProvider;
+
+public class TestProviderImpl2 implements TestProvider {
+    public TestProviderImpl2() { }
+}
+
--- a/test/java/lang/module/AutomaticModulesTest.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/test/java/lang/module/AutomaticModulesTest.java	Thu Sep 08 17:42:08 2016 -0700
@@ -462,7 +462,7 @@
 
         ModuleDescriptor descriptor2
             =  new ModuleDescriptor.Builder("m2")
-                .requires(Modifier.PUBLIC, "m3")
+                .requires(Set.of(Modifier.PUBLIC), "m3")
                 .requires("java.base")
                 .build();
 
--- a/test/java/lang/module/ConfigurationTest.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/test/java/lang/module/ConfigurationTest.java	Thu Sep 08 17:42:08 2016 -0700
@@ -114,7 +114,7 @@
 
         ModuleDescriptor descriptor2
             = new ModuleDescriptor.Builder("m2")
-                .requires(Modifier.PUBLIC, "m3")
+                .requires(Set.of(Modifier.PUBLIC), "m3")
                 .build();
 
         ModuleDescriptor descriptor3
@@ -169,7 +169,7 @@
 
         ModuleDescriptor descriptor2
             = new ModuleDescriptor.Builder("m2")
-                .requires(Modifier.PUBLIC, "m1")
+                .requires(Set.of(Modifier.PUBLIC), "m1")
                 .build();
 
         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
@@ -245,7 +245,7 @@
 
         ModuleDescriptor descriptor2
             = new ModuleDescriptor.Builder("m2")
-                .requires(Modifier.PUBLIC, "m1")
+                .requires(Set.of(Modifier.PUBLIC), "m1")
                 .build();
 
         ModuleDescriptor descriptor3
@@ -309,7 +309,7 @@
 
         ModuleDescriptor descriptor2
             = new ModuleDescriptor.Builder("m2")
-                .requires(Modifier.PUBLIC, "m1")
+                .requires(Set.of(Modifier.PUBLIC), "m1")
                 .build();
 
         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2);
@@ -371,7 +371,7 @@
 
         ModuleDescriptor descriptor2
             = new ModuleDescriptor.Builder("m2")
-                .requires(Modifier.PUBLIC, "m1")
+                .requires(Set.of(Modifier.PUBLIC), "m1")
                 .build();
 
         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
@@ -398,7 +398,7 @@
 
         ModuleDescriptor descriptor3
             = new ModuleDescriptor.Builder("m3")
-                .requires(Modifier.PUBLIC, "m2")
+                .requires(Set.of(Modifier.PUBLIC), "m2")
                 .build();
 
         ModuleDescriptor descriptor4
@@ -435,6 +435,217 @@
 
 
     /**
+     * Basic test of "requires static":
+     *     m1 requires static m2
+     *     m2 is not observable
+     *     resolve m1
+     */
+    public void testRequiresStatic1() {
+        ModuleDescriptor descriptor1
+            = new ModuleDescriptor.Builder("m1")
+                .requires(Set.of(Modifier.STATIC), "m2")
+                .build();
+
+        ModuleFinder finder = ModuleUtils.finderOf(descriptor1);
+
+        Configuration cf = resolveRequires(finder, "m1");
+
+        assertTrue(cf.modules().size() == 1);
+
+        ResolvedModule m1 = cf.findModule("m1").get();
+        assertTrue(m1.reads().size() == 0);
+    }
+
+
+    /**
+     * Basic test of "requires static":
+     *     m1 requires static m2
+     *     m2
+     *     resolve m1
+     */
+    public void testRequiresStatic2() {
+        ModuleDescriptor descriptor1
+            = new ModuleDescriptor.Builder("m1")
+                .requires(Set.of(Modifier.STATIC), "m2")
+                .build();
+
+        ModuleDescriptor descriptor2
+            = new ModuleDescriptor.Builder("m2")
+                .build();
+
+        ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
+
+        Configuration cf = resolveRequires(finder, "m1");
+
+        assertTrue(cf.modules().size() == 1);
+
+        ResolvedModule m1 = cf.findModule("m1").get();
+        assertTrue(m1.reads().size() == 0);
+    }
+
+
+    /**
+     * Basic test of "requires static":
+     *     m1 requires static m2
+     *     m2
+     *     resolve m1, m2
+     */
+    public void testRequiresStatic3() {
+        ModuleDescriptor descriptor1
+            = new ModuleDescriptor.Builder("m1")
+                .requires(Set.of(Modifier.STATIC), "m2")
+                .build();
+
+        ModuleDescriptor descriptor2
+            = new ModuleDescriptor.Builder("m2")
+                .build();
+
+        ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
+
+        Configuration cf = resolveRequires(finder, "m1", "m2");
+
+        assertTrue(cf.modules().size() == 2);
+
+        ResolvedModule m1 = cf.findModule("m1").get();
+        ResolvedModule m2 = cf.findModule("m2").get();
+
+        assertTrue(m1.reads().size() == 1);
+        assertTrue(m1.reads().contains(m2));
+
+        assertTrue(m2.reads().size() == 0);
+    }
+
+
+    /**
+     * Basic test of "requires static":
+     *     m1 requires m2, m3
+     *     m2 requires static m2
+     *     m3
+     */
+    public void testRequiresStatic4() {
+        ModuleDescriptor descriptor1
+            = new ModuleDescriptor.Builder("m1")
+                .requires("m2")
+                .requires("m3")
+                .build();
+
+        ModuleDescriptor descriptor2
+            = new ModuleDescriptor.Builder("m2")
+                .requires(Set.of(Modifier.STATIC), "m3")
+                .build();
+
+        ModuleDescriptor descriptor3
+            = new ModuleDescriptor.Builder("m3")
+                .build();
+
+        ModuleFinder finder
+                = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
+
+        Configuration cf = resolveRequires(finder, "m1");
+
+        assertTrue(cf.modules().size() == 3);
+
+        ResolvedModule m1 = cf.findModule("m1").get();
+        ResolvedModule m2 = cf.findModule("m2").get();
+        ResolvedModule m3 = cf.findModule("m3").get();
+
+        assertTrue(m1.reads().size() == 2);
+        assertTrue(m1.reads().contains(m2));
+        assertTrue(m1.reads().contains(m3));
+
+        assertTrue(m2.reads().size() == 1);
+        assertTrue(m2.reads().contains(m3));
+
+        assertTrue(m3.reads().size() == 0);
+    }
+
+
+    /**
+     * Basic test of "requires static":
+     * The test consists of three configurations:
+     * - Configuration cf1: m1, m2
+     * - Configuration cf2: m3 requires m1, requires static m2
+     */
+    public void testRequiresStatic5() {
+        ModuleDescriptor descriptor1
+            = new ModuleDescriptor.Builder("m1")
+                .build();
+
+        ModuleDescriptor descriptor2
+            = new ModuleDescriptor.Builder("m2")
+                .build();
+
+        ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
+
+        Configuration cf1 = resolveRequires(finder1, "m1", "m2");
+
+        assertTrue(cf1.modules().size() == 2);
+        assertTrue(cf1.findModule("m1").isPresent());
+        assertTrue(cf1.findModule("m2").isPresent());
+
+        ModuleDescriptor descriptor3
+            = new ModuleDescriptor.Builder("m3")
+                .requires("m1")
+                .requires(Set.of(Modifier.STATIC), "m2")
+                .build();
+
+        ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3);
+
+        Configuration cf2 = resolveRequires(cf1, finder2, "m3");
+
+        assertTrue(cf2.modules().size() == 1);
+        assertTrue(cf2.findModule("m3").isPresent());
+
+        ResolvedModule m1 = cf1.findModule("m1").get();
+        ResolvedModule m2 = cf1.findModule("m2").get();
+        ResolvedModule m3 = cf2.findModule("m3").get();
+
+        assertTrue(m3.reads().size() == 2);
+        assertTrue(m3.reads().contains(m1));
+        assertTrue(m3.reads().contains(m2));
+    }
+
+
+    /**
+     * Basic test of "requires static":
+     * The test consists of three configurations:
+     * - Configuration cf1: m1
+     * - Configuration cf2: m3 requires m1, requires static m2
+     */
+    public void testRequiresStatic6() {
+        ModuleDescriptor descriptor1
+            = new ModuleDescriptor.Builder("m1")
+                .build();
+
+        ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
+
+        Configuration cf1 = resolveRequires(finder1, "m1");
+
+        assertTrue(cf1.modules().size() == 1);
+        assertTrue(cf1.findModule("m1").isPresent());
+
+        ModuleDescriptor descriptor3
+            = new ModuleDescriptor.Builder("m3")
+                .requires("m1")
+                .requires(Set.of(Modifier.STATIC), "m2")
+                .build();
+
+        ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3);
+
+        Configuration cf2 = resolveRequires(cf1, finder2, "m3");
+
+        assertTrue(cf2.modules().size() == 1);
+        assertTrue(cf2.findModule("m3").isPresent());
+
+        ResolvedModule m1 = cf1.findModule("m1").get();
+        ResolvedModule m3 = cf2.findModule("m3").get();
+
+        assertTrue(m3.reads().size() == 1);
+        assertTrue(m3.reads().contains(m1));
+    }
+
+
+    /**
      * Basic test of binding services
      *     m1 uses p.S
      *     m2 provides p.S
@@ -917,7 +1128,7 @@
 
         ModuleDescriptor descriptor2
             = new ModuleDescriptor.Builder("m2")
-                .requires(Modifier.PUBLIC, "m1")
+                .requires(Set.of(Modifier.PUBLIC), "m1")
                 .build();
 
         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
--- a/test/java/lang/module/ModuleDescriptorTest.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/test/java/lang/module/ModuleDescriptorTest.java	Thu Sep 08 17:42:08 2016 -0700
@@ -80,21 +80,17 @@
             .next();
     }
 
+    private Requires requires(String mn) {
+        return requires(Collections.emptySet(), mn);
+    }
+
     public void testRequiresWithRequires() {
-        Requires r1 = requires(null, "foo");
+        Requires r1 = requires("foo");
         ModuleDescriptor descriptor = new Builder("m").requires(r1).build();
         Requires r2 = descriptor.requires().iterator().next();
         assertEquals(r1, r2);
     }
 
-    public void testRequiresWithNullModifiers() {
-        Requires r = requires(null, "foo");
-        assertEquals(r, r);
-        assertTrue(r.compareTo(r) == 0);
-        assertTrue(r.modifiers().isEmpty());
-        assertEquals(r.name(), "foo");
-    }
-
     public void testRequiresWithNoModifiers() {
         Requires r = requires(EnumSet.noneOf(Requires.Modifier.class), "foo");
         assertEquals(r, r);
@@ -123,19 +119,19 @@
         Requires r = requires(EnumSet.allOf(Modifier.class), "foo");
         assertEquals(r, r);
         assertTrue(r.compareTo(r) == 0);
-        assertEquals(r.modifiers(), EnumSet.allOf(Modifier.class));
+        assertEquals(r.modifiers(), EnumSet.of(PUBLIC, STATIC, SYNTHETIC, MANDATED));
         assertEquals(r.name(), "foo");
     }
 
     @Test(expectedExceptions = IllegalStateException.class)
     public void testRequiresWithDuplicatesRequires() {
-        Requires r = requires(null, "foo");
+        Requires r = requires("foo");
         new Builder("m").requires(r).requires(r);
     }
 
     @Test(expectedExceptions = IllegalArgumentException.class)
     public void testRequiresSelfWithRequires() {
-        Requires r = requires(null, "foo");
+        Requires r = requires("foo");
         new Builder("foo").requires(r);
     }
 
@@ -146,7 +142,7 @@
 
     @Test(expectedExceptions = IllegalArgumentException.class)
     public void testRequiresSelfWithOneModifier() {
-        new Builder("m").requires(PUBLIC, "m");
+        new Builder("m").requires(Set.of(PUBLIC), "m");
     }
 
     @Test(expectedExceptions = IllegalArgumentException.class)
@@ -214,6 +210,25 @@
             .next();
     }
 
+    private Exports exports(Set<Exports.Modifier> mods, String pn) {
+        return new Builder("foo")
+            .exports(mods, pn)
+            .build()
+            .exports()
+            .iterator()
+            .next();
+    }
+
+    private Exports exports(Set<Exports.Modifier> mods, String pn, Set<String> targets) {
+        return new Builder("foo")
+            .exports(mods, pn, targets)
+            .build()
+            .exports()
+            .iterator()
+            .next();
+    }
+
+
     public void testExportsExports() {
         Exports e1 = exports("p");
         ModuleDescriptor descriptor = new Builder("m").exports(e1).build();
@@ -224,6 +239,7 @@
     public void testExportsToAll() {
         Exports e = exports("p");
         assertEquals(e, e);
+        assertTrue(e.modifiers().isEmpty());
         assertEquals(e.source(), "p");
         assertFalse(e.isQualified());
         assertTrue(e.targets().isEmpty());
@@ -232,6 +248,7 @@
     public void testExportsToTarget() {
         Exports e = exports("p", "bar");
         assertEquals(e, e);
+        assertTrue(e.modifiers().isEmpty());
         assertEquals(e.source(), "p");
         assertTrue(e.isQualified());
         assertTrue(e.targets().size() == 1);
@@ -250,6 +267,7 @@
                 .iterator()
                 .next();
         assertEquals(e, e);
+        assertTrue(e.modifiers().isEmpty());
         assertEquals(e.source(), "p");
         assertTrue(e.isQualified());
         assertTrue(e.targets().size() == 2);
@@ -257,6 +275,28 @@
         assertTrue(e.targets().contains("gus"));
     }
 
+    public void testExportsToAllWithModifier() {
+        Exports e = exports(Set.of(Exports.Modifier.DYNAMIC), "p");
+        assertEquals(e, e);
+        assertTrue(e.modifiers().size() == 1);
+        assertTrue(e.modifiers().contains(Exports.Modifier.DYNAMIC));
+        assertEquals(e.source(), "p");
+        assertFalse(e.isQualified());
+        assertTrue(e.targets().isEmpty());
+    }
+
+    public void testExportsToTargetWithModifier() {
+        Exports e = exports(Set.of(Exports.Modifier.DYNAMIC), "p", Set.of("bar"));
+        assertEquals(e, e);
+        assertTrue(e.modifiers().size() == 1);
+        assertTrue(e.modifiers().contains(Exports.Modifier.DYNAMIC));
+        assertEquals(e.source(), "p");
+        assertTrue(e.isQualified());
+        assertTrue(e.targets().size() == 1);
+        assertTrue(e.targets().contains("bar"));
+    }
+
+
     @Test(expectedExceptions = IllegalStateException.class)
     public void testExportsWithDuplicate1() {
         Exports e = exports("p");
@@ -528,8 +568,11 @@
 
     // isAutomatic
     public void testIsAutomatic() {
-        ModuleDescriptor descriptor = new Builder("foo").build();
-        assertFalse(descriptor.isAutomatic());
+        ModuleDescriptor descriptor1 = new Builder("foo").build();
+        assertFalse(descriptor1.isAutomatic());
+
+        ModuleDescriptor descriptor2 = new Builder("foo").automatic().build();
+        assertTrue(descriptor2.isAutomatic());
     }
 
     // isSynthetic
--- a/test/java/lang/reflect/Layer/BasicLayerTest.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/test/java/lang/reflect/Layer/BasicLayerTest.java	Thu Sep 08 17:42:08 2016 -0700
@@ -32,6 +32,7 @@
 
 import java.lang.module.Configuration;
 import java.lang.module.ModuleDescriptor;
+import static java.lang.module.ModuleDescriptor.Requires.Modifier;
 import java.lang.module.ModuleFinder;
 import java.lang.reflect.Layer;
 import java.lang.reflect.LayerInstantiationException;
@@ -383,7 +384,7 @@
 
         ModuleDescriptor descriptor2
             = new ModuleDescriptor.Builder("m2")
-                .requires(ModuleDescriptor.Requires.Modifier.PUBLIC, "m1")
+                .requires(Set.of(Modifier.PUBLIC), "m1")
                 .build();
 
         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
@@ -464,7 +465,7 @@
 
         ModuleDescriptor descriptor2
             = new ModuleDescriptor.Builder("m2")
-                .requires(ModuleDescriptor.Requires.Modifier.PUBLIC, "m1")
+                .requires(Set.of(Modifier.PUBLIC), "m1")
                 .build();
 
         ModuleDescriptor descriptor3
@@ -532,7 +533,7 @@
 
         ModuleDescriptor descriptor2
             = new ModuleDescriptor.Builder("m2")
-                .requires(ModuleDescriptor.Requires.Modifier.PUBLIC, "m1")
+                .requires(Set.of(Modifier.PUBLIC), "m1")
                 .build();
 
         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2);
@@ -600,7 +601,7 @@
 
         ModuleDescriptor descriptor2
             = new ModuleDescriptor.Builder("m2")
-                .requires(ModuleDescriptor.Requires.Modifier.PUBLIC, "m1")
+                .requires(Set.of(Modifier.PUBLIC), "m1")
                 .build();
 
         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
@@ -615,7 +616,7 @@
 
         ModuleDescriptor descriptor3
             = new ModuleDescriptor.Builder("m3")
-                .requires(ModuleDescriptor.Requires.Modifier.PUBLIC, "m2")
+                .requires(Set.of(Modifier.PUBLIC), "m2")
                 .build();
 
         ModuleDescriptor descriptor4
--- a/test/java/lang/reflect/Layer/LayerAndLoadersTest.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/test/java/lang/reflect/Layer/LayerAndLoadersTest.java	Thu Sep 08 17:42:08 2016 -0700
@@ -30,6 +30,8 @@
  * @summary Tests for java.lang.reflect.Layer@createWithXXX methods
  */
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.module.Configuration;
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleFinder;
@@ -38,8 +40,10 @@
 import java.lang.reflect.LayerInstantiationException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Module;
+import java.net.URL;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -568,6 +572,48 @@
     }
 
 
+    /**
+     * Basic test of resource loading with a class loader created by
+     * Layer.defineModulesWithOneLoader.
+     */
+    public void testResourcesOneLoader() throws Exception {
+        Configuration cf = resolveRequires("m1");
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        Layer layer = Layer.boot().defineModulesWithOneLoader(cf, scl);
+        ClassLoader loader = layer.findLoader("m1");
+        testResourceLoading(loader, "p/Main.class");
+    }
+
+    /**
+     * Basic test of resource loading with a class loader created by
+     * Layer.defineModulesWithOneLoader.
+     */
+    public void testResourcesManyLoaders() throws Exception {
+        Configuration cf = resolveRequires("m1");
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        Layer layer = Layer.boot().defineModulesWithManyLoaders(cf, scl);
+        ClassLoader loader = layer.findLoader("m1");
+        testResourceLoading(loader, "p/Main.class");
+    }
+
+    /**
+     * Test that a resource is located by a class loader.
+     */
+    private void testResourceLoading(ClassLoader loader, String name)
+        throws IOException
+    {
+        URL url = loader.getResource(name);
+        assertNotNull(url);
+
+        try (InputStream in = loader.getResourceAsStream(name)) {
+            assertNotNull(in);
+        }
+
+        Enumeration<URL> urls = loader.getResources(name);
+        assertTrue(urls.hasMoreElements());
+    }
+
+
     // -- supporting methods --
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/reflect/Module/AnnotationsTest.java	Thu Sep 08 17:42:08 2016 -0700
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleFinder;
+import java.lang.reflect.Layer;
+import java.lang.reflect.Module;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import jdk.internal.module.ClassFileAttributes;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Opcodes;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ *          java.base/jdk.internal.module
+ *          java.xml
+ * @run testng AnnotationsTest
+ * @summary Basic test of annotations on modules
+ */
+
+public class AnnotationsTest {
+
+    /**
+     * Test that there are no annotations on an unnamed module.
+     */
+    @Test
+    public void testUnnamedModule() {
+        Module module = this.getClass().getModule();
+        assertTrue(module.getAnnotations().length == 0);
+    }
+
+    /**
+     * Test loading a module with a RuntimeVisibleAnnotation attribute.
+     * The test copies the module-info.class for java.xml, adds the attribute,
+     * and then loads the updated module.
+     */
+    @Test
+    public void testNamedModule() throws IOException {
+
+        // "deprecate" java.xml
+        Path dir = Files.createTempDirectory("mods");
+        deprecateModule("java.xml", true, "9", dir);
+
+        // "load" the cloned java.xml
+        Module module = loadModule(dir, "java.xml");
+
+        // check the annotation is present
+        assertTrue(module.isAnnotationPresent(Deprecated.class));
+        Deprecated d = module.getAnnotation(Deprecated.class);
+        assertNotNull(d, "@Deprecated not found");
+        assertTrue(d.forRemoval());
+        assertEquals(d.since(), "9");
+        Annotation[] a = module.getAnnotations();
+        assertTrue(a.length == 1);
+        assertTrue(a[0] instanceof Deprecated);
+    }
+
+
+    /**
+     * Copy the module-info.class for the given module, add the
+     * Deprecated annotation, and write the updated module-info.class
+     * to a directory.
+     */
+    static void deprecateModule(String name,
+                                boolean forRemoval,
+                                String since,
+                                Path output) throws IOException {
+        Module module = Layer.boot().findModule(name).orElse(null);
+        assertNotNull(module, name + " not found");
+
+        InputStream in = module.getResourceAsStream("module-info.class");
+        assertNotNull(in, "No module-info.class for " + name);
+
+        try (in) {
+            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
+                                             + ClassWriter.COMPUTE_FRAMES);
+
+            ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw) { };
+
+            ClassReader cr = new ClassReader(in);
+
+            List<Attribute> attrs = new ArrayList<>();
+            attrs.add(new ClassFileAttributes.ModuleAttribute());
+            attrs.add(new ClassFileAttributes.ConcealedPackagesAttribute());
+            attrs.add(new ClassFileAttributes.VersionAttribute());
+            attrs.add(new ClassFileAttributes.TargetPlatformAttribute());
+            cr.accept(cv, attrs.toArray(new Attribute[0]), 0);
+
+            AnnotationVisitor annotationVisitor
+                = cv.visitAnnotation("Ljava/lang/Deprecated;", true);
+            annotationVisitor.visit("forRemoval", forRemoval);
+            annotationVisitor.visit("since", since);
+            annotationVisitor.visitEnd();
+
+            byte[] bytes = cw.toByteArray();
+            Path mi = output.resolve("module-info.class");
+            Files.write(mi, bytes);
+        }
+    }
+
+    /**
+     * Load the module of the given name in the given directory into a
+     * child layer.
+     */
+    static Module loadModule(Path dir, String name) throws IOException {
+        ModuleFinder finder = ModuleFinder.of(dir);
+
+        Layer bootLayer = Layer.boot();
+
+        Configuration cf = bootLayer.configuration()
+                .resolveRequires(finder, ModuleFinder.of(), Set.of(name));
+
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        Layer layer = bootLayer.defineModulesWithOneLoader(cf, scl);
+
+        Module module = layer.findModule(name).orElse(null);
+        assertNotNull(module, name + " not loaded");
+        return module;
+    }
+}
--- a/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java	Thu Sep 08 21:11:54 2016 +0000
+++ b/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java	Thu Sep 08 17:42:08 2016 -0700
@@ -22,11 +22,11 @@
  */
 
 import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleDescriptor.Requires.Modifier;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.lang.module.ModuleDescriptor.Exports;
 import java.lang.module.ModuleFinder;
 import java.lang.module.ModuleReference;
 import java.util.Collections;
-import java.util.EnumSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -61,8 +61,16 @@
         assertUnmodifiable(md.conceals(), "conceal");
         assertUnmodifiable(md.packages(), "package");
         assertUnmodifiable(md.requires(),
-                           jlma.newRequires(EnumSet.allOf(Modifier.class), "require"));
-        assertUnmodifiable(md.exports(), jlma.newExports("export"));
+                           jlma.newRequires(Set.of(Requires.Modifier.PUBLIC), "require"));
+        for (Requires req : md.requires()) {
+            assertUnmodifiable(req.modifiers(), Requires.Modifier.PUBLIC);
+        }
+
+        assertUnmodifiable(md.exports(), jlma.newExports(Set.of(), "export", Set.of()));
+        for (Exports exp : md.exports()) {
+            assertUnmodifiable(exp.modifiers(), Exports.Modifier.DYNAMIC);
+        }
+
         assertUnmodifiable(md.uses(), "use");
         assertUnmodifiable(md.provides(), "provide",
                            jlma.newProvides("provide", Collections.singleton("provide")));