changeset 17756:b2361ae612b9

Merge
author mchung
date Thu, 15 Sep 2016 13:14:46 -0700
parents 54c5931849a3 b25ebe289b7c
children 680a8e8684b0
files make/gendata/GendataBreakIterator.gmk src/java.base/share/classes/com/sun/java/util/jar/pack/intrinsic.properties src/java.base/share/classes/java/lang/ClassLoader.java src/java.base/share/classes/java/lang/Package.java src/java.base/share/classes/java/lang/invoke/MethodHandles.java src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java src/java.base/share/classes/java/lang/module/Dependence.java src/java.base/share/classes/jdk/internal/module/ConfigurableModuleFinder.java src/java.base/share/classes/sun/util/locale/provider/BreakDictionary.java src/java.base/share/classes/sun/util/locale/provider/RuleBasedBreakIterator.java src/java.instrument/share/classes/java/lang/instrument/package.html src/java.instrument/share/native/libinstrument/JPLISAgent.c src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java test/ProblemList.txt test/java/lang/Class/getResource/src/m3/module-info.java test/java/lang/Class/getResource/src/m3/p3/Main.java test/java/lang/ClassLoader/getResource/modules/src/m3/module-info.java test/java/lang/ClassLoader/getResource/modules/src/m3/p3/Main.java test/java/lang/module/ModuleReader/MultiReleaseJarTest.java test/java/lang/reflect/Module/access/src/target/p/Exported.java test/java/lang/reflect/Module/access/src/target/p/Helper.java test/java/lang/reflect/Module/access/src/target/q/Internal.java test/java/time/TEST.properties test/java/util/Collection/MOAT.java test/java/util/ResourceBundle/modules/basic/src/asiabundles/jdk/test/resources/MyResources_ja_JP.properties test/java/util/ResourceBundle/modules/security/TestPermission.java test/java/util/ServiceLoader/modules/MiscTests.java test/java/util/ServiceLoader/modules/ServicesTest.java test/java/util/ServiceLoader/modules/src/bananascript/module-info.java test/java/util/ServiceLoader/modules/src/bananascript/org/banana/BananaScript.java test/java/util/ServiceLoader/modules/src/bananascript/org/banana/BananaScriptEngineFactory.java test/java/util/ServiceLoader/modules/src/test/module-info.java test/java/util/ServiceLoader/modules/src/test/test/Main.java test/java/util/concurrent/ScheduledThreadPoolExecutor/ZeroCoreThreads.java test/java/util/concurrent/tck/JSR166TestCase.java test/java/util/logging/modules/GetResourceBundleTest.java test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java test/sun/text/resources/LocaleDataTest.java test/tools/jlink/plugins/IncludeLocalesPluginTest.java
diffstat 364 files changed, 11460 insertions(+), 4756 deletions(-) [+]
line wrap: on
line diff
--- a/.jcheck/conf	Mon Sep 12 22:04:48 2016 +0000
+++ b/.jcheck/conf	Thu Sep 15 13:14:46 2016 -0700
@@ -1,1 +1,4 @@
 project=jdk9
+comments=lax
+tags=lax
+bugids=dup
--- a/make/copy/Copy-java.base.gmk	Mon Sep 12 22:04:48 2016 +0000
+++ b/make/copy/Copy-java.base.gmk	Thu Sep 15 13:14:46 2016 -0700
@@ -29,6 +29,31 @@
 
 ################################################################################
 #
+# Copy copyright, license, third-party readme files.
+
+JAVA_BASE_OTHER_FILES ?= LICENSE ASSEMBLY_EXCEPTION THIRD_PARTY_README
+JDK_DOC_LOCATION ?= $(JDK_TOPDIR)
+
+JAVA_BASE_OTHER_FILES_TARGETS := $(addprefix $(OTHER_FILES_DST_DIR)/, $(JAVA_BASE_OTHER_FILES))
+
+# Processing license files from source area to image area
+# These are modified to have the platform specific EOL chars.
+define process-doc-file
+	$(call LogInfo, Processing $(patsubst $(OUTPUT_ROOT)/%,%,$@))
+	$(MKDIR) -p $(@D)
+	$(RM) $@
+	LC_ALL=C $(SED) 's/$$//g' $< > $@
+	$(CHMOD) 444 $@
+endef
+
+$(OTHER_FILES_DST_DIR)/%: $(JDK_DOC_LOCATION)/%
+	$(process-doc-file)
+
+TARGETS += \
+    $(JAVA_BASE_OTHER_FILES_TARGETS)
+
+################################################################################
+#
 # Copy exported header files to outputdir.
 #
 TARGETS += \
--- a/make/copy/CopyCommon.gmk	Mon Sep 12 22:04:48 2016 +0000
+++ b/make/copy/CopyCommon.gmk	Thu Sep 15 13:14:46 2016 -0700
@@ -26,6 +26,7 @@
 INCLUDE_DST_DIR := $(SUPPORT_OUTPUTDIR)/modules_include/$(MODULE)
 LIB_DST_DIR := $(SUPPORT_OUTPUTDIR)/modules_libs/$(MODULE)
 CONF_DST_DIR := $(SUPPORT_OUTPUTDIR)/modules_conf/$(MODULE)
+OTHER_FILES_DST_DIR := $(SUPPORT_OUTPUTDIR)/modules_other_files/$(MODULE)
 
 INCLUDE_DST_OS_DIR := $(INCLUDE_DST_DIR)/$(OPENJDK_TARGET_OS)
 
--- a/make/src/classes/build/tools/jigsaw/GenGraphs.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/make/src/classes/build/tools/jigsaw/GenGraphs.java	Thu Sep 15 13:14:46 2016 -0700
@@ -43,7 +43,7 @@
 import java.util.TreeSet;
 import java.util.function.Function;
 import java.util.stream.Collectors;
-import static java.lang.module.ModuleDescriptor.Requires.Modifier.PUBLIC;
+import static java.lang.module.ModuleDescriptor.Requires.Modifier.TRANSITIVE;
 
 /**
  * Generate the DOT file for a module graph for each module in the JDK
@@ -172,14 +172,14 @@
             Graph<String> graph = gengraph(cf);
             descriptors.forEach(md -> {
                 String mn = md.name();
-                Set<String> requiresPublic = md.requires().stream()
-                        .filter(d -> d.modifiers().contains(PUBLIC))
+                Set<String> requiresTransitive = md.requires().stream()
+                        .filter(d -> d.modifiers().contains(TRANSITIVE))
                         .map(d -> d.name())
                         .collect(Collectors.toSet());
 
                 graph.adjacentNodes(mn).forEach(dn -> {
                     String attr = dn.equals("java.base") ? REQUIRES_BASE
-                            : (requiresPublic.contains(dn) ? REEXPORTS : REQUIRES);
+                            : (requiresTransitive.contains(dn) ? REEXPORTS : REQUIRES);
                     int w = weightOf(mn, dn);
                     if (w > 1)
                         attr += "weight=" + w;
@@ -194,8 +194,8 @@
     /**
      * Returns a Graph of the given Configuration after transitive reduction.
      *
-     * Transitive reduction of requires public edge and requires edge have
-     * to be applied separately to prevent the requires public edges
+     * Transitive reduction of requires transitive edge and requires edge have
+     * to be applied separately to prevent the requires transitive edges
      * (e.g. U -> V) from being reduced by a path (U -> X -> Y -> V)
      * in which  V would not be re-exported from U.
      */
@@ -208,21 +208,21 @@
                     .map(ResolvedModule::name)
                     .forEach(target -> builder.addEdge(mn, target));
         }
-        Graph<String> rpg = requiresPublicGraph(cf);
+        Graph<String> rpg = requiresTransitiveGraph(cf);
         return builder.build().reduce(rpg);
     }
 
     /**
-     * Returns a Graph containing only requires public edges
+     * Returns a Graph containing only requires transitive edges
      * with transitive reduction.
      */
-    private Graph<String> requiresPublicGraph(Configuration cf) {
+    private Graph<String> requiresTransitiveGraph(Configuration cf) {
         Graph.Builder<String> builder = new Graph.Builder<>();
         for (ResolvedModule resolvedModule : cf.modules()) {
             ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
             String mn = descriptor.name();
             descriptor.requires().stream()
-                    .filter(d -> d.modifiers().contains(PUBLIC))
+                    .filter(d -> d.modifiers().contains(TRANSITIVE))
                     .map(d -> d.name())
                     .forEach(d -> builder.addEdge(mn, d));
         }
--- a/make/src/classes/build/tools/jigsaw/ModuleSummary.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/make/src/classes/build/tools/jigsaw/ModuleSummary.java	Thu Sep 15 13:14:46 2016 -0700
@@ -299,7 +299,7 @@
     static class HtmlDocument {
         final String title;
         final Map<String, ModuleSummary> modules;
-        boolean requiresPublicNote = false;
+        boolean requiresTransitiveNote = false;
         boolean aggregatorNote = false;
         boolean totalBytesNote = false;
         HtmlDocument(String title, Map<String, ModuleSummary> modules) {
@@ -510,16 +510,16 @@
             public String requiresColumn() {
                 StringBuilder sb = new StringBuilder();
                 sb.append(String.format("<td>"));
-                boolean footnote = requiresPublicNote;
+                boolean footnote = requiresTransitiveNote;
                 ms.descriptor().requires().stream()
                         .sorted(Comparator.comparing(Requires::name))
                         .forEach(r -> {
-                            boolean requiresPublic = r.modifiers().contains(Requires.Modifier.PUBLIC);
-                            Selector sel = requiresPublic ? REQUIRES_PUBLIC : REQUIRES;
+                            boolean requiresTransitive = r.modifiers().contains(Requires.Modifier.TRANSITIVE);
+                            Selector sel = requiresTransitive ? REQUIRES_PUBLIC : REQUIRES;
                             String req = String.format("<a class=\"%s\" href=\"#%s\">%s</a>",
                                                        sel, r.name(), r.name());
-                            if (!requiresPublicNote && requiresPublic) {
-                                requiresPublicNote = true;
+                            if (!requiresTransitiveNote && requiresTransitive) {
+                                requiresTransitiveNote = true;
                                 req += "<sup>*</sup>";
                             }
                             sb.append(req).append("\n").append("<br>");
@@ -534,8 +534,8 @@
                     sb.append("<br>");
                     sb.append("<i>+").append(indirectDeps).append(" transitive dependencies</i>");
                 }
-                if (footnote != requiresPublicNote) {
-                    sb.append("<br><br>").append("<i>* bold denotes requires public</i>");
+                if (footnote != requiresTransitiveNote) {
+                    sb.append("<br><br>").append("<i>* bold denotes requires transitive</i>");
                 }
                 sb.append("</td>");
                 return sb.toString();
--- a/make/src/classes/build/tools/module/GenModuleInfoSource.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/make/src/classes/build/tools/module/GenModuleInfoSource.java	Thu Sep 15 13:14:46 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
@@ -30,13 +30,17 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * A build tool to extend the module-info.java in the source tree for
@@ -51,10 +55,12 @@
     private final static String USAGE =
         "Usage: GenModuleInfoSource [option] -o <output file> <module-info-java>\n" +
         "Options are:\n" +
-        "  -exports  <package-name>\n" +
-        "  -exports  <package-name>[/<module-name>]\n" +
-        "  -uses     <service>\n" +
-        "  -provides <service>/<provider-impl-classname>\n";
+        "  --exports  <package-name>\n" +
+        "  --exports  <package-name>[/<module-name>]\n" +
+        "  --exports-private  <package-name>\n" +
+        "  --exports-private  <package-name>[/<module-name>]\n" +
+        "  --uses     <service>\n" +
+        "  --provides <service>/<provider-impl-classname>\n";
 
     public static void main(String... args) throws Exception {
         Path outfile = null;
@@ -66,21 +72,23 @@
             String option = args[i];
             if (option.startsWith("-")) {
                 String arg = args[++i];
-                if (option.equals("-exports")) {
+                if (option.equals("--exports") ||
+                    option.equals("--exports-private")) {
+                    Set<Exports.Modifier> modifiers = toModifiers(option);
                     int index = arg.indexOf('/');
-                        if (index > 0) {
-                            String pn = arg.substring(0, index);
-                            String mn = arg.substring(index + 1, arg.length());
-                            genModuleInfo.exportTo(pn, mn);
-                        } else {
-                            genModuleInfo.export(arg);
-                        }
-                } else if (option.equals("-uses")) {
+                    if (index > 0) {
+                        String pn = arg.substring(0, index);
+                        String mn = arg.substring(index + 1, arg.length());
+                        genModuleInfo.exportTo(modifiers, pn, mn);
+                    } else {
+                        genModuleInfo.export(modifiers, arg);
+                    }
+                } else if (option.equals("--uses")) {
                     genModuleInfo.use(arg);
-                } else if (option.equals("-provides")) {
+                } else if (option.equals("--provides")) {
                         int index = arg.indexOf('/');
                         if (index <= 0) {
-                            throw new IllegalArgumentException("invalid -provide argument: " + arg);
+                            throw new IllegalArgumentException("invalid --provide argument: " + arg);
                         }
                         String service = arg.substring(0, index);
                         String impl = arg.substring(index + 1, arg.length());
@@ -109,27 +117,87 @@
         genModuleInfo.generate(moduleInfoJava, outfile);
     }
 
-    private final Set<String> exports = new HashSet<>();
-    private final Map<String, Set<String>> exportsTo = new HashMap<>();
+    static class Exports {
+        public static enum Modifier {
+            PRIVATE,
+        }
+
+        final Set<Modifier> mods;
+        final String source;
+        final Set<String> targets;  // empty if unqualified export
+
+        Exports(String source) {
+            this(Collections.emptySet(), source, Collections.emptySet());
+        }
+
+        Exports(Set<Modifier> ms, String source) {
+            this(ms, source, Collections.emptySet());
+        }
+
+        Exports(Set<Modifier> ms, String source, Set<String> targets) {
+            this.mods = ms;
+            this.source = source;
+            this.targets = targets;
+        }
+
+        String source() {
+            return source;
+        }
+
+        Set<String> targets() {
+            return targets;
+        }
+
+        boolean isQualified() {
+            return !targets.isEmpty();
+        }
+    }
+
+
+    static Set<Exports.Modifier> toModifiers(String option) {
+        switch (option) {
+            case "--exports":
+                return Collections.emptySet();
+            case "--exports-private":
+                return Collections.singleton(Exports.Modifier.PRIVATE);
+            default:
+                throw new IllegalArgumentException(option);
+        }
+    }
+
+    private final Map<String, Set<Exports>> exports = new HashMap<>();
+    private final Map<String, Set<Exports>> exportsTo = new HashMap<>();
     private final Set<String> uses = new HashSet<>();
     private final Map<String, Set<String>> provides = new HashMap<>();
     GenModuleInfoSource() {
     }
 
-    private void export(String p) {
+    private void export(Set<Exports.Modifier> mods, String p) {
         Objects.requireNonNull(p);
-        if (exports.contains(p) || exportsTo.containsKey(p)) {
-            throw new RuntimeException("duplicated exports: " + p);
-        }
-        exports.add(p);
+        Set<Exports> exps = exports.computeIfAbsent(p, _k -> new HashSet<>());
+        Optional<Exports> oe = exps.stream()
+            .filter(e -> e.mods.equals(mods))
+            .findFirst();
+        if (oe.isPresent())
+            throw new IllegalArgumentException("duplicate exports " +
+                        toString(mods, p));
+         exps.add(new Exports(mods, p));
     }
-    private void exportTo(String p, String mn) {
+
+    private void exportTo(Set<Exports.Modifier> mods, String p, String mn) {
         Objects.requireNonNull(p);
         Objects.requireNonNull(mn);
-        if (exports.contains(p)) {
-            throw new RuntimeException("unqualified exports already exists: " + p);
+        Set<Exports> exps = exportsTo.computeIfAbsent(p, _k -> new HashSet<>());
+        Optional<Exports> oe = exps.stream()
+            .filter(e -> e.mods.equals(mods))
+            .findFirst();
+        if (oe.isPresent()) {
+            oe.get().targets.add(mn);
+        } else {
+            Set<String> targets = new HashSet<>();
+            targets.add(mn);
+            exps.add(new Exports(mods, p, targets));
         }
-        exportsTo.computeIfAbsent(p, _k -> new HashSet<>()).add(mn);
     }
 
     private void use(String service) {
@@ -149,21 +217,36 @@
         try (BufferedWriter bw = Files.newBufferedWriter(outfile);
              PrintWriter writer = new PrintWriter(bw)) {
             int lineNumber = 0;
+            String pn = null;
+            Set<Exports.Modifier> mods = null;
             for (String l : lines) {
                 lineNumber++;
                 String[] s = l.trim().split("\\s+");
                 String keyword = s[0].trim();
                 int nextIndex = keyword.length();
-                String exp = null;
                 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) {
-                            nextIndex = l.indexOf(exp, nextIndex) + exp.length();
-                            if (s[2].trim().equals("to")) {
+                        // exports <package-name> [to <target-module>]
+                        // exports private <package-name> [to <target-module>]
+
+                        String token = s[1].trim();
+                        int nextTokenPos = 2;
+                        pn = null;
+                        mods = Collections.emptySet();
+
+                        if (token.equals("private")) {
+                            mods = Collections.singleton(Exports.Modifier.valueOf(token.toUpperCase()));
+                            pn = s.length >= 3 ? s[2].trim() : null;
+                            nextTokenPos = 3;
+                        } else {
+                            pn = token;
+                        }
+
+                        if (s.length > nextTokenPos) {
+                            nextIndex = l.indexOf(pn, nextIndex) + pn.length();
+                            if (s[nextTokenPos].trim().equals("to")) {
                                 inExportsTo = true;
                                 n = l.indexOf("to", nextIndex) + "to".length();
                             } else {
@@ -174,18 +257,18 @@
 
                         // inject the extra targets after "to"
                         if (inExportsTo) {
-                            writer.println(injectExportTargets(exp, l, n));
+                            writer.println(injectExportTargets(mods, pn, l, n));
                         } else {
                             writer.println(l);
                         }
                         break;
                     case "to":
-                        if (exp == null) {
+                        if (pn == null) {
                             throw new RuntimeException(sourcefile + ", line " +
                                 lineNumber + ", is malformed");
                         }
                         n = l.indexOf("to", nextIndex) + "to".length();
-                        writer.println(injectExportTargets(exp, l, n));
+                        writer.println(injectExportTargets(mods, pn, l, n));
                         break;
                     case "}":
                         doAugments(writer);
@@ -193,46 +276,61 @@
                     default:
                         writer.println(l);
                         // reset exports
-                        exp = null;
+                        pn = null;
                 }
             }
         }
     }
 
-    private String injectExportTargets(String pn, String exp, 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))
-              .append("\n\t")
-              .append(targets.stream()
-                             .collect(Collectors.joining(",", "", ",")))
-              .append(" /* injected */");
-            if (pos < exp.length()) {
-                // print the remaining statement followed "to"
-                sb.append("\n\t")
-                  .append(exp.substring(pos+1, exp.length()));
+    private String injectExportTargets(Set<Exports.Modifier> mods, String pn,
+                                       String exports, int pos) {
+        if (exportsTo.containsKey(pn)) {
+            Optional<Exports> oe = exportsTo.get(pn).stream()
+                .filter(e -> e.mods.equals(mods))
+                .findFirst();
+            if (oe.isPresent()) {
+                Exports exp = oe.get();
+                exportsTo.get(pn).remove(exp);
+                StringBuilder sb = new StringBuilder();
+                // inject the extra targets after the given pos
+                sb.append(exports.substring(0, pos))
+                    .append("\n\t")
+                    .append(exp.targets.stream()
+                        .collect(Collectors.joining(", ", "", ",")))
+                    .append(" /* injected */");
+                if (pos < exports.length()) {
+                    // print the remaining statement followed "to"
+                    sb.append("\n\t")
+                      .append(exports.substring(pos + 1, exports.length()));
+                }
+                return sb.toString();
             }
-            return sb.toString();
-        } else {
-            return exp;
         }
+        return exports;
     }
 
     private void doAugments(PrintWriter writer) {
-        if ((exports.size() + exportsTo.size() + uses.size() + provides.size()) == 0)
+        long exps = exports.values().stream()
+                           .flatMap(Set::stream).count() +
+                    exportsTo.values().stream()
+                           .flatMap(Set::stream).count();
+        if ((exps + uses.size() + provides.size()) == 0)
             return;
 
+        writer.println();
         writer.println("    // augmented from module-info.java.extra");
-        exports.stream()
-            .sorted()
-            .forEach(e -> writer.format("    exports %s;%n", e));
+        exports.values().stream()
+            .flatMap(Set::stream)
+            .sorted(Comparator.comparing(Exports::source))
+            .forEach(e -> writer.format("    exports %s;%n",
+                                        toString(e.mods, e.source)));
         // remaining injected qualified exports
-        exportsTo.entrySet().stream()
-            .sorted(Map.Entry.comparingByKey())
-            .map(e -> String.format("    exports %s to%n%s;", e.getKey(),
-                                    e.getValue().stream().sorted()
+        exportsTo.values().stream()
+            .flatMap(Set::stream)
+            .sorted(Comparator.comparing(Exports::source))
+            .map(e -> String.format("    exports %s to%n%s;",
+                                    toString(e.mods, e.source),
+                                    e.targets().stream().sorted()
                                         .map(mn -> String.format("        %s", mn))
                                         .collect(Collectors.joining(",\n"))))
             .forEach(writer::println);
@@ -245,4 +343,14 @@
                                                       e.getKey(), impl)))
             .forEach(writer::println);
     }
+
+    static <T> Stream<String> toStringStream(Set<T> s) {
+        return s.stream().map(e -> e.toString().toLowerCase());
+    }
+
+    static <M> String toString(Set<M> mods, String what) {
+        return (Stream.concat(toStringStream(mods), Stream.of(what)))
+            .collect(Collectors.joining(" "));
+    }
+
 }
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/intrinsic.properties	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/com/sun/java/util/jar/pack/intrinsic.properties	Thu Sep 15 13:14:46 2016 -0700
@@ -15,8 +15,8 @@
 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.ConcealedPackages = NH[RUH]
+pack.class.attribute.Module = FHNH[RUHFH]NH[RUHFHNH[RUH]]NH[RCH]NH[RCHRCH]
+pack.class.attribute.Packages = NH[RUH]
 pack.class.attribute.Version = RUH
 pack.class.attribute.MainClass = RCH
 pack.class.attribute.TargetPlatform = RUHRUHRUH
--- a/src/java.base/share/classes/java/lang/Class.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/Class.java	Thu Sep 15 13:14:46 2016 -0700
@@ -26,46 +26,55 @@
 package java.lang;
 
 import java.lang.annotation.Annotation;
+import java.lang.module.ModuleDescriptor.Version;
+import java.lang.module.ModuleFinder;
 import java.lang.module.ModuleReader;
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Array;
-import java.lang.reflect.GenericArrayType;
-import java.lang.reflect.GenericDeclaration;
-import java.lang.reflect.Member;
-import java.lang.reflect.Field;
-import java.lang.reflect.Executable;
-import java.lang.reflect.Method;
-import java.lang.reflect.Module;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.AnnotatedType;
-import java.lang.reflect.Proxy;
 import java.lang.ref.SoftReference;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.ObjectStreamField;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Layer;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Module;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
 import java.net.URL;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Objects;
 import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
 import jdk.internal.HotSpotIntrinsicCandidate;
 import jdk.internal.loader.BootLoader;
 import jdk.internal.loader.BuiltinClassLoader;
+import jdk.internal.loader.ResourceHelper;
+import jdk.internal.misc.SharedSecrets;
 import jdk.internal.misc.Unsafe;
 import jdk.internal.misc.VM;
+import jdk.internal.module.ModuleHashes;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.ConstantPool;
 import jdk.internal.reflect.Reflection;
@@ -442,21 +451,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);
         }
     }
 
@@ -979,7 +977,7 @@
     }
 
     // cached package name
-    private String packageName;
+    private transient String packageName;
 
     /**
      * Returns the interfaces directly implemented by the class or interface
@@ -2367,12 +2365,17 @@
     }
 
     /**
-     * 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
-     * associated with a given class are implemented by the defining
-     * {@linkplain ClassLoader class loader} of the class.  This method
+     * Finds a resource with a given name.
+     *
+     * <p> If this class is in a named {@link Module Module} then this method
+     * will attempt to find the resource in the module by means of the absolute
+     * resource name, subject to the rules for encapsulation specified in the
+     * {@code Module} {@link Module#getResourceAsStream getResourceAsStream}
+     * method.
+     *
+     * <p> Otherwise, if this class is not in a named module then 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
      * the bootstrap class loader, the method delegates to {@link
      * ClassLoader#getSystemResourceAsStream}.
@@ -2400,8 +2403,10 @@
      * </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, the resource is in a package
+     *          that is not exported-private to the caller module, or access to
+     *          the resource is denied by the security manager.
      * @throws  NullPointerException If {@code name} is {@code null}
      * @since  1.1
      */
@@ -2409,35 +2414,42 @@
     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;
+            if (!ResourceHelper.isSimpleResource(name)) {
+                Module caller = Reflection.getCallerClass().getModule();
+                if (caller != module) {
+                    Set<String> packages = module.getDescriptor().packages();
+                    String pn = ResourceHelper.getPackageName(name);
+                    if (packages.contains(pn)
+                            && !module.isExportedPrivate(pn, caller)) {
+                        // resource is in package not exported-export to caller
+                        return 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;
+            }
         }
 
-        // this Class and caller not in the same named module
+        // unnamed module
         ClassLoader cl = getClassLoader0();
         if (cl == null) {
             return ClassLoader.getSystemResourceAsStream(name);
@@ -2447,12 +2459,17 @@
     }
 
     /**
-     * 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
-     * associated with a given class are implemented by the defining
-     * {@linkplain ClassLoader class loader} of the class.  This method
+     * Finds a resource with a given name.
+     *
+     * <p> If this class is in a named {@link Module Module} then this method
+     * will attempt to find the resource in the module by means of the absolute
+     * resource name, subject to the rules for encapsulation specified in the
+     * {@code Module} {@link Module#getResourceAsStream getResourceAsStream}
+     * method.
+     *
+     * <p> Otherwise, if this class is not in a named module  then 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
      * the bootstrap class loader, the method delegates to {@link
      * ClassLoader#getSystemResource}.
@@ -2479,35 +2496,46 @@
      * </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, the
+     *         resource is in a package that is not exported-private to the
+     *         caller module, or access to the resource is denied by the
+     *         security manager.
+     * @throws NullPointerException If {@code name} is {@code null}
      * @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);
+            if (!ResourceHelper.isSimpleResource(name)) {
+                Module caller = Reflection.getCallerClass().getModule();
+                if (caller != module) {
+                    Set<String> packages = module.getDescriptor().packages();
+                    String pn = ResourceHelper.getPackageName(name);
+                    if (packages.contains(pn)
+                            && !module.isExportedPrivate(pn, caller)) {
+                        // resource is in package not exported-exported to caller
+                        return null;
                     }
-                } 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;
+            }
         }
 
+        // unnamed module
         ClassLoader cl = getClassLoader0();
         if (cl == null) {
             return ClassLoader.getSystemResource(name);
@@ -2632,9 +2660,6 @@
      * if name is absolute
      */
     private String resolveName(String name) {
-        if (name == null) {
-            return name;
-        }
         if (!name.startsWith("/")) {
             Class<?> c = this;
             while (c.isArray()) {
@@ -3879,4 +3904,82 @@
     public AnnotatedType[] getAnnotatedInterfaces() {
          return TypeAnnotationParser.buildAnnotatedInterfaces(getRawTypeAnnotations(), getConstantPool(), this);
     }
+
+    /*
+     * Finds JDK non-upgradeable modules, i.e. the modules that are
+     * included in the hashes in java.base.
+     */
+    private static class HashedModules {
+        static final Set<String> HASHED_MODULES = getHashedModuleNames();
+
+        static Set<String> getHashedModuleNames() {
+            Module javaBase = Layer.boot().findModule("java.base").get();
+            Optional<ModuleHashes> ohashes = SharedSecrets.getJavaLangModuleAccess()
+                .hashes(javaBase.getDescriptor());
+
+            if (ohashes.isPresent()) {
+                Set<String> names = new HashSet<>(ohashes.get().names());
+                names.add("java.base");
+                return names;
+            } else {
+                // exploded image
+                return ModuleFinder.ofSystem()
+                                   .findAll()
+                                   .stream()
+                                   .map(mref -> mref.descriptor().name())
+                                   .collect(Collectors.toSet());
+            }
+        }
+
+        static boolean contains(Module m) {
+            return m.getLayer() != Layer.boot() ||
+                !HASHED_MODULES.contains(m.getName());
+        }
+    }
+
+    /**
+     * Returns <loader>/<module>/<fully-qualified-classname> string
+     * representation of the given class.
+     * <p>
+     * If the module is a non-upgradeable JDK module then omit
+     * its version string.
+     * <p>
+     * If the loader has no name, or if the loader is one of the built-in
+     * loaders (`boot`, `platform`, or `app`) then drop the first element
+     * (`<loader>/`).
+     * <p>
+     * If the first element has been dropped and the module is unnamed
+     * then drop the second element (`<module>/`).
+     * <p>
+     * If the first element is not dropped and the module is unnamed
+     * then drop `<module>`.
+     */
+    String toLoaderModuleClassName() {
+        ClassLoader loader = getClassLoader0();
+        Module m = getModule();
+
+        // First element - class loader name
+        String s = "";
+        if (loader != null && !(loader instanceof BuiltinClassLoader) &&
+                loader.getName() != null) {
+            s = loader.getName() + "/";
+        }
+
+        // Second element - module name and version
+        if (m != null && m.isNamed()) {
+            s = s.isEmpty() ? m.getName() : s + m.getName();
+            // drop version if it's JDK module tied with java.base,
+            // i.e. non-upgradeable
+            if (HashedModules.contains(m)) {
+                Optional<Version> ov = m.getDescriptor().version();
+                if (ov.isPresent()) {
+                    String version = "@" + ov.get().toString();
+                    s = s.isEmpty() ? version : s + version;
+                }
+            }
+        }
+
+        // fully-qualified class name
+        return s.isEmpty() ? getName() : s + "/" + getName();
+    }
 }
--- a/src/java.base/share/classes/java/lang/ClassLoader.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/ClassLoader.java	Thu Sep 15 13:14:46 2016 -0700
@@ -31,6 +31,7 @@
 import java.io.File;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.Layer;
 import java.lang.reflect.Module;
 import java.net.URL;
 import java.security.AccessController;
@@ -39,21 +40,23 @@
 import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
 import java.security.cert.Certificate;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
 import java.util.Stack;
-import java.util.NoSuchElementException;
 import java.util.Vector;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
@@ -222,6 +225,9 @@
     // must be added *after* it.
     private final ClassLoader parent;
 
+    // class loader name
+    private final String name;
+
     // the unnamed module for this ClassLoader
     private final Module unnamedModule;
 
@@ -341,7 +347,8 @@
         return null;
     }
 
-    private ClassLoader(Void unused, ClassLoader parent) {
+    private ClassLoader(Void unused, String name, ClassLoader parent) {
+        this.name = name;
         this.parent = parent;
         this.unnamedModule
             = SharedSecrets.getJavaLangReflectModuleAccess()
@@ -380,7 +387,28 @@
      * @since  1.2
      */
     protected ClassLoader(ClassLoader parent) {
-        this(checkCreateClassLoader(), parent);
+        this(checkCreateClassLoader(), null, parent);
+    }
+
+    /**
+     * Creates a new class loader of the specified name and using the
+     * specified parent class loader for delegation.
+     *
+     * @param name
+     *        Class loader name; can be {@code null}
+     *
+     * @param parent
+     *        The parent class loader
+     *
+     * @throws  SecurityException
+     *          If a security manager exists and its
+     *          {@link SecurityManager#checkCreateClassLoader()}
+     *          method doesn't allow creation of a new class loader.
+     *
+     * @since  9
+     */
+    protected ClassLoader(String name, ClassLoader parent) {
+        this(checkCreateClassLoader(), name, parent);
     }
 
     /**
@@ -399,7 +427,20 @@
      *          of a new class loader.
      */
     protected ClassLoader() {
-        this(checkCreateClassLoader(), getSystemClassLoader());
+        this(checkCreateClassLoader(), null, getSystemClassLoader());
+    }
+
+    /**
+     * Returns the name of this class loader or {@code null} if
+     * this class loader is not named.
+     *
+     * @return name of this class loader; or {@code null} if
+     * this class loader is not named.
+     *
+     * @since 9
+     */
+    public String getName() {
+        return name;
     }
 
     // -- Class --
@@ -528,7 +569,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);
@@ -544,34 +585,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
@@ -672,12 +685,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
      *
@@ -687,6 +705,11 @@
      * @since 9
      */
     protected Class<?> findClass(String moduleName, String name) {
+        if (moduleName == null) {
+            try {
+                return findClass(name);
+            } catch (ClassNotFoundException ignore) { }
+        }
         return null;
     }
 
@@ -1234,10 +1257,20 @@
      * Class loader implementations that support the loading from modules
      * should override this method.
      *
-     * @implSpec The default implementation returns {@code null}.
+     * @apiNote This method is the basis for the {@code Class} {@link
+     * Class#getResource getResource} and {@link Class#getResourceAsStream
+     * getResourceAsStream} methods. It is not subject to the rules for
+     * encapsulation specified by {@code Module} {@link
+     * Module#getResourceAsStream getResourceAsStream}.
+     *
+     * @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
      *
@@ -1254,7 +1287,11 @@
      * @since 9
      */
     protected URL findResource(String moduleName, String name) throws IOException {
-        return null;
+        if (moduleName == null) {
+            return findResource(name);
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -1262,9 +1299,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.
      *
@@ -1273,16 +1307,28 @@
      * 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
+     * <p> Resources in named modules are subject to the encapsulation rules
+     * specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
+     * Additionally, and except for the special case where the resource has a
+     * name ending with "{@code .class}", this method will only find resources in
+     * packages of named modules when the package is exported-private. </p>
+     *
+     * @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, the resource is in a package
+     *          that is not unconditionally exported-private, or access to the
+     *          resource is denied by the security manager.
      *
      * @since  1.1
      */
@@ -1304,16 +1350,22 @@
      * (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
+     * <p> Resources in named modules are subject to the encapsulation rules
+     * specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
+     * Additionally, and except for the special case where the resource has a
+     * name ending with "{@code .class}", this method will only find resources in
+     * packages of named modules when the package is exported-private. </p>
+     *
+     * @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
@@ -1324,9 +1376,11 @@
      *         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, are not in a package that is unconditionally
+     *          exported-private, or access to the resource is denied by the
+     *          security manager, are not returned in the enumeration.
      *
      * @throws  IOException
      *          If I/O errors occur
@@ -1403,14 +1457,20 @@
      * 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.
+     * <p> For resources in named modules then the method must implement the
+     * rules for encapsulation specified in the {@code Module} {@link
+     * Module#getResourceAsStream getResourceAsStream} method. Additionally,
+     * it must not find non-"{@code .class}" resources in packages of named
+     * modules unless the package is unconditionally exported-private. </p>
      *
      * @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
+     * @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, the resource is in a package
+     *          that is not unconditionally exported-private, or access to the
+     *          resource is denied by the security manager.
      *
      * @since  1.2
      */
@@ -1424,14 +1484,21 @@
      * 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.
+     * <p> For resources in named modules then the method must implement the
+     * rules for encapsulation specified in the {@code Module} {@link
+     * Module#getResourceAsStream getResourceAsStream} method. Additionally,
+     * it must not find non-"{@code .class}" resources in packages of named
+     * modules unless the package is unconditionally exported-private. </p>
      *
      * @param  name
      *         The resource name
      *
      * @return  An enumeration of {@link java.net.URL <tt>URL</tt>} objects for
-     *          the resources
+     *          the resource. If no resources could  be found, the enumeration
+     *          will be empty. Resources for which a {@code URL} cannot be
+     *          constructed, are not in a package that is unconditionally
+     *          exported-private, or access to the resource is denied by the
+     *          security manager, are not returned in the enumeration.
      *
      * @throws  IOException
      *          If I/O errors occur
@@ -1439,7 +1506,7 @@
      * @since  1.2
      */
     protected Enumeration<URL> findResources(String name) throws IOException {
-        return java.util.Collections.emptyEnumeration();
+        return Collections.emptyEnumeration();
     }
 
     /**
@@ -1471,14 +1538,20 @@
      * 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.
+     * <p> Resources in named modules are subject to the encapsulation rules
+     * specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
+     * Additionally, and except for the special case where the resource has a
+     * name ending with "{@code .class}", this method will only find resources in
+     * packages of named modules when the package is exported-private. </p>
      *
      * @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, the resource is in a package
+     *          that is not unconditionally exported-private or access to the
+     *          resource is denied by the security manager.
      *
      * @since  1.1
      */
@@ -1492,17 +1565,24 @@
      * {@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>
      *
+     * <p> Resources in named modules are subject to the encapsulation rules
+     * specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
+     * Additionally, and except for the special case where the resource has a
+     * name ending with "{@code .class}", this method will only find resources in
+     * packages of named modules when the package is exported-private. </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, are not in a package that is unconditionally
+     *          exported-private, or access to the resource is denied by the
+     *          security manager, are not returned in the enumeration.
      *
      * @throws  IOException
      *          If I/O errors occur
@@ -1518,17 +1598,22 @@
     /**
      * 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>
      *
+     * <p> Resources in named modules are subject to the encapsulation rules
+     * specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
+     * Additionally, and except for the special case where the resource has a
+     * name ending with "{@code .class}", this method will only find resources in
+     * packages of named modules when the package is exported-private. </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, the resource is in a package that
+     *          is not unconditionally exported-private, or access to the
+     *          resource is denied by the security manager.
      *
      * @since  1.1
      */
@@ -1546,14 +1631,19 @@
      * 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.
+     * <p> Resources in named modules are subject to the encapsulation rules
+     * specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
+     * Additionally, and except for the special case where the resource has a
+     * name ending with "{@code .class}", this method will only find resources in
+     * packages of named modules when the package is exported-private. </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, the resource is in a package that
+     *          is not unconditionally exported-private, or access to the
+     *          resource is denied by the security manager.
      *
      * @since  1.1
      */
@@ -2626,6 +2716,45 @@
     private static native AssertionStatusDirectives retrieveDirectives();
 
 
+    // -- Layers --
+
+    /**
+     * Returns a possible-empty stream of the layers with modules defined to
+     * this class loader.
+     *
+     * @apiNote TBD whether to keep this method or a variant of.
+     *
+     * @return A stream of the layers with modules defined to this class loader
+     * @since 9
+     */
+    public Stream<Layer> layers() {
+        Collection<Layer> layers = this.layers;
+        if (layers == null) {
+            return Stream.empty();
+        } else {
+            return layers.stream();
+        }
+    }
+
+    private volatile Collection<Layer> layers; // move to CLV?
+
+    void bindToLayer(Layer layer) {
+        Collection<Layer> layers = this.layers;
+        if (layers == null) {
+            layers = new CopyOnWriteArrayList<>();
+            boolean set = trySetObjectField("layers", layers);
+            if (!set) {
+                // beaten by someone else
+                layers = this.layers;
+            }
+        }
+        layers.add(layer);
+    }
+
+
+    // -- Misc --
+
+
     /**
      * Returns the ServiceCatalog for modules defined to this class loader
      * or {@code null} if this class loader does not have a services catalog.
@@ -2652,7 +2781,7 @@
     }
 
     // the ServiceCatalog for modules associated with this class loader.
-    private volatile ServicesCatalog servicesCatalog;
+    private volatile ServicesCatalog servicesCatalog;   // move to CLV?
 
     /**
      * Returns the ConcurrentHashMap used as a storage for ClassLoaderValue(s)
--- a/src/java.base/share/classes/java/lang/Deprecated.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/Deprecated.java	Thu Sep 15 13:14:46 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	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/Package.java	Thu Sep 15 13:14:46 2016 -0700
@@ -398,10 +398,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/StackTraceElement.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/StackTraceElement.java	Thu Sep 15 13:14:46 2016 -0700
@@ -40,7 +40,15 @@
  * @author Josh Bloch
  */
 public final class StackTraceElement implements java.io.Serializable {
-    // Normally initialized by VM (public constructor added in 1.5)
+    // This field is set to the compacted String representation used
+    // by StackTraceElement::toString and stored in serial form.
+    //
+    // This field is of Object type. VM initially sets this field to
+    // the Class object of the declaring class to build the compacted string.
+    private Object classOrLoaderModuleClassName;
+
+    // Normally initialized by VM
+    private String classLoaderName;
     private String moduleName;
     private String moduleVersion;
     private String declaringClass;
@@ -72,13 +80,16 @@
      */
     public StackTraceElement(String declaringClass, String methodName,
                              String fileName, int lineNumber) {
-        this(null, null, declaringClass, methodName, fileName, lineNumber);
+        this(null, null, null, declaringClass, methodName, fileName, lineNumber);
     }
 
     /**
      * Creates a stack trace element representing the specified execution
      * point.
      *
+     * @param classLoaderName the class loader name if the class loader of
+     *        the class containing the execution point represented by
+     *        the stack trace is named; can be {@code null}
      * @param moduleName the module name if the class containing the
      *        execution point represented by the stack trace is in a named
      *        module; can be {@code null}
@@ -101,18 +112,19 @@
      *         or {@code methodName} is {@code null}
      * @since 9
      */
-    public StackTraceElement(String moduleName, String moduleVersion,
+    public StackTraceElement(String classLoaderName,
+                             String moduleName, String moduleVersion,
                              String declaringClass, String methodName,
                              String fileName, int lineNumber) {
-        this.moduleName     = moduleName;
-        this.moduleVersion  = moduleVersion;
+        this.classLoaderName = classLoaderName;
+        this.moduleName      = moduleName;
+        this.moduleVersion   = moduleVersion;
         this.declaringClass = Objects.requireNonNull(declaringClass, "Declaring class is null");
-        this.methodName     = Objects.requireNonNull(methodName, "Method name is null");
-        this.fileName       = fileName;
-        this.lineNumber     = lineNumber;
+        this.methodName      = Objects.requireNonNull(methodName, "Method name is null");
+        this.fileName        = fileName;
+        this.lineNumber      = lineNumber;
     }
 
-
     /**
      * Creates an empty stack frame element to be filled in by Throwable.
      */
@@ -178,6 +190,21 @@
     }
 
     /**
+     * Returns the name of the class loader of the class containing the
+     * execution point represented by this stack trace element.
+     *
+     * @return the name of the class loader of the class containing the execution
+     *         point represented by this stack trace element; {@code null}
+     *         if the class loader name is not available.
+     *
+     * @since 9
+     * @see java.lang.ClassLoader#getName()
+     */
+    public String getClassLoaderName() {
+        return classLoaderName;
+    }
+
+    /**
      * Returns the fully qualified name of the class containing the
      * execution point represented by this stack trace element.
      *
@@ -220,38 +247,57 @@
      * examples may be regarded as typical:
      * <ul>
      * <li>
-     *   {@code "MyClass.mash(my.module@9.0/MyClass.java:101)"} - Here,
+     *   {@code "myloader/my.module@9.0/MyClass.mash(MyClass.java:101)"}<br>
+     *   {@code "myloader"}, {@code "my.module"}, and {@code "9.0"}
+     *   is the class loader name, module name and module version of
+     *   the class containing the execution point represented by
+     *   this stack trace element respectively.
      *   {@code "MyClass"} is the <i>fully-qualified name</i> of the class
-     *   containing the execution point represented by this stack trace element,
+     *   containing the execution point.
      *   {@code "mash"} is the name of the method containing the execution
-     *   point, {@code "my.module"} is the module name, {@code "9.0"} is the
-     *   module version, and {@code "101"} is the line number of the source
+     *   point,  and {@code "101"} is the line number of the source
      *   line containing the execution point.
+     *   If the class loader is a <a href="ClassLoader.html#builtinLoaders">
+     *   built-in class loader</a>, or it does not have a name, then
+     *   {@code "myloader/"} will be omitted.
+     *   If the execution point is not in a named module, then
+     *   {@code "my.module@9.0"} will be omitted.
+     *   If the execution point is not in a named module and defined by
+     *   a built-in class loader, then {@code "/"} preceding the class name
+     *   will be omitted and the returned string may simply be:
+     *   {@code "MyClass.mash(MyClass.java:101)"}.
      * <li>
-     *   {@code "MyClass.mash(my.module@9.0/MyClass.java)"} - As above, but the
-     *   line number is unavailable.
+     *   {@code "myloader/my.module@9.0/MyClass.mash(MyClass.java)"}<br>
+     *   As above, but the line number is unavailable.
      * <li>
-     *   {@code "MyClass.mash(my.module@9.0/Unknown Source)"} - As above, but
-     *   neither the file name nor the line  number are available.
+     *   {@code "myloader/my.module@9.0/MyClass.mash(Unknown Source)"}<br>
+     *   As above, but neither the file name nor the line  number are available.
      * <li>
-     *   {@code "MyClass.mash(my.module@9.0/Native Method)"} - As above, but
-     *   neither the file name nor the line  number are available, and the
-     *   method containing the execution point is known to be a native method.
+     *   {@code "myloader/my.module@9.0/MyClass.mash(Native Method)"}<br>
+     *   As above, but neither the file name nor the line number are available,
+     *   and the method containing the execution point is known to be a native method.
      * </ul>
-     * If the execution point is not in a named module, {@code "my.module@9.0/"}
-     * will be omitted from the above.
      *
      * @see    Throwable#printStackTrace()
      */
     public String toString() {
-        String mid = "";
-        if (moduleName != null) {
-            mid = moduleName;
-            if (moduleVersion != null)
-                mid += "@" + moduleVersion;
-            mid += "/";
+        String s = buildLoaderModuleClassName();
+        if (s == null) {
+            // all elements will be included
+            s = "";
+            if (classLoaderName != null && !classLoaderName.isEmpty()) {
+                s += classLoaderName + "/";
+            }
+            if (moduleName != null && !moduleName.isEmpty()) {
+                s += moduleName;
+            }
+            if (moduleVersion != null && !moduleVersion.isEmpty()) {
+                s += "@" + moduleVersion;
+            }
+            s += declaringClass;
         }
-        return getClassName() + "." + methodName + "(" + mid +
+
+        return s + "." + methodName + "(" +
              (isNativeMethod() ? "Native Method)" :
               (fileName != null && lineNumber >= 0 ?
                fileName + ":" + lineNumber + ")" :
@@ -264,12 +310,14 @@
      * point as this instance.  Two stack trace elements {@code a} and
      * {@code b} are equal if and only if:
      * <pre>{@code
-     *     equals(a.getFileName(), b.getFileName()) &&
-     *     a.getLineNumber() == b.getLineNumber()) &&
+     *     equals(a.getClassLoaderName(), b.getClassLoaderName()) &&
      *     equals(a.getModuleName(), b.getModuleName()) &&
      *     equals(a.getModuleVersion(), b.getModuleVersion()) &&
      *     equals(a.getClassName(), b.getClassName()) &&
      *     equals(a.getMethodName(), b.getMethodName())
+     *     equals(a.getFileName(), b.getFileName()) &&
+     *     a.getLineNumber() == b.getLineNumber())
+     *
      * }</pre>
      * where {@code equals} has the semantics of {@link
      * java.util.Objects#equals(Object, Object) Objects.equals}.
@@ -285,9 +333,10 @@
         if (!(obj instanceof StackTraceElement))
             return false;
         StackTraceElement e = (StackTraceElement)obj;
-        return e.declaringClass.equals(declaringClass) &&
+        return Objects.equals(classLoaderName, e.classLoaderName) &&
             Objects.equals(moduleName, e.moduleName) &&
             Objects.equals(moduleVersion, e.moduleVersion) &&
+            e.declaringClass.equals(declaringClass) &&
             e.lineNumber == lineNumber &&
             Objects.equals(methodName, e.methodName) &&
             Objects.equals(fileName, e.fileName);
@@ -298,6 +347,7 @@
      */
     public int hashCode() {
         int result = 31*declaringClass.hashCode() + methodName.hashCode();
+        result = 31*result + Objects.hashCode(classLoaderName);
         result = 31*result + Objects.hashCode(moduleName);
         result = 31*result + Objects.hashCode(moduleVersion);
         result = 31*result + Objects.hashCode(fileName);
@@ -305,5 +355,21 @@
         return result;
     }
 
+
+    /**
+     * Build the compacted String representation to be returned by
+     * toString method from the declaring Class object.
+     */
+    synchronized String buildLoaderModuleClassName() {
+        if (classOrLoaderModuleClassName == null)
+            return null;
+
+        if (classOrLoaderModuleClassName instanceof Class) {
+            Class<?> cls = (Class<?>)classOrLoaderModuleClassName;
+            classOrLoaderModuleClassName = cls.toLoaderModuleClassName();
+        }
+        return (String)classOrLoaderModuleClassName;
+    }
+
     private static final long serialVersionUID = 6992337162326171013L;
 }
--- a/src/java.base/share/classes/java/lang/SuppressWarnings.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/SuppressWarnings.java	Thu Sep 15 13:14:46 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/System.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/System.java	Thu Sep 15 13:14:46 2016 -0700
@@ -2031,6 +2031,9 @@
             public Layer getBootLayer() {
                 return bootLayer;
             }
+            public void bindToLayer(ClassLoader loader, Layer layer) {
+                loader.bindToLayer(layer);
+            }
             public ServicesCatalog getServicesCatalog(ClassLoader cl) {
                 return cl.getServicesCatalog();
             }
--- a/src/java.base/share/classes/java/lang/Throwable.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/Throwable.java	Thu Sep 15 13:14:46 2016 -0700
@@ -24,7 +24,6 @@
  */
 
 package java.lang;
-import jdk.internal.misc.VM;
 
 import  java.io.*;
 import  java.util.*;
@@ -830,7 +829,12 @@
             for (int i = 0; i < depth; i++) {
                 stackTrace[i] = new StackTraceElement();
             }
+            // VM to fill in StackTraceElement
             getStackTraceElements(stackTrace);
+            // ensure the proper StackTraceElement initialization
+            for (StackTraceElement ste : stackTrace) {
+                ste.buildLoaderModuleClassName();
+            }
         } else if (stackTrace == null) {
             return UNASSIGNED_STACK;
         }
--- a/src/java.base/share/classes/java/lang/annotation/ElementType.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/annotation/ElementType.java	Thu Sep 15 13:14:46 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/Configuration.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/Configuration.java	Thu Sep 15 13:14:46 2016 -0700
@@ -46,14 +46,14 @@
  * dependences expressed by {@code requires} clauses.
  *
  * The <em>dependence graph</em> is augmented with edges that take account of
- * implicitly declared dependences ({@code requires public}) to create a
+ * implicitly declared dependences ({@code requires transitive}) to create a
  * <em>readability graph</em>. A {@code Configuration} encapsulates the
  * resulting graph of {@link ResolvedModule resolved modules}.
  *
  * <p> Suppose we have the following observable modules: </p>
  * <pre> {@code
  *     module m1 { requires m2; }
- *     module m2 { requires public m3; }
+ *     module m2 { requires transitive m3; }
  *     module m3 { }
  *     module m4 { }
  * } </pre>
@@ -96,9 +96,9 @@
  * <p> {@link ModuleDescriptor#isAutomatic() Automatic} modules receive special
  * treatment during resolution. Each automatic module is resolved so that it
  * reads all other modules in the configuration and all parent configurations.
- * Each automatic module is also resolved as if it {@code requires public} all
- * other automatic modules in the configuration (and all automatic modules in
- * parent configurations). </p>
+ * Each automatic module is also resolved as if it {@code requires transitive}
+ * all other automatic modules in the configuration (and all automatic modules
+ * in parent configurations). </p>
 
  * <h2><a name="servicebinding">Service binding</a></h2>
  *
--- a/src/java.base/share/classes/java/lang/module/Dependence.java	Mon Sep 12 22:04:48 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package java.lang.module;
-
-import java.util.*;
-import java.util.stream.*;
-
-
-class Dependence {
-
-    private Dependence() { }
-
-    static <T> Stream<String> toStringStream(Set<T> s) {
-        return s.stream().map(e -> e.toString().toLowerCase());
-    }
-
-    static <M> String toString(Set<M> mods, String what) {
-        return (Stream.concat(toStringStream(mods), Stream.of(what)))
-                      .collect(Collectors.joining(" "));
-    }
-
-}
--- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Thu Sep 15 13:14:46 2016 -0700
@@ -31,6 +31,7 @@
 import java.io.UncheckedIOException;
 import java.net.URI;
 import java.nio.ByteBuffer;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -44,6 +45,8 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static jdk.internal.module.Checks.*;
 import static java.util.Objects.*;
@@ -56,9 +59,11 @@
  * A module descriptor.
  *
  * <p> A {@code ModuleDescriptor} is typically created from the binary form
- * of a module declaration. The associated {@link ModuleDescriptor.Builder}
- * class can also be used to create a {@code ModuleDescriptor} from its
- * components. </p>
+ * of a module declaration. Alternatively, the {@link ModuleDescriptor.Builder}
+ * class can be used to create a {@code ModuleDescriptor} from its components.
+ * The {@link #module module}, {@link #weakModule weakModule}, and {@link
+ * #automaticModule automaticModule} methods create builders for building
+ * different kinds of modules. </p>
  *
  * <p> {@code ModuleDescriptor} objects are immutable and safe for use by
  * multiple concurrent threads.</p>
@@ -94,7 +99,13 @@
              * module</i> to have an implicitly declared dependence on the module
              * named by the {@code Requires}.
              */
-            PUBLIC,
+            TRANSITIVE,
+
+            /**
+             * 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
@@ -114,16 +125,18 @@
         private final String name;
 
         private Requires(Set<Modifier> ms, String mn) {
-            this(ms, mn, true);
+            if (ms.isEmpty()) {
+                ms = Collections.emptySet();
+            } else {
+                ms = Collections.unmodifiableSet(EnumSet.copyOf(ms));
+            }
+            this.mods = ms;
+            this.name = 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;
         }
 
         /**
@@ -222,9 +235,17 @@
          */
         @Override
         public String toString() {
-            return Dependence.toString(mods, name);
+            return toString(mods, name);
         }
 
+        private <T> Stream<String> toStringStream(Set<T> s) {
+            return s.stream().map(e -> e.toString().toLowerCase());
+        }
+
+        private <M> String toString(Set<M> mods, String what) {
+            return (Stream.concat(toStringStream(mods), Stream.of(what)))
+                    .collect(Collectors.joining(" "));
+        }
     }
 
 
@@ -238,36 +259,71 @@
 
     public final static class Exports {
 
+        /**
+         * A modifier on a module export.
+         *
+         * @since 9
+         */
+        public static enum Modifier {
+
+            /**
+             * Indicates that all types in the package, and all their members,
+             * not just public types and their public members, may be accessed
+             * by APIs that bypass or suppress default Java language access
+             * control checks.
+             *
+             * @see java.lang.reflect.AccessibleObject#setAccessible(boolean)
+             */
+            PRIVATE,
+
+            /**
+             * 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.
+         * Constructs an 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()) {
+                ms = Collections.emptySet();
+            } else {
+                ms = Collections.unmodifiableSet(EnumSet.copyOf(ms));
+            }
+            this.mods = ms;
+            this.source = source;
+            this.targets = emptyOrUnmodifiableSet(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.
+         * Returns the set of modifiers.
+         *
+         * @return A possibly-empty unmodifiable set of modifiers
          */
-        private Exports(String source) {
-            this(source, true);
-        }
-        private Exports(String source, boolean check) {
-            this.source = check ? requirePackageName(source) : source;
-            this.targets = Collections.emptySet();
+        public Set<Modifier> modifiers() {
+            return mods;
         }
 
         /**
@@ -303,25 +359,25 @@
         /**
          * Computes a hash code for this module export.
          *
-         * <p> The hash code is based upon the package name, and for a
-         * qualified export, the set of modules names to which the package
-         * is exported. It satisfies the general contract of the {@link
-         * Object#hashCode Object.hashCode} method.
+         * <p> The hash code is based upon the modifiers, the package name,
+         * and for a qualified export, the set of modules names to which the
+         * package is exported. It satisfies the general contract of the
+         * {@link Object#hashCode Object.hashCode} method.
          *
          * @return The hash-code value for this module export
          */
         @Override
         public int hashCode() {
-            return hash(source, targets);
+            return hash(mods, source, targets);
         }
 
         /**
          * Tests this module export for equality with the given object.
          *
          * <p> If the given object is not an {@code Exports} then this method
-         * returns {@code false}. Two module exports objects are equal if the
-         * package names are equal and the set of target module names is equal.
-         * </p>
+         * returns {@code false}. Two module exports objects are equal if their
+         * set of modifiers is equals, the package names are equal and the set
+         * of target module names is equal. </p>
          *
          * <p> This method satisfies the general contract of the {@link
          * java.lang.Object#equals(Object) Object.equals} method. </p>
@@ -337,8 +393,9 @@
             if (!(ob instanceof Exports))
                 return false;
             Exports other = (Exports)ob;
-            return Objects.equals(this.source, other.source) &&
-                Objects.equals(this.targets, other.targets);
+            return Objects.equals(this.mods, other.mods)
+                    && Objects.equals(this.source, other.source)
+                    && Objects.equals(this.targets, other.targets);
         }
 
         /**
@@ -348,15 +405,24 @@
          */
         @Override
         public String toString() {
+            String s = toString(mods, source);
             if (targets.isEmpty())
-                return source;
+                return s;
             else
-                return source + " to " + targets;
+                return s + " to " + targets;
         }
 
+        private <T> Stream<String> toStringStream(Set<T> s) {
+            return s.stream().map(e -> e.toString().toLowerCase());
+        }
+
+        private <M> String toString(Set<M> mods, String what) {
+            return (Stream.concat(toStringStream(mods), Stream.of(what)))
+                    .collect(Collectors.joining(" "));
+        }
     }
 
-
+
 
     /**
      * <p> A service that a module provides one or more implementations of. </p>
@@ -371,18 +437,14 @@
         private final Set<String> providers;
 
         private Provides(String service, Set<String> providers) {
-            this(service, providers, true);
+            // copy while preserving insertion order
+            providers = Collections.unmodifiableSet(new LinkedHashSet<>(providers));
+            this.service = service;
+            this.providers = 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;
         }
 
@@ -773,10 +835,7 @@
 
     // From module declarations
     private final String name;
-    private final Set<Requires> requires;
-    private final Set<Exports> exports;
-    private final Set<String> uses;
-    private final Map<String, Provides> provides;
+    private final boolean weak;
 
     // Indicates if synthesised for a JAR file found on the module path
     private final boolean automatic;
@@ -784,6 +843,11 @@
     // Not generated from a module-info.java
     private final boolean synthetic;
 
+    private final Set<Requires> requires;
+    private final Set<Exports> exports;
+    private final Set<String> uses;
+    private final Map<String, Provides> provides;
+
     // "Extended" information, added post-compilation by tools
     private final Version version;
     private final String mainClass;
@@ -795,11 +859,12 @@
 
 
     private ModuleDescriptor(String name,
+                             boolean weak,
                              boolean automatic,
                              boolean synthetic,
-                             Map<String, Requires> requires,
+                             Set<Requires> requires,
                              Set<String> uses,
-                             Map<String, Exports> exports,
+                             Set<Exports> exports,
                              Map<String, Provides> provides,
                              Version version,
                              String mainClass,
@@ -811,31 +876,23 @@
     {
 
         this.name = name;
+        this.weak = weak;
         this.automatic = automatic;
         this.synthetic = synthetic;
 
-        Set<Requires> rqs = new HashSet<>(requires.values());
-        assert (rqs.stream().map(Requires::name).sorted().distinct().count()
-                == rqs.size())
-            : "Module " + name + " has duplicate requires";
-        this.requires = emptyOrUnmodifiableSet(rqs);
+        assert (requires.stream().map(Requires::name).distinct().count()
+                == requires.size());
+        this.requires = emptyOrUnmodifiableSet(requires);
 
-        Set<Exports> exs = new HashSet<>(exports.values());
-        assert (exs.stream().map(Exports::source).sorted().distinct().count()
-                == exs.size())
-            : "Module " + name + " has duplicate exports";
-        this.exports = emptyOrUnmodifiableSet(exs);
-
+        this.exports = emptyOrUnmodifiableSet(exports);
         this.uses = emptyOrUnmodifiableSet(uses);
         this.provides = emptyOrUnmodifiableMap(provides);
-
         this.version = version;
         this.mainClass = mainClass;
         this.osName = osName;
         this.osArch = osArch;
         this.osVersion = osVersion;
         this.hashes = hashes;
-
         this.packages = emptyOrUnmodifiableSet(packages);
     }
 
@@ -844,6 +901,7 @@
      */
     ModuleDescriptor(ModuleDescriptor md, Set<String> pkgs) {
         this.name = md.name;
+        this.weak = md.weak;
         this.automatic = md.automatic;
         this.synthetic = md.synthetic;
 
@@ -865,10 +923,11 @@
     }
 
     /**
-     * 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 weak,
                      boolean automatic,
                      boolean synthetic,
                      Set<Requires> requires,
@@ -881,16 +940,17 @@
                      String osArch,
                      String osVersion,
                      Set<String> packages,
-                     ModuleHashes hashes) {
+                     ModuleHashes hashes,
+                     boolean unused) {
         this.name = name;
+        this.weak = weak;
         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;
         this.osName = osName;
@@ -909,6 +969,18 @@
     }
 
     /**
+     * <p> Returns {@code true} if this is a weak module. </p>
+     *
+     * <p> A weak module does not declare any module exports but is treated
+     * as if every package is <em>exported-private</em>.
+     *
+     * @return  {@code true} if this is a weak module
+     */
+    public boolean isWeak() {
+        return weak;
+    }
+
+    /**
      * <p> Returns {@code true} if this is an automatic module. </p>
      *
      * <p> An automatic module is defined implicitly rather than explicitly
@@ -932,8 +1004,6 @@
      *
      * @return  {@code true} if this module descriptor was not generated by
      *          an explicit or implicit module declaration
-     *
-     * @jvms 4.7.8 The {@code Synthetic} Attribute
      */
     public boolean isSynthetic() {
         return synthetic;
@@ -1045,20 +1115,8 @@
     }
 
     /**
-     * Returns the names of the packages defined in, but not exported by, this
-     * module.
-     *
-     * @return A possibly-empty unmodifiable set of the concealed packages
-     */
-    public Set<String> conceals() {
-        Set<String> conceals = new HashSet<>(packages);
-        exports.stream().map(Exports::source).forEach(conceals::remove);
-        return emptyOrUnmodifiableSet(conceals);
-    }
-
-    /**
-     * Returns the names of all the packages defined in this module, whether
-     * exported or concealed.
+     * Returns the names of all packages defined in this module, whether
+     * exported or not.
      *
      * @return A possibly-empty unmodifiable set of the all packages
      */
@@ -1077,31 +1135,37 @@
     /**
      * A builder used for building {@link ModuleDescriptor} objects.
      *
+     * <p> {@code ModuleDescriptor} defines the {@link #module module}, {@link
+     * #weakModule weakModule}, and {@link #automaticModule automaticModule}
+     * methods to create builders for building different kinds of modules. </p>
+     *
      * <p> Example usage: </p>
-     *
-     * <pre>{@code
-     *     ModuleDescriptor descriptor = new ModuleDescriptor.Builder("m1")
+     * <pre>{@code    ModuleDescriptor descriptor = ModuleDescriptor.module("m1")
+     *         .exports("p")
      *         .requires("m2")
-     *         .exports("p")
      *         .build();
      * }</pre>
      *
-     * @apiNote A {@code Builder} cannot be used to create an {@link
-     * ModuleDescriptor#isAutomatic() automatic} or a {@link
-     * ModuleDescriptor#isSynthetic() synthetic} module.
+     * @apiNote A {@code Builder} checks the components and invariants as
+     * components are added to the builder. The rational for this is to detect
+     * errors as early as possible and not defer all validation to the
+     * {@link #build build} method. A {@code Builder} cannot be used to create
+     * a {@link ModuleDescriptor#isSynthetic() synthetic} module.
      *
      * @since 9
      */
     public static final class Builder {
-
         final String name;
+        final boolean strict; // true if module names are checked
+        boolean weak;
         boolean automatic;
         boolean synthetic;
         final Map<String, Requires> requires = new HashMap<>();
+        final Map<String, Exports> unqualifiedExports = new HashMap<>();
+        final Map<String, Set<Exports>> qualifiedExports = new HashMap<>();
+        final Set<String> nonExportedPackages = new HashSet<>();
         final Set<String> uses = new HashSet<>();
-        final Map<String, Exports> exports = new HashMap<>();
         final Map<String, Provides> provides = new HashMap<>();
-        Set<String> conceals = Collections.emptySet();
         Version version;
         String osName;
         String osArch;
@@ -1112,29 +1176,32 @@
         /**
          * Initializes a new builder with the given module name.
          *
-         * @param  name
-         *         The module name
-         *
-         * @throws IllegalArgumentException
-         *         If the module name is {@code null} or is not a legal Java
-         *         identifier
+         * @param strict
+         *        Indicates whether module names are checked or not
          */
-        public Builder(String name) {
-            this.name = requireModuleName(name);
+        Builder(String name, boolean strict) {
+            this.strict = strict;
+            this.name = (strict) ? requireModuleName(name) : name;
         }
 
-        /**
-         * Updates the builder so that it builds an automatic module.
-         *
-         * @return This builder
-         *
-         * @see ModuleDescriptor#isAutomatic()
-         */
-        /* package */ Builder automatic() {
-            this.automatic = true;
+        /* package */ Builder weak(boolean weak) {
+            this.weak = weak;
             return this;
         }
 
+        /* package */ Builder automatic(boolean automatic) {
+            this.automatic = automatic;
+            return this;
+        }
+
+        /* package */  boolean isWeak() {
+            return weak;
+        }
+
+        /* package */ boolean isAutomatic() {
+            return automatic;
+        }
+
         /**
          * Adds a dependence on a module.
          *
@@ -1164,7 +1231,7 @@
          * Adds a dependence on a module with the given (and possibly empty)
          * set of modifiers.
          *
-         * @param  mods
+         * @param  ms
          *         The set of modifiers
          * @param  mn
          *         The module name
@@ -1178,14 +1245,10 @@
          * @throws IllegalStateException
          *         If the dependence on the module has already been declared
          */
-        public Builder requires(Set<Requires.Modifier> mods, String mn) {
-            if (name.equals(mn))
-                throw new IllegalArgumentException("Dependence on self");
-            if (requires.containsKey(mn))
-                throw new IllegalStateException("Dependence upon " + mn
-                                                + " already declared");
-            requires.put(mn, new Requires(mods, mn)); // checks mn
-            return this;
+        public Builder requires(Set<Requires.Modifier> ms, String mn) {
+            if (strict)
+                mn = requireModuleName(mn);
+            return requires(new Requires(ms, mn));
         }
 
         /**
@@ -1208,63 +1271,29 @@
         }
 
         /**
-         * Adds a dependence on a module with the given modifier.
+         * Adds an export.
          *
-         * @param  mod
-         *         The modifier
-         * @param  mn
-         *         The module name
+         * <p> A specific package may have two exports when one of the exports
+         * is to a set of target modules with the {@link Exports.Modifier#PRIVATE
+         * PRIVATE} modifier and the other (qualified or unqualified) does not
+         * have this modifier. More specifically, for a package {@code p}, the
+         * following are allowed: </p>
          *
-         * @return This builder
+         * <table border=1 cellpadding=5 summary="allowed">
+         *   <tr>
+         *      <td>{@code exports p}</td>
+         *      <td>{@code exports private p to <target-modules>}</td>
+         *   </tr>
+         *   <tr>
+         *      <td>{@code exports p to <target-modules>}</td>
+         *      <td>{@code exports private p to <other-target-modules>}</td>
+         *   </tr>
+         * </table>
          *
-         * @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.
-         *
-         * @param  st
-         *         The service type
-         *
-         * @return This builder
-         *
-         * @throws IllegalArgumentException
-         *         If the service type is {@code null} or is not a legal Java
-         *         identifier
-         * @throws IllegalStateException
-         *         If a dependency on the service type has already been declared
-         */
-        public Builder uses(String st) {
-            if (uses.contains(requireServiceTypeName(st)))
-                throw new IllegalStateException("Dependence upon service "
-                                                + st + " already declared");
-            uses.add(st);
-            return this;
-        }
-
-        /**
-         * Ensures that the given package name has not been declared as an
-         * exported or concealed package.
-         */
-        private void ensureNotExportedOrConcealed(String pn) {
-            if (exports.containsKey(pn))
-                throw new IllegalStateException("Export of package "
-                                                + pn + " already declared");
-            if (conceals.contains(pn))
-                throw new IllegalStateException("Concealed package "
-                                                + pn + " already declared");
-        }
-
-        /**
-         * Adds an export.
+         * <p> where {@code <target-modules>} and {@code <other-target-modules>} are
+         * non-empty sets of target modules and where the same module is not in
+         * both sets. An export may otherwise not be added when the package has
+         * already been declared as exported. </p>
          *
          * @param  e
          *         The export
@@ -1272,17 +1301,142 @@
          * @return This builder
          *
          * @throws IllegalStateException
-         *         If the package is already declared as an exported or
-         *         concealed package
+         *         If the package is already declared as a package with the
+         *         {@link #contains contains} method, the export conflicts with
+         *         a declared exported package as specified above, or this
+         *         builder was initialized to create a weak module
          */
         public Builder exports(Exports e) {
-            String pn = e.source();
-            ensureNotExportedOrConcealed(pn);
-            exports.put(pn, e);
+            // weak modules don't have exports
+            if (weak) {
+                throw new IllegalStateException("Weak modules do not declare exports");
+            }
+
+            // can't be exported and non-exported
+            String source = e.source();
+            if (nonExportedPackages.contains(source)) {
+                throw new IllegalStateException("Package " + source
+                                                 + " already declared");
+            }
+
+            // allow: exports p; exports private p to m;
+            // allow: exports p to m1; exports private p to m2;
+            Exports e1 = unqualifiedExports.get(source);
+            Exports e2 = null;
+            Set<Exports> qualifiedToSource = qualifiedExports.get(source);
+            if (qualifiedToSource != null && !qualifiedToSource.isEmpty()) {
+                if (qualifiedToSource.size() > 1) {
+                    // already have 2 qualified exports
+                    throw new IllegalStateException("Exported package " + source
+                                                    + " already declared");
+                }
+                e2 = qualifiedToSource.iterator().next();
+            }
+            if (e1 != null && e2 != null) {
+                // already have a qualified and unqualified export
+                throw new IllegalStateException("Exported package " + source
+                                                + " already declared");
+            }
+
+            Exports other = (e1 != null) ? e1 : e2;
+            if (other != null) {
+                // check for conflict with existing export
+                boolean wantPrivate = e.modifiers().contains(Exports.Modifier.PRIVATE);
+                boolean havePrivate = other.modifiers().contains(Exports.Modifier.PRIVATE);
+                if (wantPrivate == havePrivate) {
+                    throw new IllegalStateException("Exported package " + source
+                                                    + " already declared");
+                }
+                // can't have two qualified exports to the same target
+                for (String target : e.targets()) {
+                    if (other.targets().contains(target)) {
+                        throw new IllegalStateException(source
+                                                        + " already exported to "
+                                                        + target);
+                    }
+                }
+            }
+
+            // export allowed
+            if (e.isQualified()) {
+                if (qualifiedToSource == null) {
+                    qualifiedExports.put(source, Set.of(e));
+                } else {
+                    qualifiedExports.put(source, Set.of(e, other));
+                }
+            } else {
+                unqualifiedExports.put(source, e);
+            }
+
             return this;
         }
 
         /**
+         * Adds an export, with the given (and possibly empty) set of modifiers,
+         * to a set of target modules.
+         *
+         * @param  ms
+         *         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 a package with the
+         *         {@link #contains contains} method, the export conflicts with
+         *         a declared exported package (see {@link #exports(Exports)
+         *         exports(Exports)}), or this builder was initialized to
+         *         create a weak module
+         */
+        public Builder exports(Set<Exports.Modifier> ms,
+                               String pn,
+                               Set<String> targets)
+        {
+            Exports e = new Exports(ms, requirePackageName(pn), targets);
+
+            // check targets
+            targets = e.targets();
+            if (targets.isEmpty())
+                throw new IllegalArgumentException("Empty target set");
+            if (strict)
+                targets.stream().forEach(Checks::requireModuleName);
+
+            return exports(e);
+        }
+
+        /**
+         * Adds an export with the given (and possibly empty) set of modifiers.
+         *
+         * @param  ms
+         *         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 a package with the
+         *         {@link #contains contains} method, the export conflicts with
+         *         a declared exported package (see {@link #exports(Exports)
+         *         exports(Exports)}), or this builder was initialized to
+         *         create a weak module
+         */
+        public Builder exports(Set<Exports.Modifier> ms, String pn) {
+            Exports e = new Exports(ms, requirePackageName(pn), Collections.emptySet());
+            return exports(e);
+        }
+
+        /**
          * Adds an export to a set of target modules.
          *
          * @param  pn
@@ -1297,34 +1451,14 @@
          *         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
+         *         If the package is already declared as a package with the
+         *         {@link #contains contains} method, the export conflicts with
+         *         a declared exported package (see {@link #exports(Exports)
+         *         exports(Exports)}), or this builder was initialized to
+         *         create a weak module
          */
         public Builder exports(String pn, Set<String> targets) {
-            ensureNotExportedOrConcealed(pn);
-            exports.put(pn, new Exports(pn, targets)); // checks pn and targets
-            return this;
-        }
-
-        /**
-         * Adds an export to a target module.
-         *
-         * @param  pn
-         *         The package name
-         * @param  target
-         *         The target module name
-         *
-         * @return This builder
-         *
-         * @throws IllegalArgumentException
-         *         If the package name or target module 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(String pn, String target) {
-            return exports(pn, Collections.singleton(target));
+            return exports(Collections.emptySet(), pn, targets);
         }
 
         /**
@@ -1339,18 +1473,44 @@
          *         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
+         *         If the package is already declared as a package with the
+         *         {@link #contains contains} method, the export conflicts with
+         *         a declared exported package (see {@link #exports(Exports)
+         *         exports(Exports)}), or this builder was initialized to
+         *         create a weak module
          */
         public Builder exports(String pn) {
-            ensureNotExportedOrConcealed(pn);
-            exports.put(pn, new Exports(pn)); // checks pn
-            return this;
+            return exports(Collections.emptySet(), pn);
         }
 
         // Used by ModuleInfo, after a packageFinder is invoked
         /* package */ Set<String> exportedPackages() {
-            return exports.keySet();
+            Set<String> exported = new HashSet<>();
+            exported.addAll(unqualifiedExports.keySet());
+            exported.addAll(qualifiedExports.keySet());
+            return exported;
+        }
+
+        /**
+         * Adds a service dependence.
+         *
+         * @param  service
+         *         The service type
+         *
+         * @return This builder
+         *
+         * @throws IllegalArgumentException
+         *         If the service type is {@code null} or is not a legal Java
+         *         identifier
+         * @throws IllegalStateException
+         *         If a dependency on the service type has already been declared
+         */
+        public Builder uses(String service) {
+            if (uses.contains(requireServiceTypeName(service)))
+                throw new IllegalStateException("Dependence upon service "
+                        + service + " already declared");
+            uses.add(service);
+            return this;
         }
 
         /**
@@ -1375,11 +1535,11 @@
         }
 
         /**
-         * Provides service {@code st} with implementations {@code pcs}.
+         * Provides implementations of a service.
          *
-         * @param  st
+         * @param  service
          *         The service type
-         * @param  pcs
+         * @param  providers
          *         The set of provider class names
          *
          * @return This builder
@@ -1392,20 +1552,29 @@
          *         If the providers for the service type have already been
          *         declared
          */
-        public Builder provides(String st, Set<String> pcs) {
-            if (provides.containsKey(st))
+        public Builder provides(String service, Set<String> providers) {
+            if (provides.containsKey(service))
                 throw new IllegalStateException("Providers of service "
-                                                + st + " already declared");
-            provides.put(st, new Provides(st, pcs)); // checks st and pcs
+                                                + service + " already declared");
+
+            Provides p = new Provides(requireServiceTypeName(service), providers);
+
+            // check providers after the set has been copied.
+            Set<String> providerNames = p.providers();
+            if (providerNames.isEmpty())
+                throw new IllegalArgumentException("Empty providers set");
+            providerNames.forEach(Checks::requireServiceProviderName);
+
+            provides.put(service, p);
             return this;
         }
 
         /**
-         * Provides service {@code st} with implementation {@code pc}.
+         * Provides an implementation of a service.
          *
-         * @param  st
+         * @param  service
          *         The service type
-         * @param  pc
+         * @param  provider
          *         The provider class name
          *
          * @return This builder
@@ -1417,15 +1586,15 @@
          *         If the providers for the service type have already been
          *         declared
          */
-        public Builder provides(String st, String pc) {
-            return provides(st, Collections.singleton(pc));
+        public Builder provides(String service, String provider) {
+            return provides(service, Collections.singleton(provider));
         }
 
         /**
-         * Adds a set of (possible empty) concealed packages.
+         * Adds a (possible empty) set of packages to the module
          *
          * @param  pns
-         *         The set of package names of the concealed packages
+         *         The set of package names
          *
          * @return This builder
          *
@@ -1433,16 +1602,16 @@
          *         If any of the package names is {@code null} or is not a
          *         legal Java identifier
          * @throws IllegalStateException
-         *         If any of packages are already declared as a concealed or
-         *         exported package
+         *         If any of packages are already declared as packages or
+         *         exported packages
          */
-        public Builder conceals(Set<String> pns) {
-            pns.forEach(this::conceals);
+        public Builder contains(Set<String> pns) {
+            pns.forEach(this::contains);
             return this;
         }
 
         /**
-         * Adds a concealed package.
+         * Adds a package to the module.
          *
          * @param  pn
          *         The package name
@@ -1453,15 +1622,21 @@
          *         If the package name is {@code null}, or is not a legal Java
          *         identifier
          * @throws IllegalStateException
-         *         If the package is already declared as a concealed or exported
+         *         If the package is already declared as a package or exported
          *         package
          */
-        public Builder conceals(String pn) {
+        public Builder contains(String pn) {
             Checks.requirePackageName(pn);
-            ensureNotExportedOrConcealed(pn);
-            if (conceals.isEmpty())
-                conceals = new HashSet<>();
-            conceals.add(pn);
+            if (nonExportedPackages.contains(pn)) {
+                throw new IllegalStateException("Package " + pn
+                                                + " already declared");
+            }
+            if (unqualifiedExports.containsKey(pn)
+                    || qualifiedExports.containsKey(pn)) {
+                throw new IllegalStateException("Exported package "
+                                                + pn + " already declared");
+            }
+            nonExportedPackages.add(pn);
             return this;
         }
 
@@ -1472,13 +1647,8 @@
          *         The version
          *
          * @return This builder
-         *
-         * @throws IllegalStateException
-         *         If the module version is already set
          */
         public Builder version(Version v) {
-            if (version != null)
-                throw new IllegalStateException("module version already set");
             version = requireNonNull(v);
             return this;
         }
@@ -1493,16 +1663,11 @@
          *
          * @throws IllegalArgumentException
          *         If {@code v} is null or cannot be parsed as a version string
-         * @throws IllegalStateException
-         *         If the module version is already set
          *
          * @see Version#parse(String)
          */
         public Builder version(String v) {
-            if (version != null)
-                throw new IllegalStateException("module version already set");
-            version = Version.parse(v);
-            return this;
+            return version(Version.parse(v));
         }
 
         /**
@@ -1515,12 +1680,8 @@
          *
          * @throws IllegalArgumentException
          *         If {@code mainClass} is null or is not a legal Java identifier
-         * @throws IllegalStateException
-         *         If the module main class is already set
          */
         public Builder mainClass(String mc) {
-            if (mainClass != null)
-                throw new IllegalStateException("main class already set");
             mainClass = requireJavaIdentifier("main class name", mc);
             return this;
         }
@@ -1535,12 +1696,8 @@
          *
          * @throws IllegalArgumentException
          *         If {@code name} is null or the empty String
-         * @throws IllegalStateException
-         *         If the operating system name is already set
          */
         public Builder osName(String name) {
-            if (osName != null)
-                throw new IllegalStateException("OS name already set");
             if (name == null || name.isEmpty())
                 throw new IllegalArgumentException("OS name is null or empty");
             osName = name;
@@ -1557,12 +1714,8 @@
          *
          * @throws IllegalArgumentException
          *         If {@code name} is null or the empty String
-         * @throws IllegalStateException
-         *         If the operating system architecture is already set
          */
         public Builder osArch(String arch) {
-            if (osArch != null)
-                throw new IllegalStateException("OS arch already set");
             if (arch == null || arch.isEmpty())
                 throw new IllegalArgumentException("OS arch is null or empty");
             osArch = arch;
@@ -1579,12 +1732,8 @@
          *
          * @throws IllegalArgumentException
          *         If {@code name} is null or the empty String
-         * @throws IllegalStateException
-         *         If the operating system version is already set
          */
         public Builder osVersion(String version) {
-            if (osVersion != null)
-                throw new IllegalStateException("OS version already set");
             if (version == null || version.isEmpty())
                 throw new IllegalArgumentException("OS version is null or empty");
             osVersion = version;
@@ -1596,7 +1745,6 @@
             return this;
         }
 
-
         /* package */ Builder synthetic(boolean v) {
             this.synthetic = v;
             return this;
@@ -1608,11 +1756,18 @@
          * @return The module descriptor
          */
         public ModuleDescriptor build() {
-            assert name != null;
+            Set<Requires> requires = new HashSet<>(this.requires.values());
 
-            Set<String> packages = new HashSet<>(conceals);
+            Set<String> packages;
+            Set<Exports> exports = new HashSet<>();
+            packages = new HashSet<>(nonExportedPackages);
             packages.addAll(exportedPackages());
+
+            exports.addAll(unqualifiedExports.values());
+            qualifiedExports.values().forEach(e -> exports.addAll(e));
+
             return new ModuleDescriptor(name,
+                                        weak,
                                         automatic,
                                         synthetic,
                                         requires,
@@ -1630,7 +1785,6 @@
 
     }
 
-
     /**
      * Compares this module descriptor to another.
      *
@@ -1767,16 +1921,83 @@
         return sb.toString();
     }
 
+
+    /**
+     * Instantiates a builder to build a module descriptor.
+     *
+     * @param  name
+     *         The module name
+     *
+     * @return A new builder
+     *
+     * @throws IllegalArgumentException
+     *         If the module name is {@code null} or is not a legal Java
+     *         identifier
+     */
+    public static Builder module(String name) {
+        return new Builder(name, true);
+    }
+
+    /**
+     * Instantiates a builder to build a module descriptor for a weak module.
+     * A weak module does not declare any module exports but the resulting
+     * module descriptor is treated as if all packages are
+     * <em>exported-private</em>.
+     *
+     * <p> As an example, the following creates a module descriptor for a weak
+     * name "{@code m}" containing two packages: </p>
+     * <pre>{@code
+     *     ModuleDescriptor descriptor = ModuleDescriptor.weakModule("m")
+     *         .requires("java.base")
+     *         .contains("p")
+     *         .contains("q")
+     *         .build();
+     * }</pre>
+     *
+     * @param  name
+     *         The module name
+     *
+     * @return A new builder that builds a weak module
+     *
+     * @throws IllegalArgumentException
+     *         If the module name is {@code null} or is not a legal Java
+     *         identifier
+     */
+    public static Builder weakModule(String name) {
+        return new Builder(name, true).weak(true);
+    }
+
+    /**
+     * Instantiates a builder to build a module descriptor for an automatic
+     * module. Automatic modules receive special treatment during resolution
+     * (see {@link Configuration}) so that they read all other modules. When
+     * Instantiated in the Java virtual machine as a {@link java.lang.reflect.Module}
+     * then the Module reads every unnamed module in the Java virtual machine.
+     *
+     * @param  name
+     *         The module name
+     *
+     * @return A new builder that builds an automatic module
+     *
+     * @throws IllegalArgumentException
+     *         If the module name is {@code null} or is not a legal Java
+     *         identifier
+     *
+     * @see ModuleFinder#of(Path[])
+     */
+    public static Builder automaticModule(String name) {
+        return new Builder(name, true).automatic(true);
+    }
+
+
     /**
      * Reads the binary form of a module declaration from an input stream
      * as a module descriptor.
      *
      * <p> If the descriptor encoded in the input stream does not indicate a
-     * set of concealed packages then the {@code packageFinder} will be
-     * invoked.  The packages it returns, except for those indicated as
-     * exported in the encoded descriptor, will be considered to be concealed.
-     * If the {@code packageFinder} throws an {@link UncheckedIOException} then
-     * {@link IOException} cause will be re-thrown. </p>
+     * set of packages in the module then the {@code packageFinder} will be
+     * invoked. If the {@code packageFinder} throws an {@link UncheckedIOException}
+     * then {@link IOException} cause will be re-thrown. </p>
      *
      * <p> If there are bytes following the module descriptor then it is
      * implementation specific as to whether those bytes are read, ignored,
@@ -1788,12 +2009,12 @@
      *
      * @apiNote The {@code packageFinder} parameter is for use when reading
      * module descriptors from legacy module-artifact formats that do not
-     * record the set of concealed packages in the descriptor itself.
+     * record the set of packages in the descriptor itself.
      *
      * @param  in
      *         The input stream
      * @param  packageFinder
-     *         A supplier that can produce a set of package names
+     *         A supplier that can produce the set of packages
      *
      * @return The module descriptor
      *
@@ -1833,10 +2054,7 @@
      * as a module descriptor.
      *
      * <p> If the descriptor encoded in the byte buffer does not indicate a
-     * set of concealed packages then the {@code packageFinder} will be
-     * invoked.  The packages it returns, except for those indicated as
-     * exported in the encoded descriptor, will be considered to be
-     * concealed. </p>
+     * set of packages then the {@code packageFinder} will be invoked. </p>
      *
      * <p> The module descriptor is read from the buffer stating at index
      * {@code p}, where {@code p} is the buffer's {@link ByteBuffer#position()
@@ -1852,12 +2070,12 @@
      *
      * @apiNote The {@code packageFinder} parameter is for use when reading
      * module descriptors from legacy module-artifact formats that do not
-     * record the set of concealed packages in the descriptor itself.
+     * record the set of packages in the descriptor itself.
      *
      * @param  bb
      *         The byte buffer
      * @param  packageFinder
-     *         A supplier that can produce a set of package names
+     *         A supplier that can produce the set of packages
      *
      * @return The module descriptor
      *
@@ -1914,25 +2132,32 @@
          */
         jdk.internal.misc.SharedSecrets
             .setJavaLangModuleAccess(new jdk.internal.misc.JavaLangModuleAccess() {
+                @Override
+                public Builder newBuilder(String mn, boolean weak, boolean strict) {
+                    return new Builder(mn, strict).weak(weak);
+                }
 
                 @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, Collections.emptySet(), true);
                 }
 
                 @Override
                 public Provides newProvides(String service, Set<String> providers) {
-                    return new Provides(service, providers, false);
+                    return new Provides(service, providers, true);
                 }
 
                 @Override
@@ -1948,6 +2173,7 @@
 
                 @Override
                 public ModuleDescriptor newModuleDescriptor(String name,
+                                                            boolean weak,
                                                             boolean automatic,
                                                             boolean synthetic,
                                                             Set<Requires> requires,
@@ -1962,6 +2188,7 @@
                                                             Set<String> packages,
                                                             ModuleHashes hashes) {
                     return new ModuleDescriptor(name,
+                                                weak,
                                                 automatic,
                                                 synthetic,
                                                 requires,
@@ -1974,7 +2201,13 @@
                                                 osArch,
                                                 osVersion,
                                                 packages,
-                                                hashes);
+                                                hashes,
+                                                false);
+                }
+
+                @Override
+                public Optional<ModuleHashes> hashes(ModuleDescriptor descriptor) {
+                    return descriptor.hashes();
                 }
 
                 @Override
@@ -1994,9 +2227,12 @@
                 }
 
                 @Override
-                public Optional<ModuleHashes> hashes(ModuleDescriptor descriptor) {
-                    return descriptor.hashes();
+                public ModuleFinder newModulePath(Runtime.Version version,
+                                                  boolean isLinkPhase,
+                                                  Path... entries) {
+                    return new ModulePath(version, isLinkPhase, entries);
                 }
+
             });
     }
 
--- a/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Thu Sep 15 13:14:46 2016 -0700
@@ -229,15 +229,16 @@
      *
      *         <li><p> If the name matches the regular expression {@code
      *         "-(\\d+(\\.|$))"} then the module name will be derived from the
-     *         subsequence proceeding the hyphen of the first occurrence. The
+     *         subsequence preceding the hyphen of the first occurrence. The
      *         subsequence after the hyphen is parsed as a {@link
      *         ModuleDescriptor.Version} and ignored if it cannot be parsed as
      *         a {@code Version}. </p></li>
      *
-     *         <li><p> For the module name, then all non-alphanumeric
-     *         characters ({@code [^A-Za-z0-9])} are replaced with a dot
-     *         ({@code "."}), all repeating dots are replaced with one dot,
-     *         and all leading and trailing dots are removed. </p></li>
+     *         <li><p> For the module name, then any trailing digits and dots
+     *         are removed, all non-alphanumeric characters ({@code [^A-Za-z0-9]})
+     *         are replaced with a dot ({@code "."}), all repeating dots are
+     *         replaced with one dot, and all leading and trailing dots are
+     *         removed. </p></li>
      *
      *         <li><p> As an example, a JAR file named {@code foo-bar.jar} will
      *         derive a module name {@code foo.bar} and no version. A JAR file
@@ -249,18 +250,23 @@
      *     <li><p> It {@link ModuleDescriptor#requires() requires} {@code
      *     java.base}. </p></li>
      *
-     *     <li><p> All entries in the JAR file with names ending with {@code
-     *     .class} are assumed to be class files where the name corresponds
-     *     to the fully qualified name of the class. The packages of all
-     *     classes are {@link ModuleDescriptor#exports() exported}. </p></li>
+     *     <li><p> The set of packages in the module is derived from the names
+     *     of non-directory entries in the JAR file. A candidate package name
+     *     is derived from an entry using the characters up to, but not
+     *     including, the last forward slash. All remaining forward slashes are
+     *     replaced with dot ({@code "."}). If the resulting string is a valid
+     *     Java identifier then it is assumed to be a package name. For example,
+     *     if the JAR file contains an entry "{@code p/q/Foo.class}" then the
+     *     package name derived is "{@code p.q}". All packages are {@link
+     *     ModuleDescriptor#exports() exported-private}. </p></li>
      *
-     *     <li><p> The contents of all entries starting with {@code
+     *     <li><p> The contents of entries starting with {@code
      *     META-INF/services/} are assumed to be service configuration files
-     *     (see {@link java.util.ServiceLoader}). The name of the file
-     *     (that follows {@code META-INF/services/}) is assumed to be the
-     *     fully-qualified binary name of a service type. The entries in the
-     *     file are assumed to be the fully-qualified binary names of
-     *     provider classes. </p></li>
+     *     (see {@link java.util.ServiceLoader}). If the name of a file
+     *     (that follows {@code META-INF/services/}) is a legal Java identifier
+     *     then it is assumed to be the fully-qualified binary name of a
+     *     service type. The entries in the file are assumed to be the
+     *     fully-qualified binary names of provider classes. </p></li>
      *
      *     <li><p> If the JAR file has a {@code Main-Class} attribute in its
      *     main manifest then its value is the {@link
@@ -272,8 +278,8 @@
      * {@link ModuleDescriptor.Builder ModuleDescriptor.Builder} API) for an
      * automatic module then {@code FindException} is thrown. This can arise,
      * for example, when a legal Java identifier name cannot be derived from
-     * the file name of the JAR file or where a package name derived from an
-     * entry ending with {@code .class} is not a legal Java identifier. </p>
+     * the file name of the JAR file or where the JAR file contains a {@code
+     * .class} in the top-level directory of the JAR file. </p>
      *
      * <p> In addition to JAR files, an implementation may also support modules
      * that are packaged in other implementation specific module formats. When
@@ -284,8 +290,10 @@
      *
      * <p> As with automatic modules, the contents of a packaged or exploded
      * module may need to be <em>scanned</em> in order to determine the packages
-     * in the module. If a {@code .class} file that corresponds to a class in an
-     * unnamed package is encountered then {@code FindException} is thrown. </p>
+     * in the module. If a {@code .class} file (other than {@code
+     * module-info.class}) is found in the top-level directory then it is
+     * assumed to be a class in the unnamed package and so {@code FindException}
+     * is thrown. </p>
      *
      * <p> Finders created by this method are lazy and do not eagerly check
      * that the given file paths are directories or packaged modules.
--- a/src/java.base/share/classes/java/lang/module/ModuleInfo.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/ModuleInfo.java	Thu Sep 15 13:14:46 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,10 +31,13 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UncheckedIOException;
-import java.lang.module.ModuleDescriptor.Requires.Modifier;
+import java.lang.module.ModuleDescriptor.Builder;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.lang.module.ModuleDescriptor.Exports;
 import java.nio.ByteBuffer;
 import java.nio.BufferUnderflowException;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -56,15 +59,12 @@
 
 final class ModuleInfo {
 
-    // supplies the set of packages when ConcealedPackages not present
+    // supplies the set of packages when Packages attribute not present
     private final Supplier<Set<String>> packageFinder;
 
     // indicates if the Hashes attribute should be parsed
     private final boolean parseHashes;
 
-    // the builder, created when parsing
-    private ModuleDescriptor.Builder builder;
-
     private ModuleInfo(Supplier<Set<String>> pf, boolean ph) {
         packageFinder = pf;
         parseHashes = ph;
@@ -169,7 +169,6 @@
         if (suffix < 1)
             throw invalidModuleDescriptor("this_class not of form name/module-info");
         mn = mn.substring(0, suffix).replace('/', '.');
-        builder = new ModuleDescriptor.Builder(mn);
 
         int super_class = in.readUnsignedShort();
         if (super_class > 0)
@@ -192,6 +191,13 @@
         // the names of the attributes found in the class file
         Set<String> attributes = new HashSet<>();
 
+        Builder builder = null;
+        Set<String> packages = null;
+        String version = null;
+        String mainClass = null;
+        String[] osValues = null;
+        ModuleHashes hashes = null;
+
         for (int i = 0; i < attributes_count ; i++) {
             int name_index = in.readUnsignedShort();
             String attribute_name = cpool.getUtf8(name_index);
@@ -206,28 +212,28 @@
             switch (attribute_name) {
 
                 case MODULE :
-                    readModuleAttribute(mn, in, cpool);
+                    builder = readModuleAttribute(mn, in, cpool);
                     break;
 
-                case CONCEALED_PACKAGES :
-                    readConcealedPackagesAttribute(in, cpool);
+                case PACKAGES :
+                    packages = readPackagesAttribute(in, cpool);
                     break;
 
                 case VERSION :
-                    readVersionAttribute(in, cpool);
+                    version = readVersionAttribute(in, cpool);
                     break;
 
                 case MAIN_CLASS :
-                    readMainClassAttribute(in, cpool);
+                    mainClass = readMainClassAttribute(in, cpool);
                     break;
 
                 case TARGET_PLATFORM :
-                    readTargetPlatformAttribute(in, cpool);
+                    osValues = readTargetPlatformAttribute(in, cpool);
                     break;
 
                 case HASHES :
                     if (parseHashes) {
-                        readHashesAttribute(in, cpool);
+                        hashes = readHashesAttribute(in, cpool);
                     } else {
                         in.skipBytes(length);
                     }
@@ -245,53 +251,87 @@
         }
 
         // the Module attribute is required
-        if (!attributes.contains(MODULE)) {
+        if (builder == null) {
             throw invalidModuleDescriptor(MODULE + " attribute not found");
         }
 
-        // If the ConcealedPackages attribute is not present then the
-        // packageFinder is used to to find any non-exported packages.
-        if (!attributes.contains(CONCEALED_PACKAGES) && packageFinder != null) {
-            Set<String> pkgs;
+        // If the Packages attribute is not present then the packageFinder is
+        // used to find the set of packages
+        boolean usedPackageFinder = false;
+        if (packages == null && packageFinder != null) {
             try {
-                pkgs = new HashSet<>(packageFinder.get());
+                packages = new HashSet<>(packageFinder.get());
             } catch (UncheckedIOException x) {
                 throw x.getCause();
             }
-            pkgs.removeAll(builder.exportedPackages());
-            builder.conceals(pkgs);
+            usedPackageFinder = true;
+        }
+        if (packages != null) {
+            for (String pn : builder.exportedPackages()) {
+                if (!packages.contains(pn)) {
+                    String tail;
+                    if (usedPackageFinder) {
+                        tail = " not found by package finder";
+                    } else {
+                        tail = " missing from Packages attribute";
+                    }
+                    throw invalidModuleDescriptor("Exported package " + pn + tail);
+                }
+                packages.remove(pn);
+            }
+            builder.contains(packages);
         }
 
-        // Was the Synthetic attribute present?
-        if (attributes.contains(SYNTHETIC))
-            builder.synthetic(true);
+        if (version != null)
+            builder.version(version);
+        if (mainClass != null)
+            builder.mainClass(mainClass);
+        if (osValues != null) {
+            if (osValues[0] != null) builder.osName(osValues[0]);
+            if (osValues[1] != null) builder.osArch(osValues[1]);
+            if (osValues[2] != null) builder.osVersion(osValues[2]);
+        }
+        if (hashes != null)
+            builder.hashes(hashes);
 
         return builder.build();
     }
 
     /**
-     * Reads the Module attribute.
+     * Reads the Module attribute, returning the ModuleDescriptor.Builder to
+     * build the corresponding ModuleDescriptor.
      */
-    private void readModuleAttribute(String mn, DataInput in, ConstantPool cpool)
+    private Builder readModuleAttribute(String mn, DataInput in, ConstantPool cpool)
         throws IOException
     {
+        Builder builder = new ModuleDescriptor.Builder(mn, /*strict*/ false);
+
+        int module_flags = in.readUnsignedShort();
+        boolean weak = ((module_flags & ACC_WEAK) != 0);
+        if (weak)
+            builder.weak(true);
+        if ((module_flags & ACC_SYNTHETIC) != 0)
+            builder.synthetic(true);
+
         int requires_count = in.readUnsignedShort();
         boolean requiresJavaBase = false;
         for (int i=0; i<requires_count; i++) {
             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.TRANSITIVE);
+                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"))
@@ -309,9 +349,28 @@
 
         int exports_count = in.readUnsignedShort();
         if (exports_count > 0) {
+            if (weak) {
+                throw invalidModuleDescriptor("The exports table for a weak"
+                                              + " module must be 0 length");
+            }
             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_REFLECTION) != 0)
+                        mods.add(Exports.Modifier.PRIVATE);
+                    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 +378,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);
                 }
             }
         }
@@ -355,59 +414,67 @@
                 builder.provides(e.getKey(), e.getValue());
             }
         }
+
+        return builder;
     }
 
     /**
-     * Reads the ConcealedPackages attribute
+     * Reads the Packages attribute
      */
-    private void readConcealedPackagesAttribute(DataInput in, ConstantPool cpool)
+    private Set<String> readPackagesAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
+        Set<String> packages = new HashSet<>();
         int package_count = in.readUnsignedShort();
         for (int i=0; i<package_count; i++) {
             int index = in.readUnsignedShort();
             String pn = cpool.getUtf8(index).replace('/', '.');
-            builder.conceals(pn);
+            packages.add(pn);
         }
+        return packages;
     }
 
     /**
      * Reads the Version attribute
      */
-    private void readVersionAttribute(DataInput in, ConstantPool cpool)
+    private String readVersionAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
         int index = in.readUnsignedShort();
-        builder.version(cpool.getUtf8(index));
+        return cpool.getUtf8(index);
     }
 
     /**
      * Reads the MainClass attribute
      */
-    private void readMainClassAttribute(DataInput in, ConstantPool cpool)
+    private String readMainClassAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
         int index = in.readUnsignedShort();
-        builder.mainClass(cpool.getClassName(index).replace('/', '.'));
+        return cpool.getClassName(index).replace('/', '.');
     }
 
     /**
      * Reads the TargetPlatform attribute
      */
-    private void readTargetPlatformAttribute(DataInput in, ConstantPool cpool)
+    private String[] readTargetPlatformAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
+        String[] values = new String[3];
+
         int name_index = in.readUnsignedShort();
         if (name_index != 0)
-            builder.osName(cpool.getUtf8(name_index));
+            values[0] = cpool.getUtf8(name_index);
 
         int arch_index = in.readUnsignedShort();
         if (arch_index != 0)
-            builder.osArch(cpool.getUtf8(arch_index));
+            values[1] = cpool.getUtf8(arch_index);
 
         int version_index = in.readUnsignedShort();
         if (version_index != 0)
-            builder.osVersion(cpool.getUtf8(version_index));
+            values[2] = cpool.getUtf8(version_index);
+
+        return values;
     }
 
 
@@ -417,7 +484,7 @@
      * @apiNote For now the hash is stored in base64 as a UTF-8 string, this
      * should be changed to be an array of u1.
      */
-    private void readHashesAttribute(DataInput in, ConstantPool cpool)
+    private ModuleHashes readHashesAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
         int index = in.readUnsignedShort();
@@ -434,7 +501,7 @@
             map.put(dn, hash);
         }
 
-        builder.hashes(new ModuleHashes(algorithm, map));
+        return new ModuleHashes(algorithm, map);
     }
 
 
@@ -447,7 +514,7 @@
         if (name.equals(MODULE) ||
                 name.equals(SOURCE_FILE) ||
                 name.equals(SDE) ||
-                name.equals(CONCEALED_PACKAGES) ||
+                name.equals(PACKAGES) ||
                 name.equals(VERSION) ||
                 name.equals(MAIN_CLASS) ||
                 name.equals(TARGET_PLATFORM) ||
@@ -461,8 +528,8 @@
      * Return true if the given attribute name is the name of a pre-defined
      * attribute that is not allowed in the class file.
      *
-     * Except for Module, InnerClasses, Synthetic, SourceFile, SourceDebugExtension,
-     * and Deprecated, none of the pre-defined attributes in JVMS 4.7 may appear.
+     * Except for Module, InnerClasses, SourceFile, SourceDebugExtension, and
+     * Deprecated, none of the pre-defined attributes in JVMS 4.7 may appear.
      */
     private static boolean isAttributeDisallowed(String name) {
         Set<String> notAllowed = predefinedNotAllowed;
@@ -477,12 +544,11 @@
                     "LineNumberTable",
                     "LocalVariableTable",
                     "LocalVariableTypeTable",
-                    "RuntimeVisibleAnnotations",
-                    "RuntimeInvisibleAnnotations",
                     "RuntimeVisibleParameterAnnotations",
                     "RuntimeInvisibleParameterAnnotations",
                     "RuntimeVisibleTypeAnnotations",
                     "RuntimeInvisibleTypeAnnotations",
+                    "Synthetic",
                     "AnnotationDefault",
                     "BootstrapMethods",
                     "MethodParameters");
--- a/src/java.base/share/classes/java/lang/module/ModulePath.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/ModulePath.java	Thu Sep 15 13:14:46 2016 -0700
@@ -32,13 +32,17 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor.Exports;
 import java.lang.module.ModuleDescriptor.Requires;
+import java.net.URI;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.Map;
@@ -56,7 +60,7 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
-import jdk.internal.module.ConfigurableModuleFinder;
+import jdk.internal.module.Checks;
 import jdk.internal.perf.PerfCounter;
 
 
@@ -64,35 +68,39 @@
  * A {@code ModuleFinder} that locates modules on the file system by searching
  * a sequence of directories or packaged modules.
  *
- * The {@code ModuleFinder} can be configured to work in either the run-time
+ * The {@code ModuleFinder} can be created to work in either the run-time
  * or link-time phases. In both cases it locates modular JAR and exploded
- * modules. When configured for link-time then it additionally locates
+ * modules. When created for link-time then it additionally locates
  * modules in JMOD files.
  */
 
-class ModulePath implements ConfigurableModuleFinder {
+class ModulePath implements ModuleFinder {
     private static final String MODULE_INFO = "module-info.class";
 
+    // the version to use for multi-release modular JARs
+    private final Runtime.Version releaseVersion;
+
+    // true for the link phase (supports modules packaged in JMOD format)
+    private final boolean isLinkPhase;
+
     // the entries on this module path
     private final Path[] entries;
     private int next;
 
-    // true if in the link phase
-    private boolean isLinkPhase;
-
     // map of module name to module reference map for modules already located
     private final Map<String, ModuleReference> cachedModules = new HashMap<>();
 
-    ModulePath(Path... entries) {
+    ModulePath(Runtime.Version version, boolean isLinkPhase, Path... entries) {
+        this.releaseVersion = version;
+        this.isLinkPhase = isLinkPhase;
         this.entries = entries.clone();
         for (Path entry : this.entries) {
             Objects.requireNonNull(entry);
         }
     }
 
-    @Override
-    public void configurePhase(Phase phase) {
-        isLinkPhase = (phase == Phase.LINK_TIME);
+    ModulePath(Path... entries) {
+        this(JarFile.runtimeVersion(), false, entries);
     }
 
     @Override
@@ -237,9 +245,13 @@
                 if (mref != null) {
                     // can have at most one version of a module in the directory
                     String name = mref.descriptor().name();
-                    if (nameToReference.put(name, mref) != null) {
+                    ModuleReference previous = nameToReference.put(name, mref);
+                    if (previous != null) {
+                        String fn1 = fileName(mref);
+                        String fn2 = fileName(previous);
                         throw new FindException("Two versions of module "
-                                                  + name + " found in " + dir);
+                                                 + name + " found in " + dir
+                                                 + " (" + fn1 + " and " + fn2 + ")");
                     }
                 }
             }
@@ -292,14 +304,34 @@
     }
 
 
+    /**
+     * Returns a string with the file name of the module if possible.
+     * If the module location is not a file URI then return the URI
+     * as a string.
+     */
+    private String fileName(ModuleReference mref) {
+        URI uri = mref.location().orElse(null);
+        if (uri != null) {
+            if (uri.getScheme().equalsIgnoreCase("file")) {
+                Path file = Paths.get(uri);
+                return file.getFileName().toString();
+            } else {
+                return uri.toString();
+            }
+        } else {
+            return "<unknown>";
+        }
+    }
+
     // -- jmod files --
 
     private Set<String> jmodPackages(ZipFile zf) {
         return zf.stream()
-            .filter(e -> e.getName().startsWith("classes/") &&
-                    e.getName().endsWith(".class"))
-            .map(e -> toPackageName(e.getName().substring(8)))
-            .filter(pkg -> pkg.length() > 0) // module-info
+            .map(ZipEntry::getName)
+            .filter(e -> e.startsWith("classes/") && !e.endsWith("/"))
+            .map(e -> e.substring(8))
+            .map(this::toPackageName)
+            .flatMap(Optional::stream)
             .collect(Collectors.toSet());
     }
 
@@ -328,10 +360,11 @@
     // -- JAR files --
 
     private static final String SERVICES_PREFIX = "META-INF/services/";
+    private static final String VERSIONS_PREFIX = "META-INF/versions/";
 
     /**
-     * Returns a container with the service type corresponding to the name of
-     * a services configuration file.
+     * Returns the service type corresponding to the name of a services
+     * configuration file if it's a valid Java identifier.
      *
      * For example, if called with "META-INF/services/p.S" then this method
      * returns a container with the value "p.S".
@@ -343,7 +376,8 @@
             String prefix = cf.substring(0, index);
             if (prefix.equals(SERVICES_PREFIX)) {
                 String sn = cf.substring(index);
-                return Optional.of(sn);
+                if (Checks.isJavaIdentifier(sn))
+                    return Optional.of(sn);
             }
         }
         return Optional.empty();
@@ -371,8 +405,8 @@
      *
      * 1. The module name (and optionally the version) is derived from the file
      *    name of the JAR file
-     * 2. The packages of all .class files in the JAR file are exported
-     * 3. It has no module-private/concealed packages
+     * 2. All packages are exported-private
+     * 3. It has no non-exported packages
      * 4. The contents of any META-INF/services configuration files are mapped
      *    to "provides" declarations
      * 5. The Main-Class attribute in the main attributes of the JAR manifest
@@ -412,34 +446,33 @@
 
         // Builder throws IAE if module name is empty or invalid
         ModuleDescriptor.Builder builder
-            = new ModuleDescriptor.Builder(mn)
-                .automatic()
+            = ModuleDescriptor.automaticModule(mn)
                 .requires(Set.of(Requires.Modifier.MANDATED), "java.base");
         if (vs != null)
             builder.version(vs);
 
-        // scan the entries in the JAR file to locate the .class and service
-        // configuration file
-        Map<Boolean, Set<String>> map =
-            versionedStream(jf)
-              .map(JarEntry::getName)
-              .filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX)))
-              .collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
-                                                 Collectors.toSet()));
-        Set<String> classFiles = map.get(Boolean.TRUE);
-        Set<String> configFiles = map.get(Boolean.FALSE);
+        // scan the names of the entries in the JAR file
+        Map<Boolean, Set<String>> map = jarEntryNames(jf)
+                .filter(e -> !e.endsWith("/"))
+                .collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX),
+                                                   Collectors.toSet()));
 
-        // all packages are exported
-        classFiles.stream()
-            .map(c -> toPackageName(c))
-            .distinct()
-            .forEach(builder::exports);
+        Set<String> resources = map.get(Boolean.FALSE);
+        Set<String> configFiles = map.get(Boolean.TRUE);
+
+        // all packages are exported-private
+        Set<Exports.Modifier> mods = EnumSet.of(Exports.Modifier.PRIVATE);
+        resources.stream()
+                .map(this::toPackageName)
+                .flatMap(Optional::stream)
+                .distinct()
+                .forEach(pn -> builder.exports(mods, pn));
 
         // map names of service configuration files to service names
         Set<String> serviceNames = configFiles.stream()
-            .map(this::toServiceName)
-            .flatMap(Optional::stream)
-            .collect(Collectors.toSet());
+                .map(this::toServiceName)
+                .flatMap(Optional::stream)
+                .collect(Collectors.toSet());
 
         // parse each service configuration file
         for (String sn : serviceNames) {
@@ -476,6 +509,7 @@
      */
     private static class Patterns {
         static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))");
+        static final Pattern TRAILING_VERSION = Pattern.compile("(\\.|\\d)*$");
         static final Pattern NON_ALPHANUM = Pattern.compile("[^A-Za-z0-9]");
         static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+");
         static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
@@ -486,6 +520,9 @@
      * Clean up candidate module name derived from a JAR file name.
      */
     private static String cleanModuleName(String mn) {
+        // drop trailing version from name
+        mn = Patterns.TRAILING_VERSION.matcher(mn).replaceAll("");
+
         // replace non-alphanumeric
         mn = Patterns.NON_ALPHANUM.matcher(mn).replaceAll(".");
 
@@ -504,25 +541,61 @@
         return mn;
     }
 
-    private Stream<JarEntry> versionedStream(JarFile jf) {
+    /**
+     * Returns a stream of the names of the entries in a JAR file. When the
+     * JAR file is a multi-release JAR then the stream includes the names of
+     * entries in the versioned section when those entries are applicable
+     * to the release version. Suppose this ModulePath was created with a
+     * a release version of 10. In that case, the stream will include the
+     * names of entries in META-INF/versions/10 and META-INF/versions/9.
+     * The return stream may contain duplicates.
+     */
+    private Stream<String> jarEntryNames(JarFile jf) {
         if (jf.isMultiRelease()) {
-            // a stream of JarEntries whose names are base names and whose
-            // contents are from the corresponding versioned entries in
-            // a multi-release jar file
-            return jf.stream().map(JarEntry::getName)
-                    .filter(name -> !name.startsWith("META-INF/versions/"))
-                    .map(jf::getJarEntry);
+            return jf.stream()
+                    .map(JarEntry::getName)
+                    .flatMap(name -> {
+                        if (name.startsWith(VERSIONS_PREFIX))
+                            name = toExternalEntryName(name);
+                        return (name != null) ? Stream.of(name) : Stream.empty();
+                    });
         } else {
-            return jf.stream();
+            return jf.stream()
+                    .map(JarEntry::getName)
+                    .filter(name -> !name.startsWith(VERSIONS_PREFIX));
         }
     }
 
+    /**
+     * Given the name of an entry in the versioned section of JAR file,
+     * returns the entry name, minus the META-INF/versions/N/ prefix, if
+     * the entry is applicable to the release version that this ModulePath
+     * was created with.
+     */
+    private String toExternalEntryName(String name) {
+        int prefixLen = VERSIONS_PREFIX.length();
+        if (name.length() > prefixLen) {
+            String tail = name.substring(prefixLen);
+            int index = tail.indexOf('/');
+            if (index > 0) {
+                String vs = tail.substring(0, index);
+                try {
+                    int v = Integer.parseInt(vs);
+                    if (v > 8 && v <= releaseVersion.major()) {
+                        return tail.substring(index+1);
+                    }
+                } catch (NumberFormatException ignore) { }
+            }
+        }
+        return null;
+    }
+
     private Set<String> jarPackages(JarFile jf) {
-        return versionedStream(jf)
-            .filter(e -> e.getName().endsWith(".class"))
-            .map(e -> toPackageName(e.getName()))
-            .filter(pkg -> pkg.length() > 0)   // module-info
-            .collect(Collectors.toSet());
+        return jarEntryNames(jf)
+                .filter(e -> !e.endsWith("/"))
+                .map(this::toPackageName)
+                .flatMap(Optional::stream)
+                .collect(Collectors.toSet());
     }
 
     /**
@@ -537,7 +610,7 @@
         try (JarFile jf = new JarFile(file.toFile(),
                                       true,               // verify
                                       ZipFile.OPEN_READ,
-                                      JarFile.runtimeVersion()))
+                                      releaseVersion))
         {
             ModuleDescriptor md;
             JarEntry entry = jf.getJarEntry(MODULE_INFO);
@@ -567,11 +640,11 @@
     private Set<String> explodedPackages(Path dir) {
         try {
             return Files.find(dir, Integer.MAX_VALUE,
-                              ((path, attrs) -> attrs.isRegularFile() &&
-                               path.toString().endsWith(".class")))
-                .map(path -> toPackageName(dir.relativize(path)))
-                .filter(pkg -> pkg.length() > 0)   // module-info
-                .collect(Collectors.toSet());
+                              ((path, attrs) -> attrs.isRegularFile()))
+                    .map(path -> dir.relativize(path))
+                    .map(this::toPackageName)
+                    .flatMap(Optional::stream)
+                    .collect(Collectors.toSet());
         } catch (IOException x) {
             throw new UncheckedIOException(x);
         }
@@ -597,29 +670,62 @@
         return ModuleReferences.newExplodedModule(md, dir);
     }
 
+    /**
+     * Maps the name of an entry in a JAR or ZIP file to a package name.
+     *
+     * @throws IllegalArgumentException if the name is a class file in
+     *         the top-level directory of the JAR/ZIP file (and it's
+     *         not module-info.class)
+     */
+    private Optional<String> toPackageName(String name) {
+        assert !name.endsWith("/");
 
-    //
+        int index = name.lastIndexOf("/");
+        if (index == -1) {
+            if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
+                throw new IllegalArgumentException(name
+                        + " found in top-level directory:"
+                        + " (unnamed package not allowed in module)");
+            }
+            return Optional.empty();
+        }
 
-    // p/q/T.class => p.q
-    private String toPackageName(String cn) {
-        assert cn.endsWith(".class");
-        int start = 0;
-        int index = cn.lastIndexOf("/");
-        if (index > start) {
-            return cn.substring(start, index).replace('/', '.');
+        String pn = name.substring(0, index).replace('/', '.');
+        if (Checks.isJavaIdentifier(pn)) {
+            return Optional.of(pn);
         } else {
-            return "";
+            // not a valid package name
+            return Optional.empty();
         }
     }
 
-    private String toPackageName(Path path) {
-        String name = path.toString();
-        assert name.endsWith(".class");
-        int index = name.lastIndexOf(File.separatorChar);
-        if (index != -1) {
-            return name.substring(0, index).replace(File.separatorChar, '.');
+    /**
+     * Maps the relative path of an entry in an exploded module to a package
+     * name.
+     *
+     * @throws IllegalArgumentException if the name is a class file in
+     *         the top-level directory (and it's not module-info.class)
+     */
+    private Optional<String> toPackageName(Path file) {
+        assert file.getRoot() == null;
+
+        Path parent = file.getParent();
+        if (parent == null) {
+            String name = file.toString();
+            if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
+                throw new IllegalArgumentException(name
+                        + " found in in top-level directory"
+                        + " (unnamed package not allowed in module)");
+            }
+            return Optional.empty();
+        }
+
+        String pn = parent.toString().replace(File.separatorChar, '.');
+        if (Checks.isJavaIdentifier(pn)) {
+            return Optional.of(pn);
         } else {
-            return "";
+            // not a valid package name
+            return Optional.empty();
         }
     }
 
--- a/src/java.base/share/classes/java/lang/module/Resolver.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/Resolver.java	Thu Sep 15 13:14:46 2016 -0700
@@ -26,6 +26,7 @@
 package java.lang.module;
 
 import java.io.PrintStream;
+import java.lang.module.ModuleDescriptor.Exports;
 import java.lang.module.ModuleDescriptor.Requires.Modifier;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -125,6 +126,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
@@ -456,10 +462,10 @@
      * Computes the readability graph for the modules in the given Configuration.
      *
      * The readability graph is created by propagating "requires" through the
-     * "public requires" edges of the module dependence graph. So if the module
-     * dependence graph has m1 requires m2 && m2 requires public m3 then the
-     * resulting readability graph will contain m1 reads m2, m1 reads m3, and
-     * m2 reads m3.
+     * "requires transitive" edges of the module dependence graph. So if the
+     * module dependence graph has m1 requires m2 && m2 requires transitive m3
+     * then the resulting readability graph will contain m1 reads m2, m1 reads m3,
+     * and m2 reads m3.
      */
     private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) {
 
@@ -467,12 +473,12 @@
         // is iteratively updated to be the readability graph
         Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>();
 
-        // the "requires public" graph, contains requires public edges only
+        // the "requires transitive" graph, contains requires transitive edges only
         Map<ResolvedModule, Set<ResolvedModule>> g2 = new HashMap<>();
 
 
-        // need "requires public" from the modules in parent configurations as
-        // there may be selected modules that have a dependency on modules in
+        // need "requires transitive" from the modules in parent configurations
+        // as there may be selected modules that have a dependency on modules in
         // the parent configuration.
 
         Configuration p = parent;
@@ -482,7 +488,7 @@
                 ResolvedModule m1 = p.findModule(name)
                     .orElseThrow(() -> new InternalError(name + " not found"));
                 for (ModuleDescriptor.Requires requires : descriptor.requires()) {
-                    if (requires.modifiers().contains(Modifier.PUBLIC)) {
+                    if (requires.modifiers().contains(Modifier.TRANSITIVE)) {
                         String dn = requires.name();
                         ResolvedModule m2 = p.findModule(dn)
                             .orElseThrow(() -> new InternalError(dn + " not found"));
@@ -505,7 +511,7 @@
             ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref);
 
             Set<ResolvedModule> reads = new HashSet<>();
-            Set<ResolvedModule> requiresPublic = new HashSet<>();
+            Set<ResolvedModule> requiresTransitive= new HashSet<>();
 
             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
                 String dn = requires.name();
@@ -519,6 +525,7 @@
                     // parent configuration
                     m2 = parent.findModule(dn).orElse(null);
                     if (m2 == null) {
+                        assert requires.modifiers().contains(Modifier.STATIC);
                         continue;
                     }
                 }
@@ -526,9 +533,9 @@
                 // m1 requires m2 => m1 reads m2
                 reads.add(m2);
 
-                // m1 requires public m2
-                if (requires.modifiers().contains(Modifier.PUBLIC)) {
-                    requiresPublic.add(m2);
+                // m1 requires transitive m2
+                if (requires.modifiers().contains(Modifier.TRANSITIVE)) {
+                    requiresTransitive.add(m2);
                 }
 
             }
@@ -538,7 +545,7 @@
             if (descriptor.isAutomatic()) {
 
                 // reads all selected modules
-                // `requires public` all selected automatic modules
+                // `requires transitive` all selected automatic modules
                 for (ModuleReference mref2 : nameToReference.values()) {
                     ModuleDescriptor descriptor2 = mref2.descriptor();
                     String name2 = descriptor2.name();
@@ -548,18 +555,19 @@
                             = computeIfAbsent(nameToResolved, name2, cf, mref2);
                         reads.add(m2);
                         if (descriptor2.isAutomatic())
-                            requiresPublic.add(m2);
+                            requiresTransitive.add(m2);
                     }
                 }
 
                 // reads all modules in parent configurations
-                // `requires public` all automatic modules in parent configurations
+                // `requires transitive` all automatic modules in parent
+                // configurations
                 p = parent;
                 while (p != null) {
                     for (ResolvedModule m : p.modules()) {
                         reads.add(m);
                         if (m.reference().descriptor().isAutomatic())
-                            requiresPublic.add(m);
+                            requiresTransitive.add(m);
                     }
                     p = p.parent().orElse(null);
                 }
@@ -567,21 +575,22 @@
             }
 
             g1.put(m1, reads);
-            g2.put(m1, requiresPublic);
+            g2.put(m1, requiresTransitive);
         }
 
-        // Iteratively update g1 until there are no more requires public to propagate
+        // Iteratively update g1 until there are no more requires transitive
+        // to propagate
         boolean changed;
         Set<ResolvedModule> toAdd = new HashSet<>();
         do {
             changed = false;
             for (Set<ResolvedModule> m1Reads : g1.values()) {
                 for (ResolvedModule m2 : m1Reads) {
-                    Set<ResolvedModule> m2RequiresPublic = g2.get(m2);
-                    if (m2RequiresPublic != null) {
-                        for (ResolvedModule m3 : m2RequiresPublic) {
+                    Set<ResolvedModule> m2RequiresTransitive = g2.get(m2);
+                    if (m2RequiresTransitive != null) {
+                        for (ResolvedModule m3 : m2RequiresTransitive) {
                             if (!m1Reads.contains(m3)) {
-                                // m1 reads m2, m2 requires public m3
+                                // m1 reads m2, m2 requires transitive m3
                                 // => need to add m1 reads m3
                                 toAdd.add(m3);
                             }
@@ -655,7 +664,7 @@
                     // source is exported to descriptor2
                     String source = export.source();
                     ModuleDescriptor other
-                        = packageToExporter.put(source, descriptor2);
+                        = packageToExporter.putIfAbsent(source, descriptor2);
 
                     if (other != null && other != descriptor2) {
                         // package might be local to descriptor1
@@ -677,6 +686,33 @@
 
                     }
                 }
+
+                if (descriptor2.isWeak()) {
+                    for (String source : descriptor2.packages()) {
+                        ModuleDescriptor other
+                            = packageToExporter.putIfAbsent(source, descriptor2);
+
+                        if (other != null && other != descriptor2) {
+                            // package might be local to descriptor1
+                            if (other == descriptor1) {
+                                fail("Module %s contains package %s"
+                                      + ", module %s exports package %s to %s",
+                                        descriptor1.name(),
+                                        source,
+                                        descriptor2.name(),
+                                        source,
+                                        descriptor1.name());
+                            } else {
+                                fail("Modules %s and %s export package %s to module %s",
+                                        descriptor2.name(),
+                                        other.name(),
+                                        source,
+                                        descriptor1.name());
+                            }
+
+                        }
+                    }
+                }
             }
 
             // uses/provides checks not applicable to automatic modules
--- a/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java	Thu Sep 15 13:14:46 2016 -0700
@@ -55,7 +55,7 @@
  * run-time image.
  *
  * The modules linked into the run-time image are assumed to have the
- * ConcealedPackages attribute.
+ * Packages attribute.
  */
 
 class SystemModuleFinder implements ModuleFinder {
--- a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java	Thu Sep 15 13:14:46 2016 -0700
@@ -25,12 +25,12 @@
 
 package java.lang.reflect;
 
+import java.lang.annotation.Annotation;
 import java.security.AccessController;
 
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;
 import jdk.internal.reflect.ReflectionFactory;
-import java.lang.annotation.Annotation;
 
 /**
  * The AccessibleObject class is the base class for Field, Method and
@@ -81,8 +81,10 @@
      * <p>This method cannot be used to enable access to an object that is a
      * {@link Member member} of a class in a different module to the caller and
      * where the class is in a package that is not exported to the caller's
-     * module. Additionally, this method cannot be used to enable access to
-     * non-public members of {@code AccessibleObject} or {@link Module}.
+     * module. Additionally, if the member is non-public or its declaring
+     * class is non-public, then this method can only be used to enable access
+     * if the package is {@link Module#isExportedPrivate exported-private} to
+     * the caller's module.
      *
      * <p>If there is a security manager, its
      * {@code checkPermission} method is first called with a
@@ -126,8 +128,10 @@
      * <p>This method cannot be used to enable access to an object that is a
      * {@link Member member} of a class in a different module to the caller and
      * where the class is in a package that is not exported to the caller's
-     * module. Additionally, this method cannot be used to enable access to
-     * non-public members of {@code AccessibleObject} or {@link Module}.
+     * module. Additionally, if the member is non-public or its declaring
+     * class is non-public, then this method can only be used to enable access
+     * if the package is {@link Module#isExportedPrivate exported-private} to
+     * the caller's module.
      *
      * <p>If there is a security manager, its
      * {@code checkPermission} method is first called with a
@@ -161,35 +165,35 @@
         Module callerModule = caller.getModule();
         Module declaringModule = declaringClass.getModule();
 
-        if (callerModule != declaringModule
-                && callerModule != Object.class.getModule()) {
+        if (callerModule == declaringModule) return;
+        if (callerModule == Object.class.getModule()) return;
+        if (!declaringModule.isNamed()) return;
 
-            // check exports to target module
-            String pn = packageName(declaringClass);
-            if (!declaringModule.isExported(pn, callerModule)) {
-                String msg = "Unable to make member of "
-                        + declaringClass + " accessible:  "
-                        + declaringModule + " does not export "
-                        + pn + " to " + callerModule;
-                Reflection.throwInaccessibleObjectException(msg);
-            }
+        // package is exported-private to caller
+        String pn = packageName(declaringClass);
+        if (declaringModule.isExportedPrivate(pn, callerModule))
+            return;
 
+        // package is exported to caller and class/member is public
+        boolean isExported = declaringModule.isExported(pn, callerModule);
+        boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
+        int modifiers;
+        if (this instanceof Executable) {
+            modifiers = ((Executable) this).getModifiers();
+        } else {
+            modifiers = ((Field) this).getModifiers();
         }
+        boolean isMemberPublic = Modifier.isPublic(modifiers);
+        if (isExported && isClassPublic && isMemberPublic)
+            return;
 
-        if (declaringClass == Module.class
-                || declaringClass == AccessibleObject.class) {
-            int modifiers;
-            if (this instanceof Executable) {
-                modifiers = ((Executable) this).getModifiers();
-            } else {
-                modifiers = ((Field) this).getModifiers();
-            }
-            if (!Modifier.isPublic(modifiers)) {
-                String msg = "Cannot make a non-public member of "
-                        + declaringClass + " accessible";
-                Reflection.throwInaccessibleObjectException(msg);
-            }
-        }
+        // not accessible
+        String msg = "Unable to make " + this + " accessible: ";
+        msg += declaringModule + " does not \"exports ";
+        if (!isClassPublic || !isMemberPublic)
+            msg += "private ";
+        msg += pn + "\" to " + callerModule;
+        Reflection.throwInaccessibleObjectException(msg);
     }
 
     /**
--- a/src/java.base/share/classes/java/lang/reflect/Layer.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/reflect/Layer.java	Thu Sep 15 13:14:46 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;
 
 
@@ -180,7 +178,7 @@
      * <ul>
      *
      *     <li><p> <em>Overlapping packages</em>: Two or more modules in the
-     *     configuration have the same package (exported or concealed). </p></li>
+     *     configuration have the same package. </p></li>
      *
      *     <li><p> <em>Split delegation</em>: The resulting class loader would
      *     need to delegate to more than one class loader in order to load types
@@ -206,7 +204,8 @@
      * @throws LayerInstantiationException
      *         If all modules cannot be defined to the same class loader for any
      *         of the reasons listed above or the layer cannot be created because
-     *         the configuration contains a module named "{@code java.base}"
+     *         the configuration contains a module named "{@code java.base}" or
+     *         a module with a package name starting with "{@code java.}"
      * @throws SecurityException
      *         If {@code RuntimePermission("createClassLoader")} or
      *         {@code RuntimePermission("getClassLoader")} is denied by
@@ -269,7 +268,8 @@
      *         for this layer
      * @throws LayerInstantiationException
      *         If the layer cannot be created because the configuration contains
-     *         a module named "{@code java.base}"
+     *         a module named "{@code java.base}" or a module with a package
+     *         name starting with "{@code java.}"
      * @throws SecurityException
      *         If {@code RuntimePermission("createClassLoader")} or
      *         {@code RuntimePermission("getClassLoader")} is denied by
@@ -298,16 +298,19 @@
      * modules in the given {@code Configuration} to the Java virtual machine.
      * Each module is mapped, by name, to its class loader by means of the
      * given function. The class loader delegation implemented by these class
-     * loaders must respect module readability. In addition, the caller needs
-     * to arrange that the class loaders are ready to load from these module
-     * before there are any attempts to load classes or resources.
+     * loaders must respect module readability. The class loaders should be
+     * {@link ClassLoader#registerAsParallelCapable parallel-capable} so as to
+     * avoid deadlocks during class loading. In addition, the entity creating
+     * a new layer with this method should arrange that the class loaders are
+     * ready to load from these module before there are any attempts to load
+     * classes or resources.
      *
      * <p> Creating a {@code Layer} can fail for the following reasons: </p>
      *
      * <ul>
      *
-     *     <li><p> Two or more modules with the same package (exported or
-     *     concealed) are mapped to the same class loader. </p></li>
+     *     <li><p> Two or more modules with the same package are mapped to the
+     *     same class loader. </p></li>
      *
      *     <li><p> A module is mapped to a class loader that already has a
      *     module of the same name defined to it. </p></li>
@@ -338,8 +341,14 @@
      *         for this layer
      * @throws LayerInstantiationException
      *         If creating the {@code Layer} fails for any of the reasons
-     *         listed above or the layer cannot be created because the
-     *         configuration contains a module named "{@code java.base}"
+     *         listed above, the layer cannot be created because the
+     *         configuration contains a module named "{@code java.base}",
+     *         a module with a package name starting with "{@code java.}" is
+     *         mapped to a class loader other than the {@link
+     *         ClassLoader#getPlatformClassLoader() platform class loader},
+     *         or the function to map a module name to a class loader returns
+     *         {@code null}
+     *
      * @throws SecurityException
      *         If {@code RuntimePermission("getClassLoader")} is denied by
      *         the security manager
@@ -572,39 +581,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	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/lang/reflect/Module.java	Thu Sep 15 13:14:46 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,17 @@
 
 import jdk.internal.loader.BuiltinClassLoader;
 import jdk.internal.loader.BootLoader;
+import jdk.internal.loader.ResourceHelper;
+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 +94,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;
@@ -246,18 +257,14 @@
     // special Module to mean exported to "everyone"
     private static final Module EVERYONE_MODULE = new Module(null);
 
-    // exported to all modules
-    private static final Set<Module> EVERYONE = Collections.singleton(EVERYONE_MODULE);
-
 
     // -- 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 +300,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,30 +388,36 @@
         }
 
         // 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)
-    private volatile Map<String, Set<Module>> exports;
+    // the packages that are exported
+
+    // package name (key) -> exported-private? (value)
+    private volatile Map<String, Boolean> unqualifiedExports;
+
+    // package name (key) -> (target-module, exported-private?) (value)
+    private volatile Map<String, Map<Module, Boolean>> qualifiedExports;
+
 
     // additional exports added at run-time
-    // this module (1st key), other module (2nd key), exported packages (value)
+    // this module (1st key), other module (2nd key)
+    // (package name, exported-private?) (value)
     private static final WeakPairMap<Module, Module, Map<String, Boolean>>
-        transientExports = new WeakPairMap<>();
+        reflectivelyExports = new WeakPairMap<>();
 
 
     /**
-     * Returns {@code true} if this module exports the given package to at
-     * least the given module.
+     * Returns {@code true} if this module exports (or <em>exports-private</em>)
+     * the given package to at least the given module.
      *
-     * <p> This method always return {@code true} when invoked on an unnamed
-     * module. </p>
+     * <p> This method always returns {@code true} when invoked on an unnamed
+     * module or a weak module. </p>
      *
-     * <p> This method does not check if the given module reads this module </p>
+     * <p> This method does not check if the given module reads this module. </p>
      *
      * @param  pn
      *         The package name
@@ -417,17 +430,42 @@
     public boolean isExported(String pn, Module other) {
         Objects.requireNonNull(pn);
         Objects.requireNonNull(other);
-        return implIsExported(pn, other);
+        return implIsExported(pn, false, other);
     }
 
     /**
-     * Returns {@code true} if this module exports the given package
-     * unconditionally.
+     * Returns {@code true} if this module <em>exports-private</em> the given
+     * package to at least the given module.
      *
-     * <p> This method always return {@code true} when invoked on an unnamed
-     * module. </p>
+     * <p> This method always returns {@code true} when invoked on an unnamed
+     * module or a weak module. </p>
      *
-     * <p> This method does not check if the given module reads this module </p>
+     * <p> This method does not check if the given module reads this module. </p>
+     *
+     * @param  pn
+     *         The package name
+     * @param  other
+     *         The other module
+     *
+     * @return {@code true} if this module <em>exports-private</em>  the package
+     *         to at least the given module
+     *
+     * @see AccessibleObject#setAccessible(boolean)
+     */
+    public boolean isExportedPrivate(String pn, Module other) {
+        Objects.requireNonNull(pn);
+        Objects.requireNonNull(other);
+        return implIsExported(pn, true, other);
+    }
+
+    /**
+     * Returns {@code true} if this module exports (or <em>exports-private</em>)
+     * the given package unconditionally.
+     *
+     * <p> This method always returns {@code true} when invoked on an unnamed
+     * module or a weak module. </p>
+     *
+     * <p> This method does not check if the given module reads this module. </p>
      *
      * @param  pn
      *         The package name
@@ -436,26 +474,47 @@
      */
     public boolean isExported(String pn) {
         Objects.requireNonNull(pn);
-        return implIsExported(pn, EVERYONE_MODULE);
+        return implIsExported(pn, false, EVERYONE_MODULE);
     }
 
     /**
+     * Returns {@code true} if this module <em>exports-private</em> the given
+     * package unconditionally.
+     *
+     * <p> This method always returns {@code true} when invoked on an unnamed
+     * module or weak module. </p>
+     *
+     * <p> This method does not check if the given module reads this module. </p>
+     *
+     * @param  pn
+     *         The package name
+     *
+     * @return {@code true} if this module <em>exports-private</em>  the package
+     *         unconditionally
+     */
+    public boolean isExportedPrivate(String pn) {
+        Objects.requireNonNull(pn);
+        return implIsExported(pn, true, EVERYONE_MODULE);
+    }
+
+
+    /**
      * Returns {@code true} if this module exports the given package to the
      * given module. If the other module is {@code EVERYONE_MODULE} then
      * this method tests if the package is exported unconditionally.
      */
-    private boolean implIsExported(String pn, Module other) {
+    private boolean implIsExported(String pn, boolean nonPublic, Module other) {
 
-        // all packages are exported by unnamed modules
-        if (!isNamed())
+        // all packages are exported-private by unnamed and weak modules
+        if (name == null || descriptor.isWeak())
             return true;
 
         // exported via module declaration/descriptor
-        if (isExportedPermanently(pn, other))
+        if (isExportedStatically(pn, nonPublic, other))
             return true;
 
         // exported via addExports
-        if (isExportedReflectively(pn, other))
+        if (isExportedReflectively(pn, nonPublic, other))
             return true;
 
         // not exported or not exported to other
@@ -463,43 +522,76 @@
     }
 
     /**
-     * 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) {
-        Map<String, Set<Module>> exports = this.exports;
-        if (exports != null) {
-            Set<Module> targets = exports.get(pn);
+    private boolean isExportedStatically(String pn, boolean nonPublic, Module other) {
+        // exported unconditionally?
+        Map<String, Boolean> unqualifiedExports = this.unqualifiedExports;
+        if (unqualifiedExports != null) {
+            Boolean b = unqualifiedExports.get(pn);
+            if (b != null) {
+                boolean exportedPrivate = b.booleanValue();
+                if (!nonPublic || exportedPrivate) return true;
+            }
+        }
 
-            if ((targets != null)
-                && (targets.contains(other) || targets.contains(EVERYONE_MODULE)))
-                return true;
+        // qualified export
+        if (other != EVERYONE_MODULE && other != ALL_UNNAMED_MODULE) {
+            Map<String, Map<Module, Boolean>> qualifiedExports = this.qualifiedExports;
+            if (qualifiedExports != null) {
+                Map<Module, Boolean> targets = qualifiedExports.get(pn);
+                if (targets != null) {
+                    Boolean b = targets.get(other);
+                    if (b != null) {
+                        boolean exportedPrivate = b.booleanValue();
+                        if (!nonPublic || exportedPrivate) return true;
+                    }
+                }
+            }
         }
+
         return false;
     }
 
+
     /**
      * Returns {@code true} if this module reflectively exports the given
      * package package to the given module.
      */
-    private boolean isExportedReflectively(String pn, Module other) {
+    private boolean isExportedReflectively(String pn, boolean nonPublic, Module other) {
         // exported to all modules
-        Map<String, ?> exports = transientExports.get(this, EVERYONE_MODULE);
-        if (exports != null && exports.containsKey(pn))
-            return true;
+        Map<String, Boolean> exports = reflectivelyExports.get(this, EVERYONE_MODULE);
+        if (exports != null) {
+            Boolean b = exports.get(pn);
+            if (b != null) {
+                boolean exportedPrivate = b.booleanValue();
+                if (!nonPublic || exportedPrivate) return true;
+            }
+        }
 
         if (other != EVERYONE_MODULE) {
 
             // exported to other
-            exports = transientExports.get(this, other);
-            if (exports != null && exports.containsKey(pn))
-                return true;
+            exports = reflectivelyExports.get(this, other);
+            if (exports != null) {
+                Boolean b = exports.get(pn);
+                if (b != null) {
+                    boolean exportedPrivate = b.booleanValue();
+                    if (!nonPublic || exportedPrivate) return true;
+                }
+            }
 
             // other is an unnamed module && exported to all unnamed
             if (!other.isNamed()) {
-                exports = transientExports.get(this, ALL_UNNAMED_MODULE);
-                if (exports != null && exports.containsKey(pn))
-                    return true;
+                exports = reflectivelyExports.get(this, ALL_UNNAMED_MODULE);
+                if (exports != null) {
+                    Boolean b = exports.get(pn);
+                    if (b != null) {
+                        boolean exportedPrivate = b.booleanValue();
+                        if (!nonPublic || exportedPrivate) return true;
+                    }
+                }
             }
 
         }
@@ -510,11 +602,12 @@
 
     /**
      * If the caller's module is this module then update this module to export
-     * package {@code pn} to the given module.
+     * the given package to the given module.
      *
-     * <p> This method has no effect if the package is already exported to the
-     * given module. It also has no effect if invoked on an unnamed module (as
-     * unnamed modules export all packages). </p>
+     * <p> This method has no effect if the package is already exported (or
+     * <em>exported private</em>) to the given module. It also has no effect if
+     * invoked on an unnamed module or a weak module (as unnamed modules and
+     * weak modules <em>exports-private</em> all packages). </p>
      *
      * @param  pn
      *         The package name
@@ -535,18 +628,64 @@
             throw new IllegalArgumentException("package is null");
         Objects.requireNonNull(other);
 
-        if (isNamed()) {
+        if (isNamed() && !descriptor.isWeak()) {
             Module caller = Reflection.getCallerClass().getModule();
             if (caller != this) {
                 throw new IllegalStateException(caller + " != " + this);
             }
-            implAddExports(pn, other, true);
+            implAddExports(pn, other, false, true);
         }
 
         return this;
     }
 
     /**
+     * If the caller's module is this module then update this module to
+     * <em>exports-private</em> the given package to the given module.
+     * Exporting a package with this method allows all types in the package,
+     * and all their members, not just public types and their public members,
+     * to be reflected on by the given module when using APIs that bypass or
+     * suppress default Java language access control checks.
+     *
+     * <p> This method has no effect if the package is already <em>exported
+     * private</em> to the given module. It also has no effect if invoked on an
+     * unnamed module or a weak module (as unnamed modules and weak modules
+     * <em>exports-private</em> all packages). </p>
+     *
+     * @param  pn
+     *         The package name
+     * @param  other
+     *         The module
+     *
+     * @return this module
+     *
+     * @throws IllegalArgumentException
+     *         If {@code pn} is {@code null}, or this is a named module and the
+     *         package {@code pn} is not a package in this module
+     * @throws IllegalStateException
+     *         If this is a named module and the caller is not this module
+     *
+     * @see AccessibleObject#setAccessible(boolean)
+     */
+    @CallerSensitive
+    public Module addExportsPrivate(String pn, Module other) {
+        if (pn == null)
+            throw new IllegalArgumentException("package is null");
+        Objects.requireNonNull(other);
+
+        if (isNamed() && !descriptor.isWeak()) {
+            Module caller = Reflection.getCallerClass().getModule();
+            if (caller != this) {
+                throw new IllegalStateException(caller + " != " + this);
+            }
+            implAddExports(pn, other, true, true);
+        }
+
+        return this;
+    }
+
+
+    /**
      * Updates the exports so that package {@code pn} is exported to module
      * {@code other} but without notifying the VM.
      *
@@ -555,7 +694,7 @@
     void implAddExportsNoSync(String pn, Module other) {
         if (other == null)
             other = EVERYONE_MODULE;
-        implAddExports(pn.replace('/', '.'), other, false);
+        implAddExports(pn.replace('/', '.'), other, false, false);
     }
 
     /**
@@ -565,7 +704,17 @@
      * @apiNote This method is for white-box testing.
      */
     void implAddExports(String pn, Module other) {
-        implAddExports(pn, other, true);
+        implAddExports(pn, other, false, true);
+    }
+
+    /**
+     * Updates the exports so that package {@code pn} is <em>exports-private</em>
+     * to module {@code other}.
+     *
+     * @apiNote This method is for white-box testing.
+     */
+    void implAddExportsPrivate(String pn, Module other) {
+        implAddExports(pn, other, true, true);
     }
 
     /**
@@ -574,16 +723,19 @@
      *
      * If {@code syncVM} is {@code true} then the VM is notified.
      */
-    private void implAddExports(String pn, Module other, boolean syncVM) {
+    private void implAddExports(String pn,
+                                Module other,
+                                boolean nonPublic,
+                                boolean syncVM) {
         Objects.requireNonNull(other);
         Objects.requireNonNull(pn);
 
-        // unnamed modules export all packages
-        if (!isNamed())
+        // unnamed modules and weak modules export-private all packages
+        if (!isNamed() || descriptor.isWeak())
             return;
 
         // nothing to do if already exported to other
-        if (implIsExported(pn, other))
+        if (implIsExported(pn, nonPublic, other))
             return;
 
         // can only export a package in the module
@@ -604,18 +756,23 @@
             }
         }
 
-        // add package name to transientExports if absent
-        transientExports
+        // add package name to reflectivelyExports if absent
+        Map<String, Boolean> map = reflectivelyExports
             .computeIfAbsent(this, other,
-                             (_this, _other) -> new ConcurrentHashMap<>())
-            .putIfAbsent(pn, Boolean.TRUE);
+                             (m1, m2) -> new ConcurrentHashMap<>());
+
+        if (nonPublic) {
+            map.put(pn, Boolean.TRUE);  // may need to promote from FALSE to TRUE
+        } else {
+            map.putIfAbsent(pn, Boolean.FALSE);
+        }
     }
 
 
     // -- services --
 
     // additional service type (2nd key) that some module (1st key) uses
-    private static final WeakPairMap<Module, Class<?>, Boolean> transientUses
+    private static final WeakPairMap<Module, Class<?>, Boolean> reflectivelyUses
         = new WeakPairMap<>();
 
     /**
@@ -624,13 +781,13 @@
      * for use by frameworks that invoke {@link java.util.ServiceLoader
      * ServiceLoader} on behalf of other modules or where the framework is
      * passed a reference to the service type by other code. This method is
-     * a no-op when invoked on an unnamed module.
+     * a no-op when invoked on an unnamed module or an automatic module.
      *
      * <p> This method does not cause {@link
      * Configuration#resolveRequiresAndUses resolveRequiresAndUses} to be
      * re-run. </p>
      *
-     * @param  st
+     * @param  service
      *         The service type
      *
      * @return this module
@@ -642,39 +799,45 @@
      * @see ModuleDescriptor#uses()
      */
     @CallerSensitive
-    public Module addUses(Class<?> st) {
-        Objects.requireNonNull(st);
+    public Module addUses(Class<?> service) {
+        Objects.requireNonNull(service);
 
-        if (isNamed()) {
-
+        if (isNamed() && !descriptor.isAutomatic()) {
             Module caller = Reflection.getCallerClass().getModule();
             if (caller != this) {
                 throw new IllegalStateException(caller + " != " + this);
             }
-
-            if (!canUse(st)) {
-                transientUses.putIfAbsent(this, st, Boolean.TRUE);
-            }
-
+            implAddUses(service);
         }
 
         return this;
     }
 
     /**
+     * Update this module to add a service dependence on the given service
+     * type.
+     */
+    void implAddUses(Class<?> service) {
+        if (!canUse(service)) {
+            reflectivelyUses.putIfAbsent(this, service, 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.
+     * module or an automatic module.
      *
-     * @param  st
+     * @param  service
      *         The service type
      *
      * @return {@code true} if this module uses service type {@code st}
      *
      * @see #addUses(Class)
      */
-    public boolean canUse(Class<?> st) {
-        Objects.requireNonNull(st);
+    public boolean canUse(Class<?> service) {
+        Objects.requireNonNull(service);
 
         if (!isNamed())
             return true;
@@ -683,11 +846,11 @@
             return true;
 
         // uses was declared
-        if (descriptor.uses().contains(st.getName()))
+        if (descriptor.uses().contains(service.getName()))
             return true;
 
         // uses added via addUses
-        return transientUses.containsKeyPair(this, st);
+        return reflectivelyUses.containsKeyPair(this, service);
     }
 
 
@@ -822,6 +985,123 @@
     // -- creating Module objects --
 
     /**
+     * Defines all module in a configuration to the runtime.
+     *
+     * @return a map of module name to runtime {@code Module}
+     *
+     * @throws IllegalArgumentException
+     *         If defining any of the modules to the VM fails
+     */
+    static Map<String, Module> defineModules(Configuration cf,
+                                             Function<String, ClassLoader> clf,
+                                             Layer layer)
+    {
+        Map<String, Module> nameToModule = new HashMap<>();
+        Map<String, ClassLoader> moduleToLoader = new HashMap<>();
+
+        boolean isBootLayer = (Layer.boot() == null);
+        Set<ClassLoader> loaders = new HashSet<>();
+
+        // map each module to a class loader
+        for (ResolvedModule resolvedModule : cf.modules()) {
+            String name = resolvedModule.name();
+            ClassLoader loader = clf.apply(name);
+            if (loader != null) {
+                moduleToLoader.put(name, loader);
+                loaders.add(loader);
+            } else if (!isBootLayer) {
+                throw new IllegalArgumentException("loader can't be 'null'");
+            }
+        }
+
+        // define each module in the configuration to the VM
+        for (ResolvedModule resolvedModule : cf.modules()) {
+            ModuleReference mref = resolvedModule.reference();
+            ModuleDescriptor descriptor = mref.descriptor();
+            String name = descriptor.name();
+            URI uri = mref.location().orElse(null);
+            ClassLoader loader = moduleToLoader.get(resolvedModule.name());
+            Module m;
+            if (loader == null && isBootLayer && name.equals("java.base")) {
+                // java.base is already defined to the VM
+                m = Object.class.getModule();
+            } else {
+                m = new Module(layer, loader, descriptor, uri);
+            }
+            nameToModule.put(name, m);
+            moduleToLoader.put(name, loader);
+        }
+
+        // setup readability and exports
+        for (ResolvedModule resolvedModule : cf.modules()) {
+            ModuleReference mref = resolvedModule.reference();
+            ModuleDescriptor descriptor = mref.descriptor();
+
+            String mn = descriptor.name();
+            Module m = nameToModule.get(mn);
+            assert m != null;
+
+            // reads
+            Set<Module> reads = new HashSet<>();
+            for (ResolvedModule d : resolvedModule.reads()) {
+                Module m2;
+                if (d.configuration() == cf) {
+                    String dn = d.reference().descriptor().name();
+                    m2 = nameToModule.get(dn);
+                    assert m2 != null;
+                } else {
+                    m2 = find(d, layer.parent().orElse(null));
+                }
+
+                reads.add(m2);
+
+                // update VM view
+                addReads0(m, m2);
+            }
+            m.reads = reads;
+
+            // automatic modules read all unnamed modules
+            if (descriptor.isAutomatic()) {
+                m.implAddReads(ALL_UNNAMED_MODULE, true);
+            }
+
+            // exports
+            initExports(descriptor, nameToModule, m);
+        }
+
+        // For now, register the modules in the boot layer. This will be
+        // re-examined once the ServiceLoader spec is updated.
+        if (isBootLayer) {
+            for (ResolvedModule resolvedModule : cf.modules()) {
+                ModuleReference mref = resolvedModule.reference();
+                ModuleDescriptor descriptor = mref.descriptor();
+                Map<String, Provides> services = descriptor.provides();
+                if (!services.isEmpty()) {
+                    String name = descriptor.name();
+                    Module m = nameToModule.get(name);
+                    ClassLoader loader = moduleToLoader.get(name);
+                    ServicesCatalog catalog;
+                    if (loader == null) {
+                        catalog = BootLoader.getServicesCatalog();
+                    } else {
+                        catalog = SharedSecrets.getJavaLangAccess()
+                                .createOrGetServicesCatalog(loader);
+                    }
+                    catalog.register(m);
+                }
+            }
+        }
+
+        // ClassLoader::layers support, TBD whether to keep this
+        for (ClassLoader loader : loaders) {
+            SharedSecrets.getJavaLangAccess().bindToLayer(loader, layer);
+        }
+
+        return nameToModule;
+    }
+
+
+    /**
      * Find the runtime Module corresponding to the given ResolvedModule
      * in the given parent Layer (or its parents).
      */
@@ -843,125 +1123,186 @@
     }
 
     /**
-     * Defines each of the module in the given configuration to the runtime.
-     *
-     * @return a map of module name to runtime {@code Module}
-     *
-     * @throws IllegalArgumentException
-     *         If defining any of the modules to the VM fails
+     * Initialize the exports for a module.
      */
-    static Map<String, Module> defineModules(Configuration cf,
-                                             Function<String, ClassLoader> clf,
-                                             Layer layer)
+    private static void initExports(ModuleDescriptor descriptor,
+                                    Map<String, Module> nameToModule,
+                                    Module m)
     {
-        Map<String, Module> modules = new HashMap<>();
-        Map<String, ClassLoader> loaders = new HashMap<>();
-
-        // define each module in the configuration to the VM
-        for (ResolvedModule resolvedModule : cf.modules()) {
-            ModuleReference mref = resolvedModule.reference();
-            ModuleDescriptor descriptor = mref.descriptor();
-            String name = descriptor.name();
-            ClassLoader loader = clf.apply(name);
-            URI uri = mref.location().orElse(null);
-
-            Module m;
-            if (loader == null && name.equals("java.base") && Layer.boot() == null) {
-                m = Object.class.getModule();
-            } else {
-                m = new Module(layer, loader, descriptor, uri);
+        // The VM doesn't know about weak modules so need to export all packages
+        if (descriptor.isWeak()) {
+            assert descriptor.exports().isEmpty();
+            for (String source : descriptor.packages()) {
+                String sourceInternalForm = source.replace('.', '/');
+                addExportsToAll0(m, sourceInternalForm);
             }
-
-            modules.put(name, m);
-            loaders.put(name, loader);
+            return;
         }
 
-        // setup readability and exports
-        for (ResolvedModule resolvedModule : cf.modules()) {
-            ModuleReference mref = resolvedModule.reference();
-            ModuleDescriptor descriptor = mref.descriptor();
+        Map<String, Boolean> unqualifiedExports = new HashMap<>();
+        Map<String, Map<Module, Boolean>> qualifiedExports = new HashMap<>();
 
-            String mn = descriptor.name();
-            Module m = modules.get(mn);
-            assert m != null;
+        for (Exports export : descriptor.exports()) {
+            String source = export.source();
+            String sourceInternalForm = source.replace('.', '/');
 
-            // reads
-            Set<Module> reads = new HashSet<>();
-            for (ResolvedModule d : resolvedModule.reads()) {
-                Module m2;
-                if (d.configuration() == cf) {
-                    String dn = d.reference().descriptor().name();
-                    m2 = modules.get(dn);
-                    assert m2 != null;
-                } else {
-                    m2 = find(d, layer.parent().orElse(null));
+            boolean b = export.modifiers().contains(Exports.Modifier.PRIVATE);
+            Boolean nonPublic = Boolean.valueOf(b);
+
+            if (export.isQualified()) {
+
+                // qualified export
+                Map<Module, Boolean> targets = qualifiedExports.get(source);
+                if (targets == null)
+                    targets = new HashMap<>();
+                for (String target : export.targets()) {
+                    // only export to modules that are in this configuration
+                    Module m2 = nameToModule.get(target);
+                    if (m2 != null) {
+                        addExports0(m, sourceInternalForm, m2);
+                        targets.put(m2, nonPublic);
+                    }
+                }
+                if (!targets.isEmpty()) {
+                    qualifiedExports.putIfAbsent(source, targets);
                 }
 
-                reads.add(m2);
+            } else {
 
-                // update VM view
-                addReads0(m, m2);
-            }
-            m.reads = reads;
-
-            // automatic modules reads all unnamed modules
-            if (descriptor.isAutomatic()) {
-                m.implAddReads(ALL_UNNAMED_MODULE, true);
-            }
-
-            // exports
-            Map<String, Set<Module>> exports = new HashMap<>();
-            for (Exports export : descriptor.exports()) {
-                String source = export.source();
-                String sourceInternalForm = source.replace('.', '/');
-
-                if (export.isQualified()) {
-
-                    // qualified export
-                    Set<Module> targets = new HashSet<>();
-                    for (String target : export.targets()) {
-                        // only export to modules that are in this configuration
-                        Module m2 = modules.get(target);
-                        if (m2 != null) {
-                            targets.add(m2);
-                            addExports0(m, sourceInternalForm, m2);
-                        }
-                    }
-                    if (!targets.isEmpty()) {
-                        exports.put(source, targets);
-                    }
-
-                } else {
-
-                    // unqualified export
-                    exports.put(source, EVERYONE);
-                    addExportsToAll0(m, sourceInternalForm);
-                }
-            }
-            m.exports = exports;
-        }
-
-        // register the modules in the service catalog if they provide services
-        for (ResolvedModule resolvedModule : cf.modules()) {
-            ModuleReference mref = resolvedModule.reference();
-            ModuleDescriptor descriptor = mref.descriptor();
-            Map<String, Provides> services = descriptor.provides();
-            if (!services.isEmpty()) {
-                String name = descriptor.name();
-                Module m = modules.get(name);
-                ClassLoader loader = loaders.get(name);
-                ServicesCatalog catalog;
-                if (loader == null) {
-                    catalog = BootLoader.getServicesCatalog();
-                } else {
-                    catalog = SharedSecrets.getJavaLangAccess()
-                                           .createOrGetServicesCatalog(loader);
-                }
-                catalog.register(m);
+                // unqualified export
+                addExportsToAll0(m, sourceInternalForm);
+                unqualifiedExports.put(source, nonPublic);
             }
         }
 
-        return modules;
+        if (!unqualifiedExports.isEmpty())
+            m.unqualifiedExports = unqualifiedExports;
+        if (!qualifiedExports.isEmpty())
+            m.qualifiedExports = qualifiedExports;
+    }
+
+
+    // -- 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);
+        }
     }
 
 
@@ -969,16 +1310,35 @@
 
 
     /**
-     * Returns an input stream for reading a resource in this module. Returns
-     * {@code null} if the resource is not in this module or access to the
-     * resource is denied by the security manager.
-     * The {@code name} is a {@code '/'}-separated path name that identifies
-     * the resource.
+     * Returns an input stream for reading a resource in this module. The
+     * {@code name} parameter 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.
+     * <p> A resource in a named modules may be <em>encapsulated</em> so that
+     * it cannot be located by code in other modules. Whether a resource can be
+     * located or not is determined as follows:
+     *
+     * <ul>
+     *     <li> The <em>package name</em> of the resource is derived from the
+     *     subsequence of characters that precedes the last {@code '/'} and then
+     *     replacing each {@code '/'} character in the subsequence with
+     *     {@code '.'}. For example, the package name derived for a resource
+     *     named "{@code a/b/c/foo.properties}" is "{@code a.b.c}". </li>
+     *
+     *     <li> If the package name is a package in the module then the package
+     *     must be {@link #isExportedPrivate exported-private} to the module of
+     *     the caller of this method. If the package is not in the module then
+     *     the resource is not encapsulated. Resources in the unnamed package
+     *     or "{@code META-INF}", for example, are never encapsulated because
+     *     they can never be packages in a named module. </li>
+     *
+     *     <li> As a special case, resources ending with "{@code .class}" are
+     *     never encapsulated. </li>
+     * </ul>
+     *
+     * <p> This method returns {@code null} if the resource is not in this
+     * module, the resource is encapsulated and cannot be located by the caller,
+     * or access to the resource is denied by the security manager.
      *
      * @param  name
      *         The resource name
@@ -990,36 +1350,35 @@
      *
      * @see java.lang.module.ModuleReader#open(String)
      */
+    @CallerSensitive
     public InputStream getResourceAsStream(String name) throws IOException {
         Objects.requireNonNull(name);
 
-        URL url = null;
-
-        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);
+        if (isNamed() && !ResourceHelper.isSimpleResource(name)) {
+            Module caller = Reflection.getCallerClass().getModule();
+            if (caller != this && caller != Object.class.getModule()) {
+                // ignore packages added for proxies via addPackage
+                Set<String> packages = getDescriptor().packages();
+                String pn = ResourceHelper.getPackageName(name);
+                if (packages.contains(pn) && !isExported(pn, caller)) {
+                    // resource is in package not exported to caller
+                    return null;
+                }
             }
-
-            // 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);
-            }
-
         }
 
-        // fallthrough to URL case
+        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);
+        }
+
+        // locate resource in module
+        JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
+        URL url = jla.findResource(loader, mn, name);
         if (url != null) {
             try {
                 return url.openStream();
@@ -1098,15 +1457,31 @@
                 }
                 @Override
                 public void addExports(Module m, String pn, Module other) {
-                    m.implAddExports(pn, other, true);
+                    m.implAddExports(pn, other, false, true);
+                }
+                @Override
+                public void addExportsPrivate(Module m, String pn, Module other) {
+                    m.implAddExports(pn, other, true, true);
                 }
                 @Override
                 public void addExportsToAll(Module m, String pn) {
-                    m.implAddExports(pn, Module.EVERYONE_MODULE, true);
+                    m.implAddExports(pn, Module.EVERYONE_MODULE, false, true);
+                }
+                @Override
+                public void addExportsPrivateToAll(Module m, String pn) {
+                    m.implAddExports(pn, Module.EVERYONE_MODULE, true, true);
                 }
                 @Override
                 public void addExportsToAllUnnamed(Module m, String pn) {
-                    m.implAddExports(pn, Module.ALL_UNNAMED_MODULE, true);
+                    m.implAddExports(pn, Module.ALL_UNNAMED_MODULE, false, true);
+                }
+                @Override
+                public void addExportsPrivateToAllUnnamed(Module m, String pn) {
+                    m.implAddExports(pn, Module.ALL_UNNAMED_MODULE, true, true);
+                }
+                @Override
+                public void addUses(Module m, Class<?> service) {
+                    m.implAddUses(service);
                 }
                 @Override
                 public void addPackage(Module m, String pn) {
--- a/src/java.base/share/classes/java/net/URLClassLoader.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/net/URLClassLoader.java	Thu Sep 15 13:14:46 2016 -0700
@@ -110,19 +110,19 @@
         if (security != null) {
             security.checkCreateClassLoader();
         }
-        ucp = new URLClassPath(urls);
+        this.ucp = new URLClassPath(urls);
         this.acc = AccessController.getContext();
     }
 
-    URLClassLoader(URL[] urls, ClassLoader parent,
+    URLClassLoader(String name, URL[] urls, ClassLoader parent,
                    AccessControlContext acc) {
-        super(parent);
+        super(name, parent);
         // this is to make the stack depth consistent with 1.1
         SecurityManager security = System.getSecurityManager();
         if (security != null) {
             security.checkCreateClassLoader();
         }
-        ucp = new URLClassPath(urls);
+        this.ucp = new URLClassPath(urls);
         this.acc = acc;
     }
 
@@ -154,7 +154,7 @@
         if (security != null) {
             security.checkCreateClassLoader();
         }
-        ucp = new URLClassPath(urls);
+        this.ucp = new URLClassPath(urls);
         this.acc = AccessController.getContext();
     }
 
@@ -165,7 +165,7 @@
         if (security != null) {
             security.checkCreateClassLoader();
         }
-        ucp = new URLClassPath(urls);
+        this.ucp = new URLClassPath(urls);
         this.acc = acc;
     }
 
@@ -198,8 +198,70 @@
         if (security != null) {
             security.checkCreateClassLoader();
         }
-        ucp = new URLClassPath(urls, factory);
-        acc = AccessController.getContext();
+        this.ucp = new URLClassPath(urls, factory);
+        this.acc = AccessController.getContext();
+    }
+
+
+    /**
+     * Constructs a new named {@code URLClassLoader} for the specified URLs.
+     * The URLs will be searched in the order specified for classes
+     * and resources after first searching in the specified parent class loader.
+     * Any URL that ends with a '/' is assumed to refer to a directory.
+     * Otherwise, the URL is assumed to refer to a JAR file which will be
+     * downloaded and opened as needed.
+     *
+     * @param name class loader name; can be {@code null}
+     * @param urls the URLs from which to load classes and resources
+     * @param parent the parent class loader for delegation
+     * @throws SecurityException  if a security manager exists and its
+     *         {@link SecurityManager#checkCreateClassLoader()} method doesn't
+     *         allow creation of a class loader.
+     * @throws NullPointerException if {@code urls} is {@code null}.
+     *
+     * @since 9
+     */
+    public URLClassLoader(String name,
+                          URL[] urls,
+                          ClassLoader parent) {
+        super(name, parent);
+        // this is to make the stack depth consistent with 1.1
+        SecurityManager security = System.getSecurityManager();
+        if (security != null) {
+            security.checkCreateClassLoader();
+        }
+        this.ucp = new URLClassPath(urls);
+        this.acc = AccessController.getContext();
+    }
+
+    /**
+     * Constructs a new named {@code URLClassLoader} for the specified URLs,
+     * parent class loader, and URLStreamHandlerFactory.
+     * The parent argument will be used as the parent class loader for delegation.
+     * The factory argument will be used as the stream handler factory to
+     * obtain protocol handlers when creating new jar URLs.
+     *
+     * @param name class loader name; can be {@code null}
+     * @param urls the URLs from which to load classes and resources
+     * @param parent the parent class loader for delegation
+     * @param factory the URLStreamHandlerFactory to use when creating URLs
+     *
+     * @throws  SecurityException  if a security manager exists and its
+     *          {@code checkCreateClassLoader} method doesn't allow
+     *          creation of a class loader.
+     * @throws  NullPointerException if {@code urls} is {@code null}.
+     * @since 9
+     */
+    public URLClassLoader(String name, URL[] urls, ClassLoader parent,
+                          URLStreamHandlerFactory factory) {
+        super(name, parent);
+        // this is to make the stack depth consistent with 1.1
+        SecurityManager security = System.getSecurityManager();
+        if (security != null) {
+            security.checkCreateClassLoader();
+        }
+        this.ucp = new URLClassPath(urls, factory);
+        this.acc = AccessController.getContext();
     }
 
     /* A map (used as a set) to keep track of closeable local resources
@@ -735,7 +797,7 @@
         URLClassLoader ucl = AccessController.doPrivileged(
             new PrivilegedAction<>() {
                 public URLClassLoader run() {
-                    return new FactoryURLClassLoader(urls, parent, acc);
+                    return new FactoryURLClassLoader(null, urls, parent, acc);
                 }
             });
         return ucl;
@@ -784,9 +846,9 @@
         ClassLoader.registerAsParallelCapable();
     }
 
-    FactoryURLClassLoader(URL[] urls, ClassLoader parent,
+    FactoryURLClassLoader(String name, URL[] urls, ClassLoader parent,
                           AccessControlContext acc) {
-        super(urls, parent, acc);
+        super(name, urls, parent, acc);
     }
 
     FactoryURLClassLoader(URL[] urls, AccessControlContext acc) {
--- a/src/java.base/share/classes/java/security/SecureClassLoader.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/security/SecureClassLoader.java	Thu Sep 15 13:14:46 2016 -0700
@@ -25,8 +25,6 @@
 
 package java.security;
 
-import java.net.URL;
-import java.util.ArrayList;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
@@ -114,6 +112,29 @@
     }
 
     /**
+     * Creates a new {@code SecureClassLoader} of the specified name and
+     * using the specified parent class loader for delegation.
+     *
+     * @param name class loader name; can be {@code null}.
+     * @param parent the parent class loader
+     *
+     * @throws SecurityException  if a security manager exists and its
+     *         {@link SecurityManager#checkCreateClassLoader()} method
+     *         doesn't allow creation of a class loader.
+     *
+     * @since 9
+     */
+    protected SecureClassLoader(String name, ClassLoader parent) {
+        super(name, parent);
+        // this is to make the stack depth consistent with 1.1
+        SecurityManager security = System.getSecurityManager();
+        if (security != null) {
+            security.checkCreateClassLoader();
+        }
+        initialized = true;
+    }
+
+    /**
      * Converts an array of bytes into an instance of class Class,
      * with an optional CodeSource. Before the
      * class can be used it must be resolved.
--- a/src/java.base/share/classes/java/util/ResourceBundle.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/util/ResourceBundle.java	Thu Sep 15 13:14:46 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/java/util/ServiceLoader.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/java/util/ServiceLoader.java	Thu Sep 15 13:14:46 2016 -0700
@@ -32,23 +32,28 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Layer;
+import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Module;
 import java.net.URL;
 import java.net.URLConnection;
+import java.security.AccessControlContext;
 import java.security.AccessController;
-import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import jdk.internal.loader.BootLoader;
-import jdk.internal.loader.Loader;
-import jdk.internal.loader.LoaderPool;
 import jdk.internal.misc.JavaLangAccess;
+import jdk.internal.misc.JavaLangReflectModuleAccess;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.misc.VM;
 import jdk.internal.module.ServicesCatalog;
 import jdk.internal.module.ServicesCatalog.ServiceProvider;
-
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;
 
@@ -76,8 +81,19 @@
  * request together with code that can create the actual provider on demand.
  * The details of provider classes tend to be highly service-specific; no
  * single class or interface could possibly unify them, so no such type is
- * defined here. A requirement enforced by this facility is that each provider
- * class must have a {@code public} zero-argument constructor.
+ * defined here.
+ *
+ * <p> A requirement enforced by this facility is that providers deployed as
+ * explicit modules on the module path must define a static factory method
+ * to obtain the provider instance or a public zero-argument constructor.
+ * Providers deployed on the class path or as {@link
+ * java.lang.module.ModuleDescriptor#isAutomatic automatic-modules} on the
+ * module path must have a public zero-argument constructor. If an
+ * explicit module defines a static factory method then the method is a
+ * public static zero-argument method named "{@code provider}" with a return
+ * type that is assignable to the service type. If an explicit module on
+ * the module path defines both a static factory method and a public zero
+ * argument constructor then the static factory method is preferred.
  *
  * <p> An application or library using this loading facility and developed
  * and deployed as a named module must have an appropriate <i>uses</i> clause
@@ -87,10 +103,7 @@
  * <i>provides</i> clause in its module descriptor to declare that the module
  * provides an implementation of the service. The <i>uses</i> and
  * <i>provides</i> allow consumers of a service to be <i>linked</i> to
- * providers of the service. In the case of {@code load} methods that locate
- * service providers using a class loader, then provider modules defined to
- * that class loader, or a class loader <i>reachable</i> using {@link
- * ClassLoader#getParent() parent} delegation, will be located.
+ * providers of the service.
  *
  * <p> A service provider that is packaged as a JAR file for the class path is
  * identified by placing a <i>provider-configuration file</i> in the resource
@@ -114,27 +127,106 @@
  *
  * <p> Providers are located and instantiated lazily, that is, on demand.  A
  * service loader maintains a cache of the providers that have been loaded so
- * far.  Each invocation of the {@link #iterator iterator} method returns an
- * iterator that first yields all of the elements of the cache, in
- * instantiation order, and then lazily locates and instantiates any remaining
- * providers, adding each one to the cache in turn.  The cache can be cleared
+ * far. Each invocation of the {@link #iterator iterator} method returns an
+ * iterator that first yields all of the elements cached from previous
+ * iteration, in instantiation order, and then lazily locates and instantiates
+ * any remaining providers, adding each one to the cache in turn.  Similarly,
+ * each invocation of the {@link #stream stream} method returns a stream that
+ * first processes all providers loaded by previous stream operations, in load
+ * order, and then lazily locates any remaining providers. Caches are cleared
  * via the {@link #reload reload} method.
  *
+ * <h2> Locating providers </h2>
+ *
+ * <p> The {@code load} methods locate providers using a class loader or module
+ * {@link Layer layer}. When locating providers using a class loader then
+ * providers in both named and unnamed modules may be located. When locating
+ * providers using a module layer then only providers in named modules in
+ * the layer (or parent layers) are located.
+ *
+ * <p> When locating providers using a class loader then any providers in named
+ * modules defined to the class loader, or any class loader that is reachable
+ * via parent delegation, are located. Additionally, providers in module layers
+ * other than the {@link Layer#boot() boot} layer, where the module layer
+ * contains modules defined to the class loader, or any class loader reachable
+ * via parent delegation, are also located. For example, suppose there is a
+ * module layer where each module is defined to its own class loader (see {@link
+ * Layer#defineModulesWithManyLoaders defineModulesWithManyLoaders}). If the
+ * {@code load} method is invoked to locate providers using any of these class
+ * loaders for this layer then then it will locate all of the providers in that
+ * layer, irrespective of their defining class loader.
+ *
+ * <p> In the case of unnamed modules then the service configuration files are
+ * located using the class loader's {@link ClassLoader#getResources(String)
+ * ClassLoader.getResources(String)} method. Any providers listed should be
+ * visible via the class loader specified to the {@code load} method. If a
+ * provider in a named module is listed then it is ignored - this is to avoid
+ * duplicates that would otherwise arise when a module has both a
+ * <i>provides</i> clause and a service configuration file in {@code
+ * META-INF/services} that lists the same provider.
+ *
+ * <h2> Ordering </h2>
+ *
+ * <p> Service loaders created to locate providers using a {@code ClassLoader}
+ * locate providers as follows:
+ * <ul>
+ *     <li> Providers in named modules are located before providers on the
+ *     class path (or more generally, unnamed modules). </li>
+ *
+ *     <li> When locating providers in named modules then the service loader
+ *     will locate providers in modules defined to the class loader, then its
+ *     parent class loader, its parent parent, and so on to the bootstrap class
+ *     loader. If a {@code ClassLoader}, or any class loader in the parent
+ *     delegation chain, defines modules in a custom module {@link Layer} then
+ *     all providers in that layer are located, irrespective of their class
+ *     loader. The ordering of modules defined to the same class loader, or the
+ *     ordering of modules in a layer, is not defined. </li>
+ *
+ *     <li> When locating providers in unnamed modules then the ordering is
+ *     based on the order that the class loader's {@link
+ *     ClassLoader#getResources(String) ClassLoader.getResources(String)}
+ *     method finds the service configuration files. </li>
+ * </ul>
+ *
+ * <p> Service loaders created to locate providers in a module {@link Layer}
+ * will first locate providers in the layer, then its parent layer, then its
+ * parent, and so on to the boot layer. The ordering of modules in a layer is
+ * not defined.
+ *
+ * <h2> Selection and filtering </h2>
+ *
+ * <p> Selecting a provider or filtering providers will usually involve invoking
+ * a provider method. Where selection or filtering based on the provider class is
+ * needed then it can be done using a {@link #stream() stream}. For example, the
+ * following locates the providers that have a specific annotation:
+ * <pre>{@code
+ *     Stream<CodecSet> providers = ServiceLoader.load(CodecSet.class)
+ *            .stream()
+ *            .filter(p -> p.type().isAnnotationPresent(Managed.class))
+ *            .map(Provider::get);
+ * }</pre>
+ *
+ * <h2> Security </h2>
+ *
  * <p> Service loaders always execute in the security context of the caller
- * of the iterator methods and may also be restricted by the security
+ * of the iterator or stream methods and may also be restricted by the security
  * context of the caller that created the service loader.
  * Trusted system code should typically invoke the methods in this class, and
  * the methods of the iterators which they return, from within a privileged
  * security context.
  *
+ * <h2> Concurrency </h2>
+ *
  * <p> Instances of this class are not safe for use by multiple concurrent
  * threads.
  *
- * <p> Unless otherwise specified, passing a <tt>null</tt> argument to any
+ * <h2> Null handling </h2>
+ *
+ * <p> Unless otherwise specified, passing a {@code null} argument to any
  * method in this class will cause a {@link NullPointerException} to be thrown.
  *
- * <p><span style="font-weight: bold; padding-right: 1em">Example</span>
- * Suppose we have a service type <tt>com.example.CodecSet</tt> which is
+ * <h2> Example </h2>
+ * <p> Suppose we have a service type <tt>com.example.CodecSet</tt> which is
  * intended to represent sets of encoder/decoder pairs for some protocol.  In
  * this case it is an abstract class with two abstract methods:
  *
@@ -218,11 +310,12 @@
 public final class ServiceLoader<S>
     implements Iterable<S>
 {
-    private static final String PREFIX = "META-INF/services/";
-
     // The class or interface representing the service being loaded
     private final Class<S> service;
 
+    // The class of the service type
+    private final String serviceName;
+
     // The module Layer used to locate providers; null when locating
     // providers using a class loader
     private final Layer layer;
@@ -234,52 +327,59 @@
     // The access control context taken when the ServiceLoader is created
     private final AccessControlContext acc;
 
-    // Cached providers, in instantiation order
-    private List<S> providers = new ArrayList<>();
+    // The lazy-lookup iterator for iterator operations
+    private Iterator<Provider<S>> lookupIterator1;
+    private final List<S> instantiatedProviders = new ArrayList<>();
 
-    // The class names of the cached providers, only used when locating
-    // service providers via a class loader
-    private Set<String> providerNames = new HashSet<>();
+    // The lazy-lookup iterator for stream operations
+    private Iterator<Provider<S>> lookupIterator2;
+    private final List<Provider<S>> loadedProviders = new ArrayList<>();
+    private boolean loadedAllProviders; // true when all providers loaded
 
     // Incremented when reload is called
     private int reloadCount;
 
-    // the service iterator when locating services via a module layer
-    private LayerLookupIterator layerLookupIterator;
-
-    // The module services iterator when locating services in modules
-    // defined to a class loader
-    private ModuleServicesIterator moduleServicesIterator;
-
-    // The current lazy-lookup iterator for locating legacy provider on the
-    // class path via a class loader
-    private LazyClassPathIterator lazyClassPathIterator;
+    private static JavaLangAccess LANG_ACCESS;
+    private static JavaLangReflectModuleAccess JLRM_ACCESS;
+    static {
+        LANG_ACCESS = SharedSecrets.getJavaLangAccess();
+        JLRM_ACCESS = SharedSecrets.getJavaLangReflectModuleAccess();
+    }
 
 
     /**
-     * Clear this loader's provider cache so that all providers will be
-     * reloaded.
+     * Represents a service provider located by {@code ServiceLoader}.
      *
-     * <p> After invoking this method, subsequent invocations of the {@link
-     * #iterator() iterator} method will lazily look up and instantiate
-     * providers from scratch, just as is done by a newly-created loader.
+     * <p> When using a loader's {@link ServiceLoader#stream() stream()} method
+     * then the elements are of type {@code Provider}. This allows processing
+     * to select or filter on the provider class without instantiating the
+     * provider. </p>
      *
-     * <p> This method is intended for use in situations in which new providers
-     * can be installed into a running Java virtual machine.
+     * @param  <S> The service type
+     * @since 9
      */
-    public void reload() {
-        providers.clear();
+    public static interface Provider<S> extends Supplier<S> {
+        /**
+         * Returns the provider class. There is no guarantee that this type is
+         * accessible and so attempting to instantiate it, by means of its
+         * {@link Class#newInstance() newInstance()} method for example, will
+         * fail when it is not accessible. The {@link #get() get()} method
+         * should instead be used to obtain the provider.
+         *
+         * @return The provider class
+         */
+        Class<S> type();
 
-        assert layer == null || loader == null;
-        if (layer != null) {
-            layerLookupIterator = new LayerLookupIterator();
-        } else {
-            providerNames.clear();
-            moduleServicesIterator = new ModuleServicesIterator();
-            lazyClassPathIterator = new LazyClassPathIterator();
-        }
-
-        reloadCount++;
+        /**
+         * Returns an instance of the provider.
+         *
+         * @return An instance of the provider.
+         *
+         * @throws ServiceConfigurationError
+         *         If the service provider cannot be instantiated. The error
+         *         cause will carry an appropriate cause.
+         */
+        @Override S get();
     }
 
 
@@ -292,17 +392,19 @@
      *         caller's module does not declare that it uses the service type.
      */
     private ServiceLoader(Class<?> caller, Layer layer, Class<S> svc) {
+        Objects.requireNonNull(caller);
+        Objects.requireNonNull(layer);
+        Objects.requireNonNull(svc);
 
         checkModule(caller.getModule(), svc);
 
         this.service = svc;
+        this.serviceName = svc.getName();
         this.layer = layer;
         this.loader = null;
         this.acc = (System.getSecurityManager() != null)
                 ? AccessController.getContext()
                 : null;
-
-        reload();
     }
 
     /**
@@ -314,14 +416,13 @@
      *         caller's module does not declare that it uses the service type.
      */
     private ServiceLoader(Module callerModule, Class<S> svc, ClassLoader cl) {
+        Objects.requireNonNull(svc);
+
         if (VM.isBooted()) {
-
             checkModule(callerModule, svc);
-
             if (cl == null) {
                 cl = ClassLoader.getSystemClassLoader();
             }
-
         } else {
 
             // if we get here then it means that ServiceLoader is being used
@@ -338,21 +439,27 @@
         }
 
         this.service = svc;
+        this.serviceName = svc.getName();
         this.layer = null;
         this.loader = cl;
         this.acc = (System.getSecurityManager() != null)
                 ? AccessController.getContext()
                 : null;
-
-        reload();
     }
 
+    /**
+     * Initializes a new instance of this class for locating service providers
+     * via a class loader.
+     *
+     * @throws ServiceConfigurationError
+     *         If {@code svc} is not accessible to {@code caller} or that the
+     *         caller's module does not declare that it uses the service type.
+     */
     private ServiceLoader(Class<?> caller, Class<S> svc, ClassLoader cl) {
         this(caller.getModule(), svc, cl);
     }
 
 
-
     /**
      * Checks that the given service type is accessible to types in the given
      * module, and check that the module declare that it uses the service type.
@@ -392,189 +499,285 @@
         fail(service, u + ":" + line + ": " + msg);
     }
 
-    // Parse a single line from the given configuration file, adding the name
-    // on the line to the names list.
-    //
-    private int parseLine(Class<?> service, URL u, BufferedReader r, int lc,
-                          List<String> names)
-        throws IOException, ServiceConfigurationError
-    {
-        String ln = r.readLine();
-        if (ln == null) {
-            return -1;
+    /**
+     * Uses Class.forName to load a provider in a module.
+     *
+     * @throws ServiceConfigurationError
+     *         If the class cannot be loaded or is not a subtype of service
+     */
+    private Class<S> loadProviderInModule(Module module, String cn) {
+        Class<?> clazz = null;
+        if (acc == null) {
+            try {
+                clazz = Class.forName(module, cn);
+            } catch (LinkageError e) {
+                fail(service, "Unable to load " + cn, e);
+            }
+        } else {
+            PrivilegedExceptionAction<Class<?>> pa = () -> Class.forName(module, cn);
+            try {
+                clazz = AccessController.doPrivileged(pa);
+            } catch (PrivilegedActionException pae) {
+                Throwable x = pae.getCause();
+                fail(service, "Unable to load " + cn, x);
+                return null;
+            }
         }
-        int ci = ln.indexOf('#');
-        if (ci >= 0) ln = ln.substring(0, ci);
-        ln = ln.trim();
-        int n = ln.length();
-        if (n != 0) {
-            if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
-                fail(service, u, lc, "Illegal configuration-file syntax");
-            int cp = ln.codePointAt(0);
-            if (!Character.isJavaIdentifierStart(cp))
-                fail(service, u, lc, "Illegal provider-class name: " + ln);
-            for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
-                cp = ln.codePointAt(i);
-                if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
-                    fail(service, u, lc, "Illegal provider-class name: " + ln);
-            }
-            if (!providerNames.contains(ln) && !names.contains(ln))
-                names.add(ln);
-        }
-        return lc + 1;
+        if (clazz == null)
+            fail(service, "Provider " + cn  + " not found");
+        if (!service.isAssignableFrom(clazz))
+            fail(service, "Provider " + cn  + " not a subtype");
+        @SuppressWarnings("unchecked")
+        Class<S> result = (Class<S>) clazz;
+        return result;
     }
 
     /**
-     * Parse the content of the given URL as a provider-configuration file.
-     *
-     * @param  service
-     *         The service type for which providers are being sought;
-     *         used to construct error detail strings
-     *
-     * @param  u
-     *         The URL naming the configuration file to be parsed
-     *
-     * @return A (possibly empty) iterator that will yield the provider-class
-     *         names in the given configuration file that are not yet members
-     *         of the returned set
-     *
-     * @throws ServiceConfigurationError
-     *         If an I/O error occurs while reading from the given URL, or
-     *         if a configuration-file format error is detected
-     *
+     * A Provider implementation that supports invoking, with reduced
+     * permissions, the provider's static factory method or its no-arg
+     * constructor.
      */
-    private Iterator<String> parse(Class<?> service, URL u)
-        throws ServiceConfigurationError
-    {
-        ArrayList<String> names = new ArrayList<>();
-        try {
-            URLConnection uc = u.openConnection();
-            uc.setUseCaches(false);
-            try (InputStream in = uc.getInputStream();
-                 BufferedReader r
-                     = new BufferedReader(new InputStreamReader(in, "utf-8")))
-            {
-                int lc = 1;
-                while ((lc = parseLine(service, u, r, lc, names)) >= 0);
+    private final static class ProviderImpl<S> implements Provider<S> {
+        final Class<?> service;
+        final Class<S> type;
+        final AccessControlContext acc;
+
+        Method factoryMethod; // cached
+        Constructor<S> ctor;
+
+        /**
+         * @throws ServiceConfigurationError
+         *         If the provider class is not public
+         */
+        ProviderImpl(Class<?> service, Class<S> type, AccessControlContext acc) {
+            int mods = type.getModifiers();
+            if (!Modifier.isPublic(mods)) {
+                fail(service, "Provider " + type + " is not public");
             }
-        } catch (IOException x) {
-            fail(service, "Error accessing configuration file", x);
-        }
-        return names.iterator();
-    }
-
-    /**
-     * Returns the {@code Constructor} to instantiate the service provider.
-     * The constructor has its accessible flag set so that the access check
-     * is suppressed when instantiating the provider. This is necessary
-     * because newInstance is a caller sensitive method and ServiceLoader
-     * is instantiating the service provider on behalf of the service
-     * consumer.
-     */
-    private static Constructor<?> checkAndGetConstructor(Class<?> c)
-        throws NoSuchMethodException, IllegalAccessException
-    {
-        Constructor<?> ctor = c.getConstructor();
-
-        // check class and no-arg constructor are public
-        int modifiers = ctor.getModifiers();
-        if (!Modifier.isPublic(Reflection.getClassAccessFlags(c) & modifiers)) {
-            String cn = c.getName();
-            throw new IllegalAccessException(cn + " is not public");
+            this.service = service;
+            this.type = type;
+            this.acc = acc;
         }
 
-        // return Constructor to create the service implementation
-        PrivilegedAction<Void> action = new PrivilegedAction<Void>() {
-            public Void run() { ctor.setAccessible(true); return null; }
-        };
-        AccessController.doPrivileged(action);
-        return ctor;
-    }
+        @Override
+        public Class<S> type() {
+            return type;
+        }
 
-    /**
-     * Uses Class.forName to load a class in a module.
-     */
-    private static Class<?> loadClassInModule(Module module, String cn) {
-        SecurityManager sm = System.getSecurityManager();
-        if (sm == null) {
-            return Class.forName(module, cn);
-        } else {
-            PrivilegedAction<Class<?>> pa = () -> Class.forName(module, cn);
-            return AccessController.doPrivileged(pa);
-        }
-    }
-
-    /**
-     * An Iterator that runs the next and hasNext methods with permissions
-     * restricted by the {@code AccessControlContext} obtained when the
-     * ServiceLoader was created.
-     */
-    private abstract class RestrictedIterator<S>
-        implements Iterator<S>
-    {
-        /**
-         * Returns {@code true} if the iteration has more elements.
-         */
-        abstract boolean hasNextService();
-
-        /**
-         * Returns the next element in the iteration
-         */
-        abstract S nextService();
-
-        public final boolean hasNext() {
-            if (acc == null) {
-                return hasNextService();
+        @Override
+        public S get() {
+            if (factoryMethod == null && ctor == null) {
+                if (isExplicitModule()) {
+                    // look for the static "provider" method when the provider
+                    // is an explicit named module
+                    factoryMethod = getFactoryMethod();
+                }
+                if (factoryMethod == null) {
+                    ctor = getConstructor();
+                }
+            }
+            if (factoryMethod != null) {
+                return invokeFactoryMethod();
             } else {
-                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
-                    public Boolean run() { return hasNextService(); }
-                };
-                return AccessController.doPrivileged(action, acc);
+                return newInstance();
             }
         }
 
-        public final S next() {
+        /**
+         * Returns {@code true} if the provider is in an explicit module
+         */
+        private boolean isExplicitModule() {
+            Module module = type.getModule();
+            return module.isNamed() && !module.getDescriptor().isAutomatic();
+        }
+
+        /**
+         * Returns the method for the provider's "{@code public S provider()}"
+         * method or {@code null} if the method does not exist.
+         *
+         * @throws ServiceConfigurationError if the provider defines the
+         *         method but it's not static or public
+         */
+        private Method getFactoryMethod() {
+            PrivilegedAction<Method> pa = new PrivilegedAction<>() {
+                @Override
+                public Method run() {
+                    try {
+                        // TBD: invoke getMethod0 directly to avoid exception
+                        Method m = type.getMethod("provider");
+                        m.setAccessible(true);
+                        return m;
+                    } catch (NoSuchMethodException e) {
+                        return null;
+                    }
+                }
+            };
+            Method m = AccessController.doPrivileged(pa);
+            if (m != null) {
+                int mods = m.getModifiers();
+                if (!Modifier.isStatic(mods)) {
+                    fail(service, m + " is not a static method");
+                }
+                Class<?> returnType = m.getReturnType();
+                if (!service.isAssignableFrom(returnType)) {
+                    fail(service, m + " returns " + returnType);
+                }
+            }
+            return m;
+        }
+
+        /**
+         * Returns the provider's public no-arg constructor.
+         *
+         * @throws ServiceConfigurationError if the provider does not have
+         *         public no-arg constructor
+         */
+        private Constructor<S> getConstructor() {
+            PrivilegedAction<Constructor<S>> pa = new PrivilegedAction<>() {
+                @Override
+                public Constructor<S> run() {
+                    Constructor<S> ctor = null;
+                    try {
+                        ctor = type.getConstructor();
+                    } catch (NoSuchMethodException e) { }
+                    if (ctor != null && isExplicitModule())
+                        ctor.setAccessible(true);
+                    return ctor;
+                }
+            };
+            Constructor<S> ctor = AccessController.doPrivileged(pa);
+            if (ctor == null) {
+                fail(service, "Provider " + type
+                     + " does not have a public no-arg constructor");
+            }
+            return ctor;
+        }
+
+        /**
+         * Invokes the provider's "provider" method to instantiate a provider.
+         * When running with a security manager then the method runs with
+         * permissions that are restricted by the security context of whatever
+         * created this loader.
+         */
+        @SuppressWarnings("unchecked")
+        private S invokeFactoryMethod() {
+            S p = null;
+            Throwable exc = null;
             if (acc == null) {
-                return nextService();
+                try {
+                    p = (S) factoryMethod.invoke(null);
+                } catch (Throwable x) {
+                    exc = x;
+                }
             } else {
-                PrivilegedAction<S> action = new PrivilegedAction<S>() {
-                    public S run() { return nextService(); }
+                PrivilegedExceptionAction<S> pa = new PrivilegedExceptionAction<>() {
+                    @Override
+                    public S run() throws Exception {
+                        return (S) factoryMethod.invoke(null);
+                    }
                 };
-                return AccessController.doPrivileged(action, acc);
+                // invoke factory method with permissions restricted by acc
+                try {
+                    p = AccessController.doPrivileged(pa, acc);
+                } catch (PrivilegedActionException pae) {
+                    exc = pae.getCause();
+                }
             }
+            if (exc != null) {
+                if (exc instanceof InvocationTargetException)
+                    exc = exc.getCause();
+                String cn = type.getName();
+                fail(service, "Provider " + cn + " could not be obtained", exc);
+            }
+            if (p == null) {
+                fail(service, factoryMethod + " returned null");
+            }
+            return p;
+        }
+
+        /**
+         * Invokes Constructor::newInstance to instantiate a provider. When running
+         * with a security manager then the constructor runs with permissions that
+         * are restricted by the security context of whatever created this loader.
+         */
+        private S newInstance() {
+            S p = null;
+            Throwable exc = null;
+            if (acc == null) {
+                try {
+                    p = ctor.newInstance();
+                } catch (Throwable x) {
+                    exc = x;
+                }
+            } else {
+                PrivilegedExceptionAction<S> pa = new PrivilegedExceptionAction<>() {
+                    @Override
+                    public S run() throws Exception {
+                        return ctor.newInstance();
+                    }
+                };
+                // invoke constructor with permissions restricted by acc
+                try {
+                    p = AccessController.doPrivileged(pa, acc);
+                } catch (PrivilegedActionException pae) {
+                    exc = pae.getCause();
+                }
+            }
+            if (exc != null) {
+                if (exc instanceof InvocationTargetException)
+                    exc = exc.getCause();
+                String cn = type.getName();
+                fail(service,
+                     "Provider " + cn + " could not be instantiated", exc);
+            }
+            return p;
+        }
+
+        // For now, equals/hashCode uses the access control context to ensure
+        // that two Providers created with different contexts are not equal
+        // when running with a security manager.
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type, acc);
+        }
+
+        @Override
+        public boolean equals(Object ob) {
+            if (!(ob instanceof ProviderImpl))
+                return false;
+            @SuppressWarnings("unchecked")
+            ProviderImpl<?> that = (ProviderImpl<?>)ob;
+            return this.type == that.type
+                    && Objects.equals(this.acc, that.acc);
         }
     }
 
     /**
      * Implements lazy service provider lookup of service providers that
-     * are provided by modules in a module Layer.
+     * are provided by modules in a module Layer (or parent layers)
      */
-    private class LayerLookupIterator
-        extends RestrictedIterator<S>
+    private final class LayerLookupIterator<T>
+        implements Iterator<Provider<T>>
     {
-        final String serviceName;
         Layer currentLayer;
         Iterator<ServiceProvider> iterator;
         ServiceProvider nextProvider;
 
         LayerLookupIterator() {
-            serviceName = service.getName();
             currentLayer = layer;
 
             // need to get us started
-            iterator = providers(currentLayer, serviceName);
+            iterator = providers(currentLayer);
         }
 
-        Iterator<ServiceProvider> providers(Layer layer, String service) {
-            ServicesCatalog catalog = SharedSecrets
-                    .getJavaLangReflectModuleAccess()
-                    .getServicesCatalog(layer);
-
+        private Iterator<ServiceProvider> providers(Layer layer) {
+            ServicesCatalog catalog = JLRM_ACCESS.getServicesCatalog(layer);
             return catalog.findServices(serviceName).iterator();
         }
 
         @Override
-        boolean hasNextService() {
+        public boolean hasNext() {
 
             // already have the next provider cached
             if (nextProvider != null)
@@ -594,101 +797,93 @@
                     return false;
 
                 currentLayer = parent;
-                iterator = providers(currentLayer, serviceName);
+                iterator = providers(currentLayer);
             }
         }
 
         @Override
-        S nextService() {
-            if (!hasNextService())
+        public Provider<T> next() {
+            if (!hasNext())
                 throw new NoSuchElementException();
 
             ServiceProvider provider = nextProvider;
             nextProvider = null;
 
+            // attempt to load the provider
             Module module = provider.module();
             String cn = provider.providerName();
+            Class<?> c = loadProviderInModule(module, cn);
 
-            // attempt to load the provider
-            Class<?> c = loadClassInModule(module, cn);
-            if (c == null)
-                fail(service, "Provider " + cn  + " not found");
-            if (!service.isAssignableFrom(c))
-                fail(service, "Provider " + cn  + " not a subtype");
-
-            // instantiate the provider
-            S p = null;
-            try {
-                Constructor<?> ctor = checkAndGetConstructor(c);
-                p = service.cast(ctor.newInstance());
-            } catch (Throwable x) {
-                if (x instanceof InvocationTargetException)
-                    x = x.getCause();
-                fail(service,
-                        "Provider " + cn + " could not be instantiated", x);
-            }
-
-            // add to cached provider list
-            providers.add(p);
-
-            return p;
+            @SuppressWarnings("unchecked")
+            Class<T> clazz = (Class<T>) c;
+            return new ProviderImpl<T>(service, clazz, acc);
         }
     }
 
     /**
      * Implements lazy service provider lookup of service providers that
-     * are provided by modules defined to a class loader.
+     * are provided by modules defined to a class loader or to modules in
+     * layers with a module defined to the class loader.
      */
-    private class ModuleServicesIterator
-        extends RestrictedIterator<S>
+    private final class ModuleServicesLookupIterator<T>
+        implements Iterator<Provider<T>>
     {
-        final JavaLangAccess langAccess = SharedSecrets.getJavaLangAccess();
-
         ClassLoader currentLoader;
         Iterator<ServiceProvider> iterator;
         ServiceProvider nextProvider;
 
-        ModuleServicesIterator() {
+        ModuleServicesLookupIterator() {
             this.currentLoader = loader;
             this.iterator = iteratorFor(loader);
         }
 
         /**
+         * Returns iterator to iterate over the implementations of {@code
+         * service} in the given layer.
+         */
+        private Set<ServiceProvider> providers(Layer layer) {
+            ServicesCatalog catalog = JLRM_ACCESS.getServicesCatalog(layer);
+            return catalog.findServices(serviceName);
+        }
+
+        /**
          * Returns an iterator to iterate over the implementations of {@code
-         * service} in modules defined to the given class loader.
+         * service} in modules defined to the given class loader or in custom
+         * layers with a module defined to this class loader.
          */
         private Iterator<ServiceProvider> iteratorFor(ClassLoader loader) {
 
-            // if the class loader is in a loader pool then return an Iterator
-            // that iterates over all service providers in the pool that provide
-            // an implementation of the service
-            if (currentLoader instanceof Loader) {
-                LoaderPool pool = ((Loader) loader).pool();
-                if (pool != null) {
-                    return pool.loaders()
-                            .map(l -> langAccess.getServicesCatalog(l))
-                            .filter(sc -> sc != null)
-                            .map(sc -> sc.findServices(service.getName()))
-                            .flatMap(Set::stream)
-                            .iterator();
-                }
+            // modules defined to this class loader
+            ServicesCatalog catalog;
+            if (loader == null) {
+                catalog = BootLoader.getServicesCatalog();
+            } else {
+                catalog = LANG_ACCESS.getServicesCatalog(loader);
+            }
+            Stream<ServiceProvider> stream1;
+            if (catalog == null) {
+                stream1 = Stream.empty();
+            } else {
+                stream1 = catalog.findServices(serviceName).stream();
             }
 
-            ServicesCatalog catalog;
-            if (currentLoader == null) {
-                catalog = BootLoader.getServicesCatalog();
+            // modules in custom layers that define modules to the class loader
+            Stream<ServiceProvider> stream2;
+            if (loader == null) {
+                stream2 = Stream.empty();
             } else {
-                catalog = langAccess.getServicesCatalog(currentLoader);
+                Layer bootLayer = Layer.boot();
+                stream2 = loader.layers()
+                        .filter(l -> (l != bootLayer))
+                        .map(l -> providers(l))
+                        .flatMap(Set::stream);
             }
-            if (catalog == null) {
-                return Collections.emptyIterator();
-            } else {
-                return catalog.findServices(service.getName()).iterator();
-            }
+
+            return Stream.concat(stream1, stream2).iterator();
         }
 
         @Override
-        boolean hasNextService() {
+        public boolean hasNext() {
             // already have the next provider cached
             if (nextProvider != null)
                 return true;
@@ -710,8 +905,8 @@
         }
 
         @Override
-        S nextService() {
-            if (!hasNextService())
+        public Provider<T> next() {
+            if (!hasNext())
                 throw new NoSuchElementException();
 
             ServiceProvider provider = nextProvider;
@@ -720,128 +915,195 @@
             // attempt to load the provider
             Module module = provider.module();
             String cn = provider.providerName();
+            Class<?> c = loadProviderInModule(module, cn);
 
-            Class<?> c = loadClassInModule(module, cn);
-            if (c == null) {
-                fail(service,
-                    "Provider " + cn + " not found in " + module.getName());
-            }
-            if (!service.isAssignableFrom(c)) {
-                fail(service, "Provider " + cn  + " not a subtype");
-            }
-
-            // instantiate the provider
-            S p = null;
-            try {
-                Constructor<?> ctor = checkAndGetConstructor(c);
-                p = service.cast(ctor.newInstance());
-            } catch (Throwable x) {
-                if (x instanceof InvocationTargetException)
-                    x = x.getCause();
-                fail(service,
-                    "Provider " + cn + " could not be instantiated", x);
-            }
-
-            // add to provider list
-            providers.add(p);
-
-            // record the class name of the service provider, this is
-            // needed for cases where there a module has both a "uses"
-            // and a services configuration file listing the same
-            // provider
-            providerNames.add(cn);
-
-            return p;
+            @SuppressWarnings("unchecked")
+            Class<T> clazz = (Class<T>) c;
+            return new ProviderImpl<T>(service, clazz, acc);
         }
     }
 
     /**
-     * Implements lazy service provider lookup where the service providers
-     * are configured via service configuration files.
+     * Implements lazy service provider lookup where the service providers are
+     * configured via service configuration files. Service providers in named
+     * modules are silently ignored by this lookup iterator.
      */
-    private class LazyClassPathIterator
-        extends RestrictedIterator<S>
+    private final class LazyClassPathLookupIterator<T>
+        implements Iterator<Provider<T>>
     {
+        static final String PREFIX = "META-INF/services/";
+
         Enumeration<URL> configs;
         Iterator<String> pending;
-        String nextName;
+        Class<T> next;
 
-        @Override
-        boolean hasNextService() {
-            if (nextName != null) {
+        /**
+         * Parse a single line from the given configuration file, adding the
+         * name on the line to the names list.
+         */
+        private int parseLine(URL u, BufferedReader r, int lc, Set<String> names)
+            throws IOException
+        {
+            String ln = r.readLine();
+            if (ln == null) {
+                return -1;
+            }
+            int ci = ln.indexOf('#');
+            if (ci >= 0) ln = ln.substring(0, ci);
+            ln = ln.trim();
+            int n = ln.length();
+            if (n != 0) {
+                if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
+                    fail(service, u, lc, "Illegal configuration-file syntax");
+                int cp = ln.codePointAt(0);
+                if (!Character.isJavaIdentifierStart(cp))
+                    fail(service, u, lc, "Illegal provider-class name: " + ln);
+                int start = Character.charCount(cp);
+                for (int i = start; i < n; i += Character.charCount(cp)) {
+                    cp = ln.codePointAt(i);
+                    if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
+                        fail(service, u, lc, "Illegal provider-class name: " + ln);
+                }
+                names.add(ln);
+            }
+            return lc + 1;
+        }
+
+        /**
+         * Parse the content of the given URL as a provider-configuration file.
+         */
+        private Iterator<String> parse(URL u) {
+            Set<String> names = new LinkedHashSet<>(); // preserve insertion order
+            try {
+                URLConnection uc = u.openConnection();
+                uc.setUseCaches(false);
+                try (InputStream in = uc.getInputStream();
+                     BufferedReader r
+                         = new BufferedReader(new InputStreamReader(in, "utf-8")))
+                {
+                    int lc = 1;
+                    while ((lc = parseLine(u, r, lc, names)) >= 0);
+                }
+            } catch (IOException x) {
+                fail(service, "Error accessing configuration file", x);
+            }
+            return names.iterator();
+        }
+
+        @SuppressWarnings("unchecked")
+        private boolean hasNextService() {
+            if (next != null) {
                 return true;
             }
-            if (configs == null) {
+
+            Class<?> clazz = null;
+            do {
+                if (configs == null) {
+                    try {
+                        String fullName = PREFIX + service.getName();
+                        if (loader == null)
+                            configs = ClassLoader.getSystemResources(fullName);
+                        else
+                            configs = loader.getResources(fullName);
+                    } catch (IOException x) {
+                        fail(service, "Error locating configuration files", x);
+                    }
+                }
+                while ((pending == null) || !pending.hasNext()) {
+                    if (!configs.hasMoreElements()) {
+                        return false;
+                    }
+                    pending = parse(configs.nextElement());
+                }
+                String cn = pending.next();
                 try {
-                    String fullName = PREFIX + service.getName();
-                    if (loader == null)
-                        configs = ClassLoader.getSystemResources(fullName);
-                    else
-                        configs = loader.getResources(fullName);
-                } catch (IOException x) {
-                    fail(service, "Error locating configuration files", x);
+                    clazz = Class.forName(cn, false, loader);
+                } catch (ClassNotFoundException x) {
+                    fail(service, "Provider " + cn + " not found");
                 }
-            }
-            while ((pending == null) || !pending.hasNext()) {
-                if (!configs.hasMoreElements()) {
-                    return false;
+
+                if (!service.isAssignableFrom(clazz)) {
+                    fail(service, "Provider " + cn  + " not a subtype");
                 }
-                pending = parse(service, configs.nextElement());
-            }
-            nextName = pending.next();
+
+            } while (clazz.getModule().isNamed()); // ignore if in named module
+
+            next = (Class<T>) clazz;
             return true;
         }
 
         @Override
-        S nextService() {
-            if (!hasNextService())
+        public boolean hasNext() {
+            if (acc == null) {
+                return hasNextService();
+            } else {
+                PrivilegedAction<Boolean> action = new PrivilegedAction<>() {
+                    public Boolean run() { return hasNextService(); }
+                };
+                return AccessController.doPrivileged(action, acc);
+            }
+        }
+
+        @Override
+        public Provider<T> next() {
+            if (!hasNext())
                 throw new NoSuchElementException();
-            String cn = nextName;
-            nextName = null;
-            Class<?> c = null;
-            try {
-                c = Class.forName(cn, false, loader);
-            } catch (ClassNotFoundException x) {
-                fail(service,
-                     "Provider " + cn + " not found");
-            }
-            if (!service.isAssignableFrom(c)) {
-                fail(service,
-                     "Provider " + cn  + " not a subtype");
-            }
-            S p = null;
-            try {
-                @SuppressWarnings("deprecation")
-                Object tmp = c.newInstance();
-                p = service.cast(tmp);
-            } catch (Throwable x) {
-                fail(service,
-                     "Provider " + cn + " could not be instantiated",
-                     x);
-            }
-            providers.add(p);
-            providerNames.add(cn);
-            return p;
+
+            Class<T> clazz = next;
+            next = null;
+
+            // Provider::get will invoke constructor will reduced permissions
+            return new ProviderImpl<T>(service, clazz, acc);
         }
     }
 
     /**
-     * Lazily loads the available providers of this loader's service.
+     * Returns a new lookup iterator.
+     */
+    private Iterator<Provider<S>> newLookupIterator() {
+        assert layer == null || loader == null;
+        if (layer != null) {
+            return new LayerLookupIterator<>();
+        } else {
+            Iterator<Provider<S>> first = new ModuleServicesLookupIterator<>();
+            Iterator<Provider<S>> second = new LazyClassPathLookupIterator<>();
+            return new Iterator<Provider<S>>() {
+                @Override
+                public boolean hasNext() {
+                    return (first.hasNext() || second.hasNext());
+                }
+                @Override
+                public Provider<S> next() {
+                    if (first.hasNext()) {
+                        return first.next();
+                    } else if (second.hasNext()) {
+                        return second.next();
+                    } else {
+                        throw new NoSuchElementException();
+                    }
+                }
+            };
+        }
+    }
+
+    /**
+     * Lazily load and instantiate the available providers of this loader's
+     * service.
      *
      * <p> The iterator returned by this method first yields all of the
-     * elements of the provider cache, in instantiation order.  It then lazily
-     * loads and instantiates any remaining providers, adding each one to the
-     * cache in turn.
+     * elements of the provider cache, in the order that they were loaded.
+     * It then lazily loads and instantiates any remaining providers,
+     * adding each one to the cache in turn.
      *
      * <p> To achieve laziness the actual work of locating and instantiating
      * providers must be done by the iterator itself. Its {@link
      * java.util.Iterator#hasNext hasNext} and {@link java.util.Iterator#next
      * next} methods can therefore throw a {@link ServiceConfigurationError}
-     * if a provider class cannot be loaded, doesn't have the appropriate
-     * constructor, can't be assigned to the service type or if any other kind
-     * of exception or error is thrown as the next provider is located and
-     * instantiated. To write robust code it is only necessary to catch {@link
-     * ServiceConfigurationError} when using a service iterator.
+     * if a provider class cannot be loaded, doesn't have an appropriate static
+     * factory method or constructor, can't be assigned to the service type or
+     * if any other kind of exception or error is thrown as the next provider
+     * is located and instantiated. To write robust code it is only necessary
+     * to catch {@link ServiceConfigurationError} when using a service iterator.
      *
      * <p> If such an error is thrown then subsequent invocations of the
      * iterator will make a best effort to locate and instantiate the next
@@ -856,7 +1118,7 @@
      * preferable to throw an error rather than try to recover or, even worse,
      * fail silently.</blockquote>
      *
-     * <p> If this loader's provider cache is cleared by invoking the {@link
+     * <p> If this loader's provider caches are cleared by invoking the {@link
      * #reload() reload} method then existing iterators for this service
      * loader should be discarded.
      * The {@link java.util.Iterator#hasNext() hasNext} and {@link
@@ -868,16 +1130,16 @@
      * Invoking its {@link java.util.Iterator#remove() remove} method will
      * cause an {@link UnsupportedOperationException} to be thrown.
      *
-     * @implNote When adding providers to the cache, the {@link #iterator
-     * Iterator} processes resources in the order that the {@link
-     * java.lang.ClassLoader#getResources(java.lang.String)
-     * ClassLoader.getResources(String)} method finds the service configuration
-     * files.
-     *
      * @return  An iterator that lazily loads providers for this loader's
      *          service
      */
     public Iterator<S> iterator() {
+
+        // create lookup iterator if needed
+        if (lookupIterator1 == null) {
+            lookupIterator1 = newLookupIterator();
+        }
+
         return new Iterator<S>() {
 
             // record reload count
@@ -895,34 +1157,24 @@
                     throw new ConcurrentModificationException();
             }
 
+            @Override
             public boolean hasNext() {
                 checkReloadCount();
-                if (index < providers.size())
+                if (index < instantiatedProviders.size())
                     return true;
-
-                if (layerLookupIterator != null) {
-                    return layerLookupIterator.hasNext();
-                } else {
-                    return moduleServicesIterator.hasNext() ||
-                            lazyClassPathIterator.hasNext();
-                }
+                return lookupIterator1.hasNext();
             }
 
+            @Override
+            @SuppressWarnings("unchecked")
             public S next() {
                 checkReloadCount();
                 S next;
-                if (index < providers.size()) {
-                    next = providers.get(index);
+                if (index < instantiatedProviders.size()) {
+                    next = instantiatedProviders.get(index);
                 } else {
-                    if (layerLookupIterator != null) {
-                        next = layerLookupIterator.next();
-                    } else {
-                        if (moduleServicesIterator.hasNext()) {
-                            next = moduleServicesIterator.next();
-                        } else {
-                            next = lazyClassPathIterator.next();
-                        }
-                    }
+                    next = lookupIterator1.next().get();
+                    instantiatedProviders.add(next);
                 }
                 index++;
                 return next;
@@ -932,6 +1184,108 @@
     }
 
     /**
+     * Returns a stream that lazily loads the available providers of this
+     * loader's service. The stream elements are of type {@link Provider
+     * Provider}, the {@code Provider}'s {@link Provider#get() get} method
+     * must be invoked to get or instantiate the provider.
+     *
+     * <p> When processing the stream then providers that were previously
+     * loaded by stream operations are processed first, in load order. It then
+     * lazily loads any remaining providers. If a provider class cannot be
+     * loaded, can't be assigned to the service type, or some other error is
+     * thrown when locating the provider then it is wrapped with a {@code
+     * ServiceConfigurationError} and thrown by whatever method caused the
+     * provider to be loaded. </p>
+     *
+     * <p> If this loader's provider caches are cleared by invoking the {@link
+     * #reload() reload} method then existing streams for this service
+     * loader should be discarded. </p>
+     *
+     * <p> The following examples demonstrate usage. The first example
+     * creates a stream of providers, the second example is the same except
+     * that it sorts the providers by provider class name (and so locate all
+     * providers).
+     * <pre>{@code
+     *    Stream<CodecSet> providers = ServiceLoader.load(CodecSet.class)
+     *            .stream()
+     *            .map(Provider::get);
+     *
+     *    Stream<CodecSet> providers = ServiceLoader.load(CodecSet.class)
+     *            .stream()
+     *            .sorted(Comparator.comparing(p -> p.type().getName()))
+     *            .map(Provider::get);
+     * }</pre>
+     *
+     * @return  A stream that lazily loads providers for this loader's service
+     *
+     * @since 9
+     */
+    public Stream<Provider<S>> stream() {
+        // use cached providers as the source when all providers loaded
+        if (loadedAllProviders) {
+            return loadedProviders.stream();
+        }
+
+        // create lookup iterator if needed
+        if (lookupIterator2 == null) {
+            lookupIterator2 = newLookupIterator();
+        }
+
+        // use lookup iterator and cached providers as source
+        Spliterator<Provider<S>> s = new ProviderSpliterator<>(lookupIterator2);
+        return StreamSupport.stream(s, false);
+    }
+
+    private class ProviderSpliterator<T> implements Spliterator<Provider<T>> {
+        final int expectedReloadCount = ServiceLoader.this.reloadCount;
+        final Iterator<Provider<T>> iterator;
+        int index;
+
+        ProviderSpliterator(Iterator<Provider<T>> iterator) {
+            this.iterator = iterator;
+        }
+
+        @Override
+        public Spliterator<Provider<T>> trySplit() {
+            return null;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public boolean tryAdvance(Consumer<? super Provider<T>> action) {
+            if (ServiceLoader.this.reloadCount != expectedReloadCount)
+                throw new ConcurrentModificationException();
+            Provider<T> next = null;
+            if (index < loadedProviders.size()) {
+                next = (Provider<T>) loadedProviders.get(index);
+            } else if (iterator.hasNext()) {
+                next = iterator.next();
+            } else {
+                loadedAllProviders = true;
+            }
+            index++;
+            if (next != null) {
+                action.accept(next);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int characteristics() {
+            // need to decide on DISTINCT
+            // not IMMUTABLE as structural interference possible
+            return Spliterator.ORDERED + Spliterator.NONNULL;
+        }
+
+        @Override
+        public long estimateSize() {
+            return Long.MAX_VALUE;
+        }
+    }
+
+    /**
      * Creates a new service loader for the given service type, class
      * loader, and caller.
      *
@@ -993,15 +1347,22 @@
      * context class loader}.
      *
      * <p> An invocation of this convenience method of the form
-     *
-     * <blockquote><pre>
-     * ServiceLoader.load(<i>service</i>)</pre></blockquote>
+     * <pre>{@code
+     * ServiceLoader.load(service)
+     * }</pre>
      *
      * is equivalent to
      *
-     * <blockquote><pre>
-     * ServiceLoader.load(<i>service</i>,
-     *                    Thread.currentThread().getContextClassLoader())</pre></blockquote>
+     * <pre>{@code
+     * ServiceLoader.load(service, Thread.currentThread().getContextClassLoader())
+     * }</pre>
+     *
+     * @apiNote Service loader objects obtained with this method should not be
+     * cached VM-wide. For example, different applications in the same VM may
+     * have different thread context class loaders. A lookup by one application
+     * may locate a service provider that is only visible via its thread
+     * context class loader and so is not suitable to be located by the other
+     * application. Memory leaks can also arise.
      *
      * @param  <S> the class of the service type
      *
@@ -1027,9 +1388,9 @@
      *
      * <p> This convenience method is equivalent to: </p>
      *
-     * <blockquote><pre>
-     * ServiceLoader.load(<i>service</i>, <i>ClassLoader.getPlatformClassLoader())</i>
-     * </pre></blockquote>
+     * <pre>{@code
+     * ServiceLoader.load(service, ClassLoader.getPlatformClassLoader())
+     * }</pre>
      *
      * <p> This method is intended for use when only installed providers are
      * desired.  The resulting service will only find and load providers that
@@ -1050,13 +1411,8 @@
      */
     @CallerSensitive
     public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {
-        ClassLoader cl = ClassLoader.getSystemClassLoader();
-        ClassLoader prev = null;
-        while (cl != null) {
-            prev = cl;
-            cl = cl.getParent();
-        }
-        return new ServiceLoader<>(Reflection.getCallerClass(), service, prev);
+        ClassLoader cl = ClassLoader.getPlatformClassLoader();
+        return new ServiceLoader<>(Reflection.getCallerClass(), service, cl);
     }
 
     /**
@@ -1087,9 +1443,58 @@
      */
     @CallerSensitive
     public static <S> ServiceLoader<S> load(Layer layer, Class<S> service) {
-        return new ServiceLoader<>(Reflection.getCallerClass(),
-                                   Objects.requireNonNull(layer),
-                                   Objects.requireNonNull(service));
+        return new ServiceLoader<>(Reflection.getCallerClass(), layer, service);
+    }
+
+    /**
+     * Load the first available provider of this loader's service. This
+     * convenience method is equivalent to invoking the {@link #iterator()
+     * iterator()} method and obtaining the first element. It therefore
+     * returns the first element from the provider cache if possible, it
+     * otherwise attempts to load and instantiate the first provider.
+     *
+     * @return The first provider or empty {@code Optional} if no providers
+     *         are located
+     *
+     * @throws ServiceConfigurationError
+     *         If a provider class cannot be loaded, doesn't have the
+     *         appropriate static factory method or constructor, can't be
+     *         assigned to the service type, or if any other kind of exception
+     *         or error is thrown when locating or instantiating the provider.
+     *
+     * @since 9
+     */
+    public Optional<S> findFirst() {
+        Iterator<S> iterator = iterator();
+        if (iterator.hasNext()) {
+            return Optional.of(iterator.next());
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    /**
+     * Clear this loader's provider cache so that all providers will be
+     * reloaded.
+     *
+     * <p> After invoking this method, subsequent invocations of the {@link
+     * #iterator() iterator} or {@link #stream() stream} methods will lazily
+     * look up providers (and instantiate in the case of {@code iterator})
+     * from scratch, just as is done by a newly-created loader.
+     *
+     * <p> This method is intended for use in situations in which new providers
+     * can be installed into a running Java virtual machine.
+     */
+    public void reload() {
+        lookupIterator1 = null;
+        instantiatedProviders.clear();
+
+        lookupIterator2 = null;
+        loadedProviders.clear();
+        loadedAllProviders = false;
+
+        // increment count to allow CME be thrown
+        reloadCount++;
     }
 
     /**
--- a/src/java.base/share/classes/jdk/internal/loader/BootLoader.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/loader/BootLoader.java	Thu Sep 15 13:14:46 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	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Thu Sep 15 13:14:46 2016 -0700
@@ -29,6 +29,8 @@
 import java.io.FilePermission;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Exports;
 import java.lang.module.ModuleReference;
 import java.lang.module.ModuleReader;
 import java.net.MalformedURLException;
@@ -90,7 +92,7 @@
 {
     static {
         if (!ClassLoader.registerAsParallelCapable())
-            throw new InternalError();
+            throw new InternalError("Unable to register as parallel capable");
     }
 
     // parent ClassLoader
@@ -116,7 +118,7 @@
             if (mref.location().isPresent()) {
                 try {
                     url = mref.location().get().toURL();
-                } catch (MalformedURLException e) { }
+                } catch (MalformedURLException | IllegalArgumentException e) { }
             }
             this.loader = loader;
             this.mref = mref;
@@ -144,9 +146,9 @@
     /**
      * Create a new instance.
      */
-    BuiltinClassLoader(BuiltinClassLoader parent, URLClassPath ucp) {
+    BuiltinClassLoader(BuiltinClassLoader parent, String name, URLClassPath ucp) {
         // ensure getParent() returns null when the parent is the boot loader
-        super(parent == null || parent == ClassLoaders.bootLoader() ? null : parent);
+        super(name, parent == null || parent == ClassLoaders.bootLoader() ? null : parent);
 
         this.parent = parent;
         this.ucp = ucp;
@@ -194,31 +196,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 +232,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,43 +259,142 @@
     }
 
     /**
-     * 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) {
+        String pn = ResourceHelper.getPackageName(name);
+        LoadedModule module = packageToModule.get(pn);
+        if (module != null) {
+            if (module.loader() == this && (name.endsWith(".class")
+                    || isExportedPrivate(module.mref(), pn))) {
+                try {
+                    return findResource(module.name(), name);
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }
+        } else {
+            URL 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<>();
+
+        String pn = ResourceHelper.getPackageName(name);
+        LoadedModule module = packageToModule.get(pn);
+        if (module != null) {
+            if (module.loader() == this && (name.endsWith(".class")
+                    || isExportedPrivate(module.mref(), pn))) {
+                try {
+                    URL url = findResource(module.name(), name);
+                    if (url != null) checked.add(url);
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }
+        } 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 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 | IllegalArgumentException 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 a URL to a resource of the given name on the class path.
      */
-    @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);
-                }
-            }
-            return Collections.enumeration(result); // checked URLs
-        } else {
-            return Collections.emptyEnumeration();
-        }
+    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 +434,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 +546,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.
      *
@@ -661,13 +782,13 @@
                 sealBase = url;
         }
         return definePackage(pn,
-                             specTitle,
-                             specVersion,
-                             specVendor,
-                             implTitle,
-                             implVersion,
-                             implVendor,
-                             sealBase);
+                specTitle,
+                specVersion,
+                specVendor,
+                implTitle,
+                implVersion,
+                implVendor,
+                sealBase);
     }
 
     /**
@@ -755,6 +876,28 @@
     };
 
     /**
+     * Returns true if the given module exports-private the given package
+     * unconditionally.
+     *
+     * @implNote This method currently iterates over each of the module
+     * exports. This will be replaced once the ModuleDescriptor.Exports
+     * API is updated.
+     */
+    private boolean isExportedPrivate(ModuleReference mref, String pn) {
+        ModuleDescriptor descriptor = mref.descriptor();
+        if (descriptor.isWeak())
+            return true;
+        for (ModuleDescriptor.Exports e : descriptor.exports()) {
+            String source = e.source();
+            if (!e.isQualified() && source.equals(pn)
+                    && e.modifiers().contains(Exports.Modifier.PRIVATE)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Checks access to the given URL. We use URLClassPath for consistent
      * checking with java.net.URLClassLoader.
      */
--- a/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java	Thu Sep 15 13:14:46 2016 -0700
@@ -117,7 +117,7 @@
      */
     private static class BootClassLoader extends BuiltinClassLoader {
         BootClassLoader(URLClassPath bcp) {
-            super(null, bcp);
+            super(null, null, bcp);
         }
 
         @Override
@@ -137,7 +137,7 @@
         }
 
         PlatformClassLoader(BootClassLoader parent) {
-            super(parent, null);
+            super(parent, "platform", null);
         }
 
         /**
@@ -164,7 +164,7 @@
         final URLClassPath ucp;
 
         AppClassLoader(PlatformClassLoader parent, URLClassPath ucp) {
-            super(parent, ucp);
+            super(parent, "app", ucp);
             this.ucp = ucp;
         }
 
--- a/src/java.base/share/classes/jdk/internal/loader/Loader.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/loader/Loader.java	Thu Sep 15 13:14:46 2016 -0700
@@ -30,6 +30,7 @@
 import java.io.IOException;
 import java.lang.module.Configuration;
 import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Exports;
 import java.lang.module.ModuleReader;
 import java.lang.module.ModuleReference;
 import java.lang.module.ResolvedModule;
@@ -48,8 +49,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;
@@ -118,7 +123,7 @@
             if (mref.location().isPresent()) {
                 try {
                     url = mref.location().get().toURL();
-                } catch (MalformedURLException e) { }
+                } catch (MalformedURLException | IllegalArgumentException e) { }
             }
             this.mref = mref;
             this.url = url;
@@ -237,7 +242,7 @@
                     }
                     assert layer != null;
 
-                    // find the class loader for the module in the layer
+                    // find the class loader for the module
                     // For now we use the platform loader for modules defined to the
                     // boot loader
                     assert layer.findModule(mn).isPresent();
@@ -267,8 +272,19 @@
                             throw new IllegalArgumentException("Package "
                                 + pn + " cannot be imported from multiple loaders");
                         }
+                    }
+                }
 
-                    }
+                // weak modules export all packages to the target module
+                if (descriptor.isWeak()) {
+                    ClassLoader ldr = loader;
+                    descriptor.packages().forEach(pn -> {
+                        ClassLoader l = remotePackageToLoader.putIfAbsent(pn, ldr);
+                        if (l != null && l != ldr) {
+                            throw new IllegalArgumentException("Package "
+                                    + pn + " cannot be imported from multiple loaders");
+                        }
+                    });
                 }
             }
 
@@ -295,12 +311,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 {
@@ -308,16 +326,89 @@
                         if (ouri.isPresent()) {
                             try {
                                 return ouri.get().toURL();
-                            } catch (MalformedURLException e) { }
+                            } catch (MalformedURLException |
+                                     IllegalArgumentException e) { }
                         }
                         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) {
+        URL url = null;
+        String pn = ResourceHelper.getPackageName(name);
+        LoadedModule module = localPackageToModule.get(pn);
+        if (module != null) {
+            if (name.endsWith(".class") || isExportedPrivate(module.mref(), pn)) {
+                try {
+                    url = findResource(module.name(), name);
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }
+        } else {
+            for (ModuleReference mref : nameToModule.values()) {
+                try {
+                    url = findResource(mref.descriptor().name(), name);
+                    if (url != null)
+                        break;
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }
+        }
+        return url;
+    }
+
+    @Override
+    public Enumeration<URL> findResources(String name) throws IOException {
+        List<URL> urls = new ArrayList<>();
+        String pn = ResourceHelper.getPackageName(name);
+        LoadedModule module = localPackageToModule.get(pn);
+        if (module != null) {
+            if (name.endsWith(".class") || isExportedPrivate(module.mref(), pn)) {
+                try {
+                    URL url = findResource(module.name(), name);
+                    if (url != null)
+                        urls.add(url);
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }
+        } else {
+            for (ModuleReference mref : nameToModule.values()) {
+                try {
+                    URL url = findResource(mref.descriptor().name(), name);
+                    if (url != null)
+                        urls.add(url);
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }
+        }
+        return Collections.enumeration(urls);
     }
 
 
@@ -539,4 +630,25 @@
         }
     }
 
+    /**
+     * Returns true if the given module exports the given package
+     * unconditionally.
+     *
+     * @implNote This method currently iterates over each of the module
+     * exports. This will be replaced once the ModuleDescriptor.Exports
+     * API is updated.
+     */
+    private boolean isExportedPrivate(ModuleReference mref, String pn) {
+        ModuleDescriptor descriptor = mref.descriptor();
+        if (descriptor.isWeak())
+            return true;
+        for (ModuleDescriptor.Exports e : descriptor.exports()) {
+            String source = e.source();
+            if (!e.isQualified() && source.equals(pn)
+                    && e.modifiers().contains(Exports.Modifier.PRIVATE)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/loader/ResourceHelper.java	Thu Sep 15 13:14:46 2016 -0700
@@ -0,0 +1,64 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.loader;
+
+import jdk.internal.module.Checks;
+
+/**
+ * Helper class for Class#getResource, Module#getResourceAsStream, and other
+ * methods that locate a resource in a module.
+ */
+public final class ResourceHelper {
+    private ResourceHelper() { }
+
+    /**
+     * Returns the <em>package name</em> for a resource.
+     */
+    public static String getPackageName(String name) {
+        int index = name.lastIndexOf('/');
+        if (index != -1) {
+            return name.substring(0, index).replace("/", ".");
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * Returns true if the resource is a <em>simple resource</em> that can
+     * never be encapsulated. Resources ending in "{@code .class}" or where
+     * the package name is not a Java identifier are resources that can
+     * never be encapsulated.
+     */
+    public static boolean isSimpleResource(String name) {
+        int len = name.length();
+        if (len > 6 && name.endsWith(".class")) {
+            return true;
+        }
+        if (!Checks.isJavaIdentifier(getPackageName(name))) {
+            return true;
+        }
+        return false;
+    }
+}
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java	Thu Sep 15 13:14:46 2016 -0700
@@ -136,6 +136,12 @@
     Layer getBootLayer();
 
     /**
+     * Records in the class loader that the given layer has at least one
+     * module defined to the class loader.
+     */
+    void bindToLayer(ClassLoader loader, Layer layer);
+
+    /**
      * Returns the ServicesCatalog for the given class loader.
      */
     ServicesCatalog getServicesCatalog(ClassLoader cl);
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Thu Sep 15 13:14:46 2016 -0700
@@ -39,6 +39,7 @@
 import java.lang.module.ModuleReader;
 import java.lang.module.ModuleReference;
 import java.net.URI;
+import java.nio.file.Path;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
@@ -51,22 +52,35 @@
 public interface JavaLangModuleAccess {
 
     /**
-     * Returns {@code ModuleDescriptor.Requires} of the given modifier
+     * Creates a new builder with the given module name.
+     *
+     * @param weak
+     *        Indicates whether the module is weak
+     * @param strict
+     *        Indicates whether module names are checked or not
+     */
+    ModuleDescriptor.Builder newBuilder(String mn, boolean weak, boolean strict);
+
+    /**
+     * 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}
@@ -88,6 +102,7 @@
      * Returns a new {@code ModuleDescriptor} instance.
      */
     ModuleDescriptor newModuleDescriptor(String name,
+                                         boolean weak,
                                          boolean automatic,
                                          boolean synthetic,
                                          Set<Requires> requires,
@@ -103,6 +118,11 @@
                                          ModuleHashes hashes);
 
     /**
+     * Returns the object with the hashes of other modules
+     */
+    Optional<ModuleHashes> hashes(ModuleDescriptor descriptor);
+
+    /**
      * Resolves a collection of root modules, with service binding
      * and the empty configuration as the parent. The post resolution
      * checks are optionally run.
@@ -120,8 +140,10 @@
                                      Supplier<ModuleReader> readerSupplier);
 
     /**
-     * Returns the object with the hashes of other modules
+     * Creates a ModuleFinder for a module path.
      */
-    Optional<ModuleHashes> hashes(ModuleDescriptor descriptor);
+    ModuleFinder newModulePath(Runtime.Version version,
+                               boolean isLinkPhase,
+                               Path... entries);
 
 }
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java	Thu Sep 15 13:14:46 2016 -0700
@@ -71,16 +71,37 @@
     void addExports(Module m1, String pkg, Module m2);
 
     /**
+     * Updates module m1 to export "private" a package to module m2. The export
+     * does not result in a strong reference to m2 (m2 can be GC'ed).
+     */
+    void addExportsPrivate(Module m1, String pkg, Module m2);
+
+    /**
      * Updates a module m to export a package to all modules.
      */
     void addExportsToAll(Module m, String pkg);
 
     /**
+     * Updates a module m to export "private" a package to all modules.
+     */
+    void addExportsPrivateToAll(Module m, String pkg);
+
+    /**
      * Updates a module m to export a package to all unnamed modules.
      */
     void addExportsToAllUnnamed(Module m, String pkg);
 
     /**
+     * Updates a module m to export "private" a package to all unnamed modules.
+     */
+    void addExportsPrivateToAllUnnamed(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	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/Builder.java	Thu Sep 15 13:14:46 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
@@ -30,6 +30,7 @@
 import java.lang.module.ModuleDescriptor.Requires;
 import java.lang.module.ModuleDescriptor.Version;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -40,7 +41,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 +54,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;
@@ -66,6 +62,9 @@
     final Set<Requires> requires;
     final Set<Exports> exports;
     final Map<String, Provides> provides;
+    boolean weak;
+    boolean automatic;
+    boolean synthetic;
     Set<String> packages;
     Set<String> uses;
     Version version;
@@ -76,8 +75,7 @@
     String algorithm;
     Map<String, String> hashes;
 
-    Builder(String name, int reqs, int exports,
-            int provides, int packages) {
+    Builder(String name, int reqs, int exports, int provides) {
         this.name = name;
         this.requires = reqs > 0 ? new HashSet<>(reqs) : Collections.emptySet();
         this.exports  = exports > 0 ? new HashSet<>(exports) : Collections.emptySet();
@@ -85,34 +83,27 @@
         this.uses = Collections.emptySet();
     }
 
+    Builder weak(boolean value) {
+        this.weak = value;
+        return this;
+    }
+
+    Builder automatic(boolean value) {
+        this.automatic = value;
+        return this;
+    }
+
+    Builder synthetic(boolean value) {
+        this.synthetic = value;
+        return this;
+    }
+
     /**
      * Adds a module dependence with the given (and possibly empty) set
      * 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 +116,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,9 +257,24 @@
         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
+                                        weak,         // weak
+                                        automatic,    // automatic
+                                        synthetic,    // synthetic
                                         requires,
                                         uses,
                                         exports,
--- a/src/java.base/share/classes/jdk/internal/module/Checks.java	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/Checks.java	Thu Sep 15 13:14:46 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	Mon Sep 12 22:04:48 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java	Thu Sep 15 13:14:46 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;
@@ -38,6 +37,8 @@
 import java.util.Map;
 import java.util.Set;
 
+import jdk.internal.misc.JavaLangModuleAccess;
+import jdk.internal.misc.SharedSecrets;
 import jdk.internal.org.objectweb.asm.Attribute;
 import jdk.internal.org.objectweb.asm.ByteVector;
 import jdk.internal.org.objectweb.asm.ClassReader;
@@ -51,7 +52,7 @@
  * class file attributes in a module-info class file.
  */
 
-class ClassFileAttributes {
+public final class ClassFileAttributes {
 
     private ClassFileAttributes() { }
 
@@ -60,16 +61,18 @@
      *   // See lang-vm.html for details.
      * }
      */
-    static class ModuleAttribute extends Attribute {
+    public static class ModuleAttribute extends Attribute {
+        private static final JavaLangModuleAccess JLMA
+            = SharedSecrets.getJavaLangModuleAccess();
 
         private ModuleDescriptor descriptor;
 
-        ModuleAttribute(ModuleDescriptor descriptor) {
+        public ModuleAttribute(ModuleDescriptor descriptor) {
             super(MODULE);
             this.descriptor = descriptor;
         }
 
-        ModuleAttribute() {
+        public ModuleAttribute() {
             super(MODULE);
         }
 
@@ -81,27 +84,34 @@
                                  int codeOff,
                                  Label[] labels)
         {
-            ModuleDescriptor.Builder builder
-                = new ModuleDescriptor.Builder("xyzzy"); // Name never used
             ModuleAttribute attr = new ModuleAttribute();
 
+            // module_flags
+            int module_flags = cr.readUnsignedShort(off);
+            boolean weak = ((module_flags & ACC_WEAK) != 0);
+            off += 2;
+
+            ModuleDescriptor.Builder builder = JLMA.newBuilder("m", weak, false);
+
             // requires_count and requires[requires_count]
             int requires_count = cr.readUnsignedShort(off);
             off += 2;
             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.TRANSITIVE);
+                    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 +123,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_REFLECTION) != 0)
+                            mods.add(Exports.Modifier.PRIVATE);
+                        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 +149,9 @@
                             off += 2;
                             targets.add(t);
                         }
-                        builder.exports(pkg, targets);
+                        builder.exports(mods, pkg, targets);
                     } else {
-                        builder.exports(pkg);
+                        builder.exports(mods, pkg);
                     }
                 }
             }
@@ -169,6 +196,14 @@
             assert descriptor != null;
             ByteVector attr = new ByteVector();
 
+            // module_flags
+            int module_flags = 0;
+            if (descriptor.isWeak())
+                module_flags |= ACC_WEAK;
+            if (descriptor.isSynthetic())
+                module_flags |= ACC_SYNTHETIC;
+            attr.putShort(module_flags);
+
             // requires_count
             attr.putShort(descriptor.requires().size());
 
@@ -176,11 +211,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.TRANSITIVE))
+                    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);
@@ -188,13 +225,23 @@
             }
 
             // exports_count and exports[exports_count];
-            if (descriptor.exports().isEmpty()) {
+            if (descriptor.isWeak() || descriptor.exports().isEmpty()) {
                 attr.putShort(0);
             } else {