changeset 18529:b2b4d228af9a

More refactoring to make it easier to add JDK specific attributes
author alanb
date Mon, 12 Dec 2016 14:31:55 +0000
parents 6743bd89af1f
children ec901a312639
files src/java.base/share/classes/java/lang/StackTraceElement.java src/java.base/share/classes/java/lang/module/ModuleDescriptor.java src/java.base/share/classes/java/lang/module/ModuleFinder.java src/java.base/share/classes/java/lang/module/ModuleInfo.java src/java.base/share/classes/java/lang/module/ModulePath.java src/java.base/share/classes/java/lang/module/ModuleReference.java src/java.base/share/classes/java/lang/module/ModuleReferences.java src/java.base/share/classes/java/lang/module/Resolver.java src/java.base/share/classes/java/lang/module/SystemModuleFinder.java src/java.base/share/classes/java/lang/reflect/Module.java src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java src/java.base/share/classes/jdk/internal/module/ModuleHashes.java src/java.base/share/classes/jdk/internal/module/ModuleInfo.java src/java.base/share/classes/jdk/internal/module/ModulePatcher.java src/java.base/share/classes/jdk/internal/module/ModulePath.java src/java.base/share/classes/jdk/internal/module/ModuleReferenceImpl.java src/java.base/share/classes/jdk/internal/module/ModuleReferences.java src/java.base/share/classes/jdk/internal/module/SystemModuleFinder.java src/jdk.jartool/share/classes/sun/tools/jar/Main.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolConfiguration.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java test/java/lang/module/ModuleDescriptorTest.java test/java/lang/module/ModuleReader/ModuleReaderTest.java test/java/lang/module/ModuleReferenceTest.java test/lib/testlibrary/ModuleUtils.java test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java test/tools/jmod/hashes/HashesTest.java
diffstat 31 files changed, 2949 insertions(+), 2964 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/StackTraceElement.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/java.base/share/classes/java/lang/StackTraceElement.java	Mon Dec 12 14:31:55 2016 +0000
@@ -26,9 +26,9 @@
 package java.lang;
 
 import jdk.internal.loader.BuiltinClassLoader;
-import jdk.internal.misc.SharedSecrets;
 import jdk.internal.misc.VM;
 import jdk.internal.module.ModuleHashes;
+import jdk.internal.module.ModuleReferenceImpl;
 
 import java.lang.module.ModuleDescriptor.Version;
 import java.lang.module.ModuleReference;
@@ -492,8 +492,8 @@
                     .findModule("java.base");
             assert resolvedModule.isPresent();
             ModuleReference mref = resolvedModule.get().reference();
-            ModuleHashes hashes = SharedSecrets.getJavaLangModuleAccess()
-                    .recordedHashes(mref);
+            assert mref instanceof ModuleReferenceImpl;
+            ModuleHashes hashes = ((ModuleReferenceImpl)mref).recordedHashes();
             if (hashes != null) {
                 Set<String> names = new HashSet<>(hashes.names());
                 names.add("java.base");
--- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Mon Dec 12 14:31:55 2016 +0000
@@ -29,7 +29,6 @@
 import java.io.IOException;
 import java.io.PrintStream;
 import java.io.UncheckedIOException;
-import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -51,7 +50,7 @@
 import static java.util.Objects.*;
 
 import jdk.internal.module.Checks;
-import jdk.internal.module.ModuleHashes;
+import jdk.internal.module.ModuleInfo;
 
 
 /**
@@ -1342,15 +1341,13 @@
     public static final class Builder {
         final String name;
         final boolean strict; // true if module names are checked
-        boolean open;
+        final boolean open;
+        final boolean synthetic;
         boolean automatic;
-        boolean synthetic;
         final Map<String, Requires> requires = new HashMap<>();
-
         final Map<String, Exports> exports = new HashMap<>();
         final Map<String, Opens> opens = new HashMap<>();
         final Set<String> concealedPackages = new HashSet<>();
-
         final Set<String> uses = new HashSet<>();
         final Map<String, Provides> provides = new HashMap<>();
         Version version;
@@ -1365,14 +1362,11 @@
          * @param strict
          *        Indicates whether module names are checked or not
          */
-        Builder(String name, boolean strict) {
+        Builder(String name, boolean strict, boolean open, boolean synthetic) {
+            this.name = (strict) ? requireModuleName(name) : name;
             this.strict = strict;
-            this.name = (strict) ? requireModuleName(name) : name;
-        }
-
-        /* package */ Builder open(boolean open) {
             this.open = open;
-            return this;
+            this.synthetic = synthetic;
         }
 
         /* package */ Builder automatic(boolean automatic) {
@@ -1380,10 +1374,20 @@
             return this;
         }
 
-        /* package */ boolean isOpen() { return open; }
+        /**
+         * Returns the set of packages that are exported (unconditionally or
+         * unconditionally).
+         */
+        /* package */ Set<String> exportedPackages() {
+            return exports.keySet();
+        }
 
-        /* package */ boolean isAutomatic() {
-            return automatic;
+        /**
+         * Returns the set of packages that are opened (unconditionally or
+         * unconditionally).
+         */
+        /* package */Set<String> openPackages() {
+            return opens.keySet();
         }
 
         /**
@@ -1759,17 +1763,6 @@
             return opens(Collections.emptySet(), pn);
         }
 
-
-        // Used by ModuleInfo, after a packageFinder is invoked
-        /* package */ Set<String> exportedAndOpenPackages() {
-            if (opens.isEmpty())
-                return exports.keySet();
-            Set<String> result = new HashSet<>();
-            result.addAll(exports.keySet());
-            result.addAll(opens.keySet());
-            return result;
-        }
-
         /**
          * Adds a service dependence.
          *
@@ -1843,7 +1836,6 @@
             if (providerNames.isEmpty())
                 throw new IllegalArgumentException("Empty providers set");
             providerNames.forEach(Checks::requireServiceProviderName);
-
             provides.put(service, p);
             return this;
         }
@@ -2026,11 +2018,6 @@
             return this;
         }
 
-        /* package */ Builder synthetic(boolean v) {
-            this.synthetic = v;
-            return this;
-        }
-
         /**
          * Builds and returns a {@code ModuleDescriptor} from its components.
          *
@@ -2039,7 +2026,9 @@
         public ModuleDescriptor build() {
             Set<Requires> requires = new HashSet<>(this.requires.values());
 
-            Set<String> packages = new HashSet<>(exportedAndOpenPackages());
+            Set<String> packages = new HashSet<>();
+            packages.addAll(exports.keySet());
+            packages.addAll(opens.keySet());
             packages.addAll(concealedPackages);
 
             Set<Exports> exports = new HashSet<>(this.exports.values());
@@ -2217,7 +2206,7 @@
      *         identifier
      */
     public static Builder module(String name) {
-        return new Builder(name, true);
+        return new Builder(name, true, false, false);
     }
 
     /**
@@ -2245,7 +2234,7 @@
      *         identifier
      */
     public static Builder openModule(String name) {
-        return new Builder(name, true).open(true);
+        return new Builder(name, true, true, false);
     }
 
     /**
@@ -2267,7 +2256,7 @@
      * @see ModuleFinder#of(Path[])
      */
     public static Builder automaticModule(String name) {
-        return new Builder(name, true).automatic(true);
+        return new Builder(name, true, false, false).automatic(true);
     }
 
 
@@ -2423,13 +2412,21 @@
         jdk.internal.misc.SharedSecrets
             .setJavaLangModuleAccess(new jdk.internal.misc.JavaLangModuleAccess() {
                 @Override
-                public Builder newModuleBuilder(String mn, boolean strict) {
-                    return new Builder(mn, strict);
+                public Builder newModuleBuilder(String mn,
+                                                boolean strict,
+                                                boolean open,
+                                                boolean synthetic) {
+                    return new Builder(mn, strict, open, synthetic);
                 }
 
                 @Override
-                public Builder newOpenModuleBuilder(String mn, boolean strict) {
-                    return new Builder(mn, strict).open(true);
+                public Set<String> exportedPackages(ModuleDescriptor.Builder builder) {
+                    return builder.exportedPackages();
+                }
+
+                @Override
+                public Set<String> openPackages(ModuleDescriptor.Builder builder) {
+                    return builder.openPackages();
                 }
 
                 @Override
@@ -2509,17 +2506,10 @@
                                                 osName,
                                                 osArch,
                                                 osVersion,
-
                                                 hashCode,
                                                 false);
                 }
 
-
-                @Override
-                public ModuleHashes recordedHashes(ModuleReference mref) {
-                    return mref.recordedHashes();
-                }
-
                 @Override
                 public Configuration resolveRequiresAndUses(ModuleFinder finder,
                                                             Collection<String> roots,
@@ -2528,20 +2518,6 @@
                 {
                     return Configuration.resolveRequiresAndUses(finder, roots, check, traceOutput);
                 }
-
-                @Override
-                public ModuleReference newPatchedModule(ModuleDescriptor descriptor,
-                                                        URI location,
-                                                        Supplier<ModuleReader> s) {
-                    return new ModuleReference(descriptor, location, s, true, null, null);
-                }
-
-                @Override
-                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	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Mon Dec 12 14:31:55 2016 +0000
@@ -42,6 +42,8 @@
 import java.util.Optional;
 import java.util.Set;
 
+import jdk.internal.module.ModulePath;
+import jdk.internal.module.SystemModuleFinder;
 import sun.security.action.GetPropertyAction;
 
 /**
--- a/src/java.base/share/classes/java/lang/module/ModuleInfo.java	Sat Dec 10 05:22:58 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,967 +0,0 @@
-/*
- * 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
- * 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.io.DataInput;
-import java.io.DataInputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
-import java.lang.module.ModuleDescriptor.Builder;
-import java.lang.module.ModuleDescriptor.Requires;
-import java.lang.module.ModuleDescriptor.Exports;
-import java.lang.module.ModuleDescriptor.Opens;
-import java.lang.module.ModuleDescriptor.Version;
-import java.nio.ByteBuffer;
-import java.nio.BufferUnderflowException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Supplier;
-
-import jdk.internal.module.ModuleHashes;
-
-import static jdk.internal.module.ClassFileConstants.*;
-
-
-/**
- * Read module information from a {@code module-info} class file.
- *
- * @implNote The rationale for the hand-coded reader is startup performance
- * and fine control over the throwing of InvalidModuleDescriptorException.
- */
-
-final class ModuleInfo {
-
-    // supplies the set of packages when ModulePackages attribute not present
-    private final Supplier<Set<String>> packageFinder;
-
-    // indicates if the ModuleHashes attribute should be parsed
-    private final boolean parseHashes;
-
-    private ModuleInfo(Supplier<Set<String>> pf, boolean ph) {
-        packageFinder = pf;
-        parseHashes = ph;
-    }
-
-    private ModuleInfo(Supplier<Set<String>> pf) {
-        this(pf, true);
-    }
-
-    /**
-     * A holder class for the ModuleDescriptor that is created by reading the
-     * Module and other standard class file attributes. It also holds the objects
-     * that represent the non-standard class file attributes that are read from
-     * the class file.
-     */
-    static final class Attributes {
-        private final ModuleDescriptor descriptor;
-        private final ModuleHashes recordedHashes;
-        Attributes(ModuleDescriptor descriptor, ModuleHashes recordedHashes) {
-            this.descriptor = descriptor;
-            this.recordedHashes = recordedHashes;
-        }
-        ModuleDescriptor descriptor() {
-            return descriptor;
-        }
-        ModuleHashes recordedHashes() {
-            return recordedHashes;
-        }
-    }
-
-
-    /**
-     * Reads a {@code module-info.class} from the given input stream.
-     *
-     * @throws InvalidModuleDescriptorException
-     * @throws IOException
-     */
-    static Attributes read(InputStream in, Supplier<Set<String>> pf)
-        throws IOException
-    {
-        try {
-            return new ModuleInfo(pf).doRead(new DataInputStream(in));
-        } catch (IllegalArgumentException | IllegalStateException e) {
-            throw invalidModuleDescriptor(e.getMessage());
-        } catch (EOFException x) {
-            throw truncatedModuleDescriptor();
-        }
-    }
-
-    /**
-     * Reads a {@code module-info.class} from the given byte buffer.
-     *
-     * @throws InvalidModuleDescriptorException
-     * @throws UncheckedIOException
-     */
-    static Attributes read(ByteBuffer bb, Supplier<Set<String>> pf) {
-        try {
-            return new ModuleInfo(pf).doRead(new DataInputWrapper(bb));
-        } catch (IllegalArgumentException | IllegalStateException e) {
-            throw invalidModuleDescriptor(e.getMessage());
-        } catch (EOFException x) {
-            throw truncatedModuleDescriptor();
-        } catch (IOException ioe) {
-            throw new UncheckedIOException(ioe);
-        }
-    }
-
-    /**
-     * Reads a {@code module-info.class} from the given byte buffer
-     * but ignore the {@code ModuleHashes} attribute.
-     *
-     * @throws InvalidModuleDescriptorException
-     * @throws UncheckedIOException
-     */
-    static Attributes readIgnoringHashes(ByteBuffer bb, Supplier<Set<String>> pf) {
-        try {
-            return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb));
-        } catch (IllegalArgumentException | IllegalStateException e) {
-            throw invalidModuleDescriptor(e.getMessage());
-        } catch (EOFException x) {
-            throw truncatedModuleDescriptor();
-        } catch (IOException ioe) {
-            throw new UncheckedIOException(ioe);
-        }
-    }
-
-    /**
-     * Reads the input as a module-info class file.
-     *
-     * @throws IOException
-     * @throws InvalidModuleDescriptorException
-     * @throws IllegalArgumentException if thrown by the ModuleDescriptor.Builder
-     *         because an identifier is not a legal Java identifier, duplicate
-     *         exports, and many other reasons
-     */
-    private Attributes doRead(DataInput in) throws IOException {
-
-        int magic = in.readInt();
-        if (magic != 0xCAFEBABE)
-            throw invalidModuleDescriptor("Bad magic number");
-
-        int minor_version = in.readUnsignedShort();
-        int major_version = in.readUnsignedShort();
-        if (major_version < 53) {
-            throw invalidModuleDescriptor("Must be >= 53.0");
-        }
-
-        ConstantPool cpool = new ConstantPool(in);
-
-        int access_flags = in.readUnsignedShort();
-        if (access_flags != ACC_MODULE)
-            throw invalidModuleDescriptor("access_flags should be ACC_MODULE");
-
-        int this_class = in.readUnsignedShort();
-        String mn = cpool.getClassName(this_class);
-        if (!"module-info".equals(mn))
-            throw invalidModuleDescriptor("this_class should be module-info");
-
-        int super_class = in.readUnsignedShort();
-        if (super_class > 0)
-            throw invalidModuleDescriptor("bad #super_class");
-
-        int interfaces_count = in.readUnsignedShort();
-        if (interfaces_count > 0)
-            throw invalidModuleDescriptor("Bad #interfaces");
-
-        int fields_count = in.readUnsignedShort();
-        if (fields_count > 0)
-            throw invalidModuleDescriptor("Bad #fields");
-
-        int methods_count = in.readUnsignedShort();
-        if (methods_count > 0)
-            throw invalidModuleDescriptor("Bad #methods");
-
-        int attributes_count = in.readUnsignedShort();
-
-        // the names of the attributes found in the class file
-        Set<String> attributes = new HashSet<>();
-
-        Builder builder = null;
-        Set<String> packages = 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);
-            int length = in.readInt();
-
-            boolean added = attributes.add(attribute_name);
-            if (!added && isAttributeAtMostOnce(attribute_name)) {
-                throw invalidModuleDescriptor("More than one "
-                                              + attribute_name + " attribute");
-            }
-
-            switch (attribute_name) {
-
-                case MODULE :
-                    builder = readModuleAttribute(in, cpool);
-                    break;
-
-                case MODULE_PACKAGES :
-                    packages = readModulePackagesAttribute(in, cpool);
-                    break;
-
-                case MODULE_MAIN_CLASS :
-                    mainClass = readModuleMainClassAttribute(in, cpool);
-                    break;
-
-                case MODULE_TARGET :
-                    osValues = readModuleTargetAttribute(in, cpool);
-                    break;
-
-                case MODULE_HASHES :
-                    if (parseHashes) {
-                        hashes = readModuleHashesAttribute(in, cpool);
-                    } else {
-                        in.skipBytes(length);
-                    }
-                    break;
-
-                default:
-                    if (isAttributeDisallowed(attribute_name)) {
-                        throw invalidModuleDescriptor(attribute_name
-                                                      + " attribute not allowed");
-                    } else {
-                        in.skipBytes(length);
-                    }
-
-            }
-        }
-
-        // the Module attribute is required
-        if (builder == null) {
-            throw invalidModuleDescriptor(MODULE + " attribute not found");
-        }
-
-        // If the ModulePackages attribute is not present then the packageFinder
-        // is used to find the set of packages
-        boolean usedPackageFinder = false;
-        if (packages == null && packageFinder != null) {
-            try {
-                packages = new HashSet<>(packageFinder.get());
-            } catch (UncheckedIOException x) {
-                throw x.getCause();
-            }
-            usedPackageFinder = true;
-        }
-        if (packages != null) {
-            for (String pn : builder.exportedAndOpenPackages()) {
-                if (!packages.contains(pn)) {
-                    String tail;
-                    if (usedPackageFinder) {
-                        tail = " not found by package finder";
-                    } else {
-                        tail = " missing from ModulePackages attribute";
-                    }
-                    throw invalidModuleDescriptor("Package " + pn + tail);
-                }
-                packages.remove(pn);
-            }
-            builder.contains(packages);
-        }
-
-        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]);
-        }
-
-        ModuleDescriptor descriptor = builder.build();
-        return new Attributes(descriptor, hashes);
-    }
-
-    /**
-     * Reads the Module attribute, returning the ModuleDescriptor.Builder to
-     * build the corresponding ModuleDescriptor.
-     */
-    private Builder readModuleAttribute(DataInput in, ConstantPool cpool)
-        throws IOException
-    {
-        // module_name
-        int module_name_index = in.readUnsignedShort();
-        String mn = cpool.getModuleName(module_name_index);
-
-        Builder builder = new ModuleDescriptor.Builder(mn, /*strict*/ false);
-
-        int module_flags = in.readUnsignedShort();
-        boolean open = ((module_flags & ACC_OPEN) != 0);
-        if (open)
-            builder.open(true);
-        if ((module_flags & ACC_SYNTHETIC) != 0)
-            builder.synthetic(true);
-
-        int module_version_index = in.readUnsignedShort();
-        if (module_version_index != 0) {
-            String vs = cpool.getUtf8(module_version_index);
-            builder.version(vs);
-        }
-
-        int requires_count = in.readUnsignedShort();
-        boolean requiresJavaBase = false;
-        for (int i=0; i<requires_count; i++) {
-            int requires_index = in.readUnsignedShort();
-            String dn = cpool.getModuleName(requires_index);
-
-            int requires_flags = in.readUnsignedShort();
-            Set<Requires.Modifier> mods;
-            if (requires_flags == 0) {
-                mods = Collections.emptySet();
-            } else {
-                mods = new HashSet<>();
-                if ((requires_flags & ACC_TRANSITIVE) != 0)
-                    mods.add(Requires.Modifier.TRANSITIVE);
-                if ((requires_flags & ACC_STATIC_PHASE) != 0)
-                    mods.add(Requires.Modifier.STATIC);
-                if ((requires_flags & ACC_SYNTHETIC) != 0)
-                    mods.add(Requires.Modifier.SYNTHETIC);
-                if ((requires_flags & ACC_MANDATED) != 0)
-                    mods.add(Requires.Modifier.MANDATED);
-            }
-
-            int requires_version_index = in.readUnsignedShort();
-            Version compiledVersion = null;
-            if (requires_version_index != 0) {
-                String vs = cpool.getUtf8(requires_version_index);
-                compiledVersion = Version.parse(vs);
-            }
-
-            if (compiledVersion == null) {
-                builder.requires(mods, dn);
-            } else {
-                builder.requires(mods, dn, compiledVersion);
-            }
-
-            if (dn.equals("java.base"))
-                requiresJavaBase = true;
-        }
-        if (mn.equals("java.base")) {
-            if (requires_count > 0) {
-                throw invalidModuleDescriptor("The requires table for java.base"
-                                              + " must be 0 length");
-            }
-        } else if (!requiresJavaBase) {
-            throw invalidModuleDescriptor("The requires table must have"
-                                          + " an entry for java.base");
-        }
-
-        int exports_count = in.readUnsignedShort();
-        if (exports_count > 0) {
-            for (int i=0; i<exports_count; i++) {
-                int exports_index = in.readUnsignedShort();
-                String pkg = cpool.getPackageName(exports_index);
-
-                Set<Exports.Modifier> mods;
-                int exports_flags = in.readUnsignedShort();
-                if (exports_flags == 0) {
-                    mods = Collections.emptySet();
-                } else {
-                    mods = new HashSet<>();
-                    if ((exports_flags & ACC_SYNTHETIC) != 0)
-                        mods.add(Exports.Modifier.SYNTHETIC);
-                    if ((exports_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);
-                    for (int j=0; j<exports_to_count; j++) {
-                        int exports_to_index = in.readUnsignedShort();
-                        targets.add(cpool.getModuleName(exports_to_index));
-                    }
-                    builder.exports(mods, pkg, targets);
-                } else {
-                    builder.exports(mods, pkg);
-                }
-            }
-        }
-
-        int opens_count = in.readUnsignedShort();
-        if (opens_count > 0) {
-            if (open) {
-                throw invalidModuleDescriptor("The opens table for an open"
-                                              + " module must be 0 length");
-            }
-            for (int i=0; i<opens_count; i++) {
-                int opens_index = in.readUnsignedShort();
-                String pkg = cpool.getPackageName(opens_index);
-
-                Set<Opens.Modifier> mods;
-                int opens_flags = in.readUnsignedShort();
-                if (opens_flags == 0) {
-                    mods = Collections.emptySet();
-                } else {
-                    mods = new HashSet<>();
-                    if ((opens_flags & ACC_SYNTHETIC) != 0)
-                        mods.add(Opens.Modifier.SYNTHETIC);
-                    if ((opens_flags & ACC_MANDATED) != 0)
-                        mods.add(Opens.Modifier.MANDATED);
-                }
-
-                int open_to_count = in.readUnsignedShort();
-                if (open_to_count > 0) {
-                    Set<String> targets = new HashSet<>(open_to_count);
-                    for (int j=0; j<open_to_count; j++) {
-                        int opens_to_index = in.readUnsignedShort();
-                        targets.add(cpool.getModuleName(opens_to_index));
-                    }
-                    builder.opens(mods, pkg, targets);
-                } else {
-                    builder.opens(mods, pkg);
-                }
-            }
-        }
-
-        int uses_count = in.readUnsignedShort();
-        if (uses_count > 0) {
-            for (int i=0; i<uses_count; i++) {
-                int index = in.readUnsignedShort();
-                String sn = cpool.getClassName(index);
-                builder.uses(sn);
-            }
-        }
-
-        int provides_count = in.readUnsignedShort();
-        if (provides_count > 0) {
-            for (int i=0; i<provides_count; i++) {
-                int index = in.readUnsignedShort();
-                String sn = cpool.getClassName(index);
-                int with_count = in.readUnsignedShort();
-                List<String> providers = new ArrayList<>(with_count);
-                for (int j=0; j<with_count; j++) {
-                    index = in.readUnsignedShort();
-                    String pn = cpool.getClassName(index);
-                    providers.add(pn);
-                }
-                builder.provides(sn, providers);
-            }
-        }
-
-        return builder;
-    }
-
-    /**
-     * Reads the ModulePackages attribute
-     */
-    private Set<String> readModulePackagesAttribute(DataInput in, ConstantPool cpool)
-        throws IOException
-    {
-        int package_count = in.readUnsignedShort();
-        Set<String> packages = new HashSet<>(package_count);
-        for (int i=0; i<package_count; i++) {
-            int index = in.readUnsignedShort();
-            String pn = cpool.getPackageName(index);
-            boolean added = packages.add(pn);
-            if (!added) {
-                throw invalidModuleDescriptor("Package " + pn + " in ModulePackages"
-                                              + "attribute more than once");
-            }
-        }
-        return packages;
-    }
-
-    /**
-     * Reads the ModuleMainClass attribute
-     */
-    private String readModuleMainClassAttribute(DataInput in, ConstantPool cpool)
-        throws IOException
-    {
-        int index = in.readUnsignedShort();
-        return cpool.getClassName(index);
-    }
-
-    /**
-     * Reads the ModuleTarget attribute
-     */
-    private String[] readModuleTargetAttribute(DataInput in, ConstantPool cpool)
-        throws IOException
-    {
-        String[] values = new String[3];
-
-        int name_index = in.readUnsignedShort();
-        if (name_index != 0)
-            values[0] = cpool.getUtf8(name_index);
-
-        int arch_index = in.readUnsignedShort();
-        if (arch_index != 0)
-            values[1] = cpool.getUtf8(arch_index);
-
-        int version_index = in.readUnsignedShort();
-        if (version_index != 0)
-            values[2] = cpool.getUtf8(version_index);
-
-        return values;
-    }
-
-
-    /**
-     * Reads the ModuleHashes attribute
-     */
-    private ModuleHashes readModuleHashesAttribute(DataInput in, ConstantPool cpool)
-        throws IOException
-    {
-        int algorithm_index = in.readUnsignedShort();
-        String algorithm = cpool.getUtf8(algorithm_index);
-
-        int hash_count = in.readUnsignedShort();
-        Map<String, byte[]> map = new HashMap<>(hash_count);
-        for (int i=0; i<hash_count; i++) {
-            int module_name_index = in.readUnsignedShort();
-            String mn = cpool.getModuleName(module_name_index);
-            int hash_length = in.readUnsignedShort();
-            if (hash_length == 0) {
-                throw invalidModuleDescriptor("hash_length == 0");
-            }
-            byte[] hash = new byte[hash_length];
-            in.readFully(hash);
-            map.put(mn, hash);
-        }
-
-        return new ModuleHashes(algorithm, map);
-    }
-
-
-    /**
-     * Returns true if the given attribute can be present at most once
-     * in the class file. Returns false otherwise.
-     */
-    private static boolean isAttributeAtMostOnce(String name) {
-
-        if (name.equals(MODULE) ||
-                name.equals(SOURCE_FILE) ||
-                name.equals(SDE) ||
-                name.equals(MODULE_PACKAGES) ||
-                name.equals(MODULE_MAIN_CLASS) ||
-                name.equals(MODULE_TARGET) ||
-                name.equals(MODULE_HASHES))
-            return true;
-
-        return false;
-    }
-
-    /**
-     * 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, 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;
-        if (notAllowed == null) {
-            notAllowed = Set.of(
-                    "ConstantValue",
-                    "Code",
-                    "StackMapTable",
-                    "Exceptions",
-                    "EnclosingMethod",
-                    "Signature",
-                    "LineNumberTable",
-                    "LocalVariableTable",
-                    "LocalVariableTypeTable",
-                    "RuntimeVisibleParameterAnnotations",
-                    "RuntimeInvisibleParameterAnnotations",
-                    "RuntimeVisibleTypeAnnotations",
-                    "RuntimeInvisibleTypeAnnotations",
-                    "Synthetic",
-                    "AnnotationDefault",
-                    "BootstrapMethods",
-                    "MethodParameters");
-            predefinedNotAllowed = notAllowed;
-        }
-        return notAllowed.contains(name);
-    }
-
-    // lazily created set the pre-defined attributes that are not allowed
-    private static volatile Set<String> predefinedNotAllowed;
-
-
-    /**
-     * The constant pool in a class file.
-     */
-    private static class ConstantPool {
-        static final int CONSTANT_Utf8 = 1;
-        static final int CONSTANT_Integer = 3;
-        static final int CONSTANT_Float = 4;
-        static final int CONSTANT_Long = 5;
-        static final int CONSTANT_Double = 6;
-        static final int CONSTANT_Class = 7;
-        static final int CONSTANT_String = 8;
-        static final int CONSTANT_Fieldref = 9;
-        static final int CONSTANT_Methodref = 10;
-        static final int CONSTANT_InterfaceMethodref = 11;
-        static final int CONSTANT_NameAndType = 12;
-        static final int CONSTANT_MethodHandle = 15;
-        static final int CONSTANT_MethodType = 16;
-        static final int CONSTANT_InvokeDynamic = 18;
-        static final int CONSTANT_Module = 19;
-        static final int CONSTANT_Package = 20;
-
-        private static class Entry {
-            protected Entry(int tag) {
-                this.tag = tag;
-            }
-            final int tag;
-        }
-
-        private static class IndexEntry extends Entry {
-            IndexEntry(int tag, int index) {
-                super(tag);
-                this.index = index;
-            }
-            final int index;
-        }
-
-        private static class Index2Entry extends Entry {
-            Index2Entry(int tag, int index1, int index2) {
-                super(tag);
-                this.index1 = index1;
-                this.index2 = index2;
-            }
-            final int index1,  index2;
-        }
-
-        private static class ValueEntry extends Entry {
-            ValueEntry(int tag, Object value) {
-                super(tag);
-                this.value = value;
-            }
-            final Object value;
-        }
-
-        final Entry[] pool;
-
-        ConstantPool(DataInput in) throws IOException {
-            int count = in.readUnsignedShort();
-            pool = new Entry[count];
-
-            for (int i = 1; i < count; i++) {
-                int tag = in.readUnsignedByte();
-                switch (tag) {
-
-                    case CONSTANT_Utf8:
-                        String svalue = in.readUTF();
-                        pool[i] = new ValueEntry(tag, svalue);
-                        break;
-
-                    case CONSTANT_Class:
-                    case CONSTANT_Package:
-                    case CONSTANT_Module:
-                    case CONSTANT_String:
-                        int index = in.readUnsignedShort();
-                        pool[i] = new IndexEntry(tag, index);
-                        break;
-
-                    case CONSTANT_Double:
-                        double dvalue = in.readDouble();
-                        pool[i] = new ValueEntry(tag, dvalue);
-                        i++;
-                        break;
-
-                    case CONSTANT_Fieldref:
-                    case CONSTANT_InterfaceMethodref:
-                    case CONSTANT_Methodref:
-                    case CONSTANT_InvokeDynamic:
-                    case CONSTANT_NameAndType:
-                        int index1 = in.readUnsignedShort();
-                        int index2 = in.readUnsignedShort();
-                        pool[i] = new Index2Entry(tag, index1, index2);
-                        break;
-
-                    case CONSTANT_MethodHandle:
-                        int refKind = in.readUnsignedByte();
-                        index = in.readUnsignedShort();
-                        pool[i] = new Index2Entry(tag, refKind, index);
-                        break;
-
-                    case CONSTANT_MethodType:
-                        index = in.readUnsignedShort();
-                        pool[i] = new IndexEntry(tag, index);
-                        break;
-
-                    case CONSTANT_Float:
-                        float fvalue = in.readFloat();
-                        pool[i] = new ValueEntry(tag, fvalue);
-                        break;
-
-                    case CONSTANT_Integer:
-                        int ivalue = in.readInt();
-                        pool[i] = new ValueEntry(tag, ivalue);
-                        break;
-
-                    case CONSTANT_Long:
-                        long lvalue = in.readLong();
-                        pool[i] = new ValueEntry(tag, lvalue);
-                        i++;
-                        break;
-
-                    default:
-                        throw invalidModuleDescriptor("Bad constant pool entry: "
-                                                      + i);
-                }
-            }
-        }
-
-        String getClassName(int index) {
-            checkIndex(index);
-            Entry e = pool[index];
-            if (e.tag != CONSTANT_Class) {
-                throw invalidModuleDescriptor("CONSTANT_Class expected at entry: "
-                                              + index);
-            }
-            String value = getUtf8(((IndexEntry) e).index);
-            checkUnqualifiedName("CONSTANT_Class", index, value);
-            return value.replace('/', '.');  // internal form -> binary name
-        }
-
-        String getPackageName(int index) {
-            checkIndex(index);
-            Entry e = pool[index];
-            if (e.tag != CONSTANT_Package) {
-                throw invalidModuleDescriptor("CONSTANT_Package expected at entry: "
-                                              + index);
-            }
-            String value = getUtf8(((IndexEntry) e).index);
-            checkUnqualifiedName("CONSTANT_Package", index, value);
-            return value.replace('/', '.');  // internal form -> binary name
-        }
-
-        String getModuleName(int index) {
-            checkIndex(index);
-            Entry e = pool[index];
-            if (e.tag != CONSTANT_Module) {
-                throw invalidModuleDescriptor("CONSTANT_Module expected at entry: "
-                                              + index);
-            }
-            String value = getUtf8(((IndexEntry) e).index);
-            checkModuleName(index, value);
-            return value;
-        }
-
-        String getUtf8(int index) {
-            checkIndex(index);
-            Entry e = pool[index];
-            if (e.tag != CONSTANT_Utf8) {
-                throw invalidModuleDescriptor("CONSTANT_Utf8 expected at entry: "
-                                              + index);
-            }
-            return (String) (((ValueEntry) e).value);
-        }
-
-        void checkIndex(int index) {
-            if (index < 1 || index >= pool.length)
-                throw invalidModuleDescriptor("Index into constant pool out of range");
-        }
-
-        void checkUnqualifiedName(String what, int index, String value) {
-            int len = value.length();
-            if (len == 0) {
-                throw invalidModuleDescriptor(what + " at entry " + index
-                                              + " has zero length");
-            }
-            for (int i=0; i<len; i++) {
-                char c = value.charAt(i);
-                if (c == '.' || c == ';' || c == '[') {
-                    throw invalidModuleDescriptor(what + " at entry " + index
-                                                  + " has illegal character: '"
-                                                  + c + "'");
-                }
-            }
-        }
-
-        void checkModuleName(int index, String value) {
-            int len = value.length();
-            if (len == 0) {
-                throw invalidModuleDescriptor("Module name is zero length");
-            }
-            // TBD catch illegal characters
-        }
-    }
-
-    /**
-     * A DataInput implementation that reads from a ByteBuffer.
-     */
-    private static class DataInputWrapper implements DataInput {
-        private final ByteBuffer bb;
-
-        DataInputWrapper(ByteBuffer bb) {
-            this.bb = bb;
-        }
-
-        @Override
-        public void readFully(byte b[]) throws IOException {
-            readFully(b, 0, b.length);
-        }
-
-        @Override
-        public void readFully(byte b[], int off, int len) throws IOException {
-            try {
-                bb.get(b, off, len);
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public int skipBytes(int n) {
-            int skip = Math.min(n, bb.remaining());
-            bb.position(bb.position() + skip);
-            return skip;
-        }
-
-        @Override
-        public boolean readBoolean() throws IOException {
-            try {
-                int ch = bb.get();
-                return (ch != 0);
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public byte readByte() throws IOException {
-            try {
-                return bb.get();
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public int readUnsignedByte() throws IOException {
-            try {
-                return ((int) bb.get()) & 0xff;
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public short readShort() throws IOException {
-            try {
-                return bb.getShort();
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public int readUnsignedShort() throws IOException {
-            try {
-                return ((int) bb.getShort()) & 0xffff;
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public char readChar() throws IOException {
-            try {
-                return bb.getChar();
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public int readInt() throws IOException {
-            try {
-                return bb.getInt();
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public long readLong() throws IOException {
-            try {
-                return bb.getLong();
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public float readFloat() throws IOException {
-            try {
-                return bb.getFloat();
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public double readDouble() throws IOException {
-            try {
-                return bb.getDouble();
-            } catch (BufferUnderflowException e) {
-                throw new EOFException(e.getMessage());
-            }
-        }
-
-        @Override
-        public String readLine() {
-            throw new RuntimeException("not implemented");
-        }
-
-        @Override
-        public String readUTF() throws IOException {
-            // ### Need to measure the performance and feasibility of using
-            // the UTF-8 decoder instead.
-            return DataInputStream.readUTF(this);
-        }
-    }
-
-    /**
-     * Returns an InvalidModuleDescriptorException with the given detail
-     * message
-     */
-    private static InvalidModuleDescriptorException
-    invalidModuleDescriptor(String msg) {
-        return new InvalidModuleDescriptorException(msg);
-    }
-
-    /**
-     * Returns an InvalidModuleDescriptorException with a detail message to
-     * indicate that the class file is truncated.
-     */
-    private static InvalidModuleDescriptorException truncatedModuleDescriptor() {
-        return invalidModuleDescriptor("Truncated module-info.class");
-    }
-
-}
--- a/src/java.base/share/classes/java/lang/module/ModulePath.java	Sat Dec 10 05:22:58 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,682 +0,0 @@
-/*
- * 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
- * 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.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.UncheckedIOException;
-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.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.jar.Manifest;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.zip.ZipFile;
-
-import jdk.internal.jmod.JmodFile;
-import jdk.internal.jmod.JmodFile.Section;
-import jdk.internal.module.Checks;
-import jdk.internal.perf.PerfCounter;
-import jdk.internal.util.jar.VersionedStream;
-
-
-/**
- * A {@code ModuleFinder} that locates modules on the file system by searching
- * a sequence of directories or packaged modules.
- *
- * 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 created for link-time then it additionally locates
- * modules in JMOD files.
- */
-
-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;
-
-    // map of module name to module reference map for modules already located
-    private final Map<String, ModuleReference> cachedModules = new HashMap<>();
-
-    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);
-        }
-    }
-
-    ModulePath(Path... entries) {
-        this(JarFile.runtimeVersion(), false, entries);
-    }
-
-    @Override
-    public Optional<ModuleReference> find(String name) {
-        Objects.requireNonNull(name);
-
-        // try cached modules
-        ModuleReference m = cachedModules.get(name);
-        if (m != null)
-            return Optional.of(m);
-
-        // the module may not have been encountered yet
-        while (hasNextEntry()) {
-            scanNextEntry();
-            m = cachedModules.get(name);
-            if (m != null)
-                return Optional.of(m);
-        }
-        return Optional.empty();
-    }
-
-    @Override
-    public Set<ModuleReference> findAll() {
-        // need to ensure that all entries have been scanned
-        while (hasNextEntry()) {
-            scanNextEntry();
-        }
-        return cachedModules.values().stream().collect(Collectors.toSet());
-    }
-
-    /**
-     * Returns {@code true} if there are additional entries to scan
-     */
-    private boolean hasNextEntry() {
-        return next < entries.length;
-    }
-
-    /**
-     * Scans the next entry on the module path. A no-op if all entries have
-     * already been scanned.
-     *
-     * @throws FindException if an error occurs scanning the next entry
-     */
-    private void scanNextEntry() {
-        if (hasNextEntry()) {
-
-            long t0 = System.nanoTime();
-
-            Path entry = entries[next];
-            Map<String, ModuleReference> modules = scan(entry);
-            next++;
-
-            // update cache, ignoring duplicates
-            int initialSize = cachedModules.size();
-            for (Map.Entry<String, ModuleReference> e : modules.entrySet()) {
-                cachedModules.putIfAbsent(e.getKey(), e.getValue());
-            }
-
-            // update counters
-            int added = cachedModules.size() - initialSize;
-            moduleCount.add(added);
-
-            scanTime.addElapsedTimeFrom(t0);
-        }
-    }
-
-
-    /**
-     * Scan the given module path entry. If the entry is a directory then it is
-     * a directory of modules or an exploded module. If the entry is a regular
-     * file then it is assumed to be a packaged module.
-     *
-     * @throws FindException if an error occurs scanning the entry
-     */
-    private Map<String, ModuleReference> scan(Path entry) {
-
-        BasicFileAttributes attrs;
-        try {
-            attrs = Files.readAttributes(entry, BasicFileAttributes.class);
-        } catch (NoSuchFileException e) {
-            return Collections.emptyMap();
-        } catch (IOException ioe) {
-            throw new FindException(ioe);
-        }
-
-        try {
-
-            if (attrs.isDirectory()) {
-                Path mi = entry.resolve(MODULE_INFO);
-                if (!Files.exists(mi)) {
-                    // does not exist or unable to determine so assume a
-                    // directory of modules
-                    return scanDirectory(entry);
-                }
-            }
-
-            // packaged or exploded module
-            ModuleReference mref = readModule(entry, attrs);
-            if (mref != null) {
-                String name = mref.descriptor().name();
-                return Collections.singletonMap(name, mref);
-            } else {
-                // skipped
-                return Collections.emptyMap();
-            }
-
-        } catch (IOException ioe) {
-            throw new FindException(ioe);
-        }
-    }
-
-
-    /**
-     * Scans the given directory for packaged or exploded modules.
-     *
-     * @return a map of module name to ModuleReference for the modules found
-     *         in the directory
-     *
-     * @throws IOException if an I/O error occurs
-     * @throws FindException if an error occurs scanning the entry or the
-     *         directory contains two or more modules with the same name
-     */
-    private Map<String, ModuleReference> scanDirectory(Path dir)
-        throws IOException
-    {
-        // The map of name -> mref of modules found in this directory.
-        Map<String, ModuleReference> nameToReference = new HashMap<>();
-
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
-            for (Path entry : stream) {
-                BasicFileAttributes attrs;
-                try {
-                    attrs = Files.readAttributes(entry, BasicFileAttributes.class);
-                } catch (NoSuchFileException ignore) {
-                    // file has been removed or moved, ignore for now
-                    continue;
-                }
-
-                ModuleReference mref = readModule(entry, attrs);
-
-                // module found
-                if (mref != null) {
-                    // can have at most one version of a module in the directory
-                    String name = mref.descriptor().name();
-                    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
-                                                 + " (" + fn1 + " and " + fn2 + ")");
-                    }
-                }
-            }
-        }
-
-        return nameToReference;
-    }
-
-
-    /**
-     * Locates a packaged or exploded module, returning a {@code ModuleReference}
-     * to the module. Returns {@code null} if the entry is skipped because it is
-     * to a directory that does not contain a module-info.class or it's a hidden
-     * file.
-     *
-     * @throws IOException if an I/O error occurs
-     * @throws FindException if the file is not recognized as a module or an
-     *         error occurs parsing its module descriptor
-     */
-    private ModuleReference readModule(Path entry, BasicFileAttributes attrs)
-        throws IOException
-    {
-        try {
-
-            if (attrs.isDirectory()) {
-                return readExplodedModule(entry); // may return null
-            }
-
-            String fn = entry.getFileName().toString();
-            if (attrs.isRegularFile()) {
-                if (fn.endsWith(".jar")) {
-                    return readJar(entry);
-                } else if (fn.endsWith(".jmod")) {
-                    if (isLinkPhase)
-                        return readJMod(entry);
-                    throw new FindException("JMOD files not supported: " + entry);
-                }
-            }
-
-            // skip hidden files
-            if (fn.startsWith(".") || Files.isHidden(entry)) {
-                return null;
-            } else {
-                throw new FindException("Unrecognized module: " + entry);
-            }
-
-        } catch (InvalidModuleDescriptorException e) {
-            throw new FindException("Error reading module: " + entry, e);
-        }
-    }
-
-
-    /**
-     * 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(JmodFile jf) {
-        return jf.stream()
-            .filter(e -> e.section() == Section.CLASSES)
-            .map(JmodFile.Entry::name)
-            .map(this::toPackageName)
-            .flatMap(Optional::stream)
-            .collect(Collectors.toSet());
-    }
-
-    /**
-     * Returns a {@code ModuleReference} to a module in jmod file on the
-     * file system.
-     *
-     * @throws IOException
-     * @throws InvalidModuleDescriptorException
-     */
-    private ModuleReference readJMod(Path file) throws IOException {
-        try (JmodFile jf = new JmodFile(file)) {
-            ModuleInfo.Attributes attrs;
-            try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
-                attrs  = ModuleInfo.read(in, () -> jmodPackages(jf));
-            }
-            return ModuleReferences.newJModModule(attrs, file);
-        }
-    }
-
-
-    // -- JAR files --
-
-    private static final String SERVICES_PREFIX = "META-INF/services/";
-
-    /**
-     * Returns the service type corresponding to the name of a services
-     * configuration file if it is 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".
-     */
-    private Optional<String> toServiceName(String cf) {
-        assert cf.startsWith(SERVICES_PREFIX);
-        int index = cf.lastIndexOf("/") + 1;
-        if (index < cf.length()) {
-            String prefix = cf.substring(0, index);
-            if (prefix.equals(SERVICES_PREFIX)) {
-                String sn = cf.substring(index);
-                if (Checks.isJavaIdentifier(sn))
-                    return Optional.of(sn);
-            }
-        }
-        return Optional.empty();
-    }
-
-    /**
-     * Reads the next line from the given reader and trims it of comments and
-     * leading/trailing white space.
-     *
-     * Returns null if the reader is at EOF.
-     */
-    private String nextLine(BufferedReader reader) throws IOException {
-        String ln = reader.readLine();
-        if (ln != null) {
-            int ci = ln.indexOf('#');
-            if (ci >= 0)
-                ln = ln.substring(0, ci);
-            ln = ln.trim();
-        }
-        return ln;
-    }
-
-    /**
-     * Treat the given JAR file as a module as follows:
-     *
-     * 1. The module name (and optionally the version) is derived from the file
-     *    name of the JAR file
-     * 2. All packages are exported and open
-     * 3. It has no non-exported/non-open 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
-     *    is mapped to the module descriptor mainClass
-     */
-    private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
-        throws IOException
-    {
-        // Derive module name and version from JAR file name
-
-        String fn = jf.getName();
-        int i = fn.lastIndexOf(File.separator);
-        if (i != -1)
-            fn = fn.substring(i+1);
-
-        // drop .jar
-        String mn = fn.substring(0, fn.length()-4);
-        String vs = null;
-
-        // find first occurrence of -${NUMBER}. or -${NUMBER}$
-        Matcher matcher = Patterns.DASH_VERSION.matcher(mn);
-        if (matcher.find()) {
-            int start = matcher.start();
-
-            // attempt to parse the tail as a version string
-            try {
-                String tail = mn.substring(start+1);
-                ModuleDescriptor.Version.parse(tail);
-                vs = tail;
-            } catch (IllegalArgumentException ignore) { }
-
-            mn = mn.substring(0, start);
-        }
-
-        // finally clean up the module name
-        mn = cleanModuleName(mn);
-
-        // Builder throws IAE if module name is empty or invalid
-        ModuleDescriptor.Builder builder
-            = ModuleDescriptor.automaticModule(mn)
-                .requires(Set.of(Requires.Modifier.MANDATED), "java.base");
-        if (vs != null)
-            builder.version(vs);
-
-        // scan the names of the entries in the JAR file
-        Map<Boolean, Set<String>> map = VersionedStream.stream(jf)
-                .filter(e -> !e.isDirectory())
-                .map(JarEntry::getName)
-                .collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX),
-                                                   Collectors.toSet()));
-
-        Set<String> resources = map.get(Boolean.FALSE);
-        Set<String> configFiles = map.get(Boolean.TRUE);
-        // all packages are exported and open
-        resources.stream()
-                .map(this::toPackageName)
-                .flatMap(Optional::stream)
-                .distinct()
-                .forEach(pn -> builder.exports(pn).opens(pn));
-
-        // map names of service configuration files to service names
-        Set<String> serviceNames = configFiles.stream()
-                .map(this::toServiceName)
-                .flatMap(Optional::stream)
-                .collect(Collectors.toSet());
-
-        // parse each service configuration file
-        for (String sn : serviceNames) {
-            JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
-            List<String> providerClasses = new ArrayList<>();
-            try (InputStream in = jf.getInputStream(entry)) {
-                BufferedReader reader
-                    = new BufferedReader(new InputStreamReader(in, "UTF-8"));
-                String cn;
-                while ((cn = nextLine(reader)) != null) {
-                    if (cn.length() > 0) {
-                        providerClasses.add(cn);
-                    }
-                }
-            }
-            if (!providerClasses.isEmpty())
-                builder.provides(sn, providerClasses);
-        }
-
-        // Main-Class attribute if it exists
-        Manifest man = jf.getManifest();
-        if (man != null) {
-            Attributes attrs = man.getMainAttributes();
-            String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
-            if (mainClass != null)
-                builder.mainClass(mainClass.replace("/", "."));
-        }
-
-        return builder.build();
-    }
-
-    /**
-     * Patterns used to derive the module name from a JAR file name.
-     */
-    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("^\\.");
-        static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
-    }
-
-    /**
-     * 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(".");
-
-        // collapse repeating dots
-        mn = Patterns.REPEATING_DOTS.matcher(mn).replaceAll(".");
-
-        // drop leading dots
-        if (mn.length() > 0 && mn.charAt(0) == '.')
-            mn = Patterns.LEADING_DOTS.matcher(mn).replaceAll("");
-
-        // drop trailing dots
-        int len = mn.length();
-        if (len > 0 && mn.charAt(len-1) == '.')
-            mn = Patterns.TRAILING_DOTS.matcher(mn).replaceAll("");
-
-        return mn;
-    }
-
-    private Set<String> jarPackages(JarFile jf) {
-        return VersionedStream.stream(jf)
-                .filter(e -> !e.isDirectory())
-                .map(JarEntry::getName)
-                .map(this::toPackageName)
-                .flatMap(Optional::stream)
-                .collect(Collectors.toSet());
-    }
-
-    /**
-     * Returns a {@code ModuleReference} to a module in modular JAR file on
-     * the file system.
-     *
-     * @throws IOException
-     * @throws FindException
-     * @throws InvalidModuleDescriptorException
-     */
-    private ModuleReference readJar(Path file) throws IOException {
-        try (JarFile jf = new JarFile(file.toFile(),
-                                      true,               // verify
-                                      ZipFile.OPEN_READ,
-                                      releaseVersion))
-        {
-            ModuleInfo.Attributes attrs;
-            JarEntry entry = jf.getJarEntry(MODULE_INFO);
-            if (entry == null) {
-
-                // no module-info.class so treat it as automatic module
-                try {
-                    ModuleDescriptor md = deriveModuleDescriptor(jf);
-                    attrs = new ModuleInfo.Attributes(md, null);
-                } catch (IllegalArgumentException iae) {
-                    throw new FindException(
-                        "Unable to derive module descriptor for: "
-                        + jf.getName(), iae);
-                }
-
-            } else {
-                attrs = ModuleInfo.read(jf.getInputStream(entry),
-                                        () -> jarPackages(jf));
-            }
-
-            return ModuleReferences.newJarModule(attrs, file);
-        }
-    }
-
-
-    // -- exploded directories --
-
-    private Set<String> explodedPackages(Path dir) {
-        try {
-            return Files.find(dir, Integer.MAX_VALUE,
-                              ((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);
-        }
-    }
-
-    /**
-     * Returns a {@code ModuleReference} to an exploded module on the file
-     * system or {@code null} if {@code module-info.class} not found.
-     *
-     * @throws IOException
-     * @throws InvalidModuleDescriptorException
-     */
-    private ModuleReference readExplodedModule(Path dir) throws IOException {
-        Path mi = dir.resolve(MODULE_INFO);
-        ModuleInfo.Attributes attrs;
-        try (InputStream in = Files.newInputStream(mi)) {
-            attrs = ModuleInfo.read(new BufferedInputStream(in),
-                                    () -> explodedPackages(dir));
-        } catch (NoSuchFileException e) {
-            // for now
-            return null;
-        }
-        return ModuleReferences.newExplodedModule(attrs, 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();
-        }
-
-        String pn = name.substring(0, index).replace('/', '.');
-        if (Checks.isJavaIdentifier(pn)) {
-            return Optional.of(pn);
-        } else {
-            // not a valid package name
-            return Optional.empty();
-        }
-    }
-
-    /**
-     * 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 {
-            // not a valid package name
-            return Optional.empty();
-        }
-    }
-
-    private static final PerfCounter scanTime
-        = PerfCounter.newPerfCounter("jdk.module.finder.modulepath.scanTime");
-    private static final PerfCounter moduleCount
-        = PerfCounter.newPerfCounter("jdk.module.finder.modulepath.modules");
-}
--- a/src/java.base/share/classes/java/lang/module/ModuleReference.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/ModuleReference.java	Mon Dec 12 14:31:55 2016 +0000
@@ -26,108 +26,50 @@
 package java.lang.module;
 
 import java.io.IOException;
-import java.io.UncheckedIOException;
 import java.net.URI;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.function.Supplier;
-
-import jdk.internal.module.ModuleHashes;
-import jdk.internal.module.ModuleHashes.HashSupplier;
 
 
 /**
  * A reference to a module's content.
  *
- * <p> A module reference contains the module's descriptor and its location, if
- * known.  It also has the ability to create a {@link ModuleReader} in order to
- * access the module's content, which may be inside the Java run-time system
- * itself or in an artifact such as a modular JAR file.
+ * <p> A module reference is a concrete implementation of this class that
+ * implements the abstract methods defined by this class. It contains the
+ * module's descriptor and its location, if known.  It also has the ability to
+ * create a {@link ModuleReader} in order to access the module's content, which
+ * may be inside the Java run-time system itself or in an artifact such as a
+ * modular JAR file.
  *
  * @see ModuleFinder
  * @see ModuleReader
  * @since 9
  */
 
-public final class ModuleReference {
+public abstract class ModuleReference {
 
     private final ModuleDescriptor descriptor;
     private final URI location;
-    private final Supplier<ModuleReader> readerSupplier;
-
-    // true if this is a reference to a patched module
-    private boolean patched;
-
-    // the hashes of other modules recorded in this module
-    private final ModuleHashes recordedHashes;
-
-    // the function that computes the hash of this module
-    private final HashSupplier hasher;
-
-    // cached hash of this module to avoid needing to compute it many times
-    private byte[] cachedHash;
 
     /**
      * Constructs a new instance of this class.
-     */
-    ModuleReference(ModuleDescriptor descriptor,
-                    URI location,
-                    Supplier<ModuleReader> readerSupplier,
-                    boolean patched,
-                    ModuleHashes recordedHashes,
-                    HashSupplier hasher)
-
-    {
-        this.descriptor = Objects.requireNonNull(descriptor);
-        this.location = location;
-        this.readerSupplier = Objects.requireNonNull(readerSupplier);
-        this.patched = patched;
-        this.recordedHashes = recordedHashes;
-        this.hasher = hasher;
-    }
-
-    /**
-     * Constructs a new instance of this class.
-     */
-    ModuleReference(ModuleDescriptor descriptor,
-                    URI location,
-                    Supplier<ModuleReader> readerSupplier,
-                    ModuleHashes recordedHashes,
-                    HashSupplier hasher)
-
-    {
-        this(descriptor, location, readerSupplier, false, recordedHashes, hasher);
-    }
-
-    /**
-     * Constructs a new instance of this class.
-     *
-     * <p> The {@code readSupplier} parameter is the supplier of the {@link
-     * ModuleReader} that may be used to read the module content. Its {@link
-     * Supplier#get() get()} method throws {@link UncheckedIOException} if an
-     * I/O error occurs opening the module content. The {@code get()} method
-     * throws {@link SecurityException} if opening the module is denied by the
-     * security manager.
      *
      * @param descriptor
      *        The module descriptor
      * @param location
      *        The module location or {@code null} if not known
-     * @param readerSupplier
-     *        The {@code Supplier} of the {@code ModuleReader}
      */
-    public ModuleReference(ModuleDescriptor descriptor,
-                           URI location,
-                           Supplier<ModuleReader> readerSupplier)
-    {
-        this(descriptor,location, readerSupplier, false, null, null);
+    protected ModuleReference(ModuleDescriptor descriptor, URI location) {
+        this.descriptor = Objects.requireNonNull(descriptor);
+        this.location = location;
     }
+
     /**
      * Returns the module descriptor.
      *
      * @return The module descriptor
      */
-    public ModuleDescriptor descriptor() {
+    public final ModuleDescriptor descriptor() {
         return descriptor;
     }
 
@@ -142,17 +84,13 @@
      *
      * @return The location or an empty {@code Optional} if not known
      */
-    public Optional<URI> location() {
+    public final Optional<URI> location() {
         return Optional.ofNullable(location);
     }
 
     /**
      * Opens the module content for reading.
      *
-     * <p> This method opens the module content by invoking the {@link
-     * Supplier#get() get()} method of the {@code readSupplier} specified at
-     * construction time. </p>
-     *
      * @return A {@code ModuleReader} to read the module
      *
      * @throws IOException
@@ -160,122 +98,5 @@
      * @throws SecurityException
      *         If denied by the security manager
      */
-    public ModuleReader open() throws IOException {
-        try {
-            return readerSupplier.get();
-        } catch (UncheckedIOException e) {
-            throw e.getCause();
-        }
-
-    }
-
-    /**
-     * Returns {@code true} if this module has been patched via --patch-module.
-     */
-    boolean isPatched() {
-        return patched;
-    }
-
-    /**
-     * Returns the hashes recorded in this module or {@code null} if there
-     * are no hashes recorded.
-     */
-    ModuleHashes recordedHashes() {
-        return recordedHashes;
-    }
-
-    /**
-     * Returns the supplier that computes the hash of this module.
-     */
-    HashSupplier hasher() {
-        return hasher;
-    }
-
-    /**
-     * Computes the hash of this module. Returns {@code null} if the hash
-     * cannot be computed.
-     *
-     * @throws java.io.UncheckedIOException if an I/O error occurs
-     */
-    byte[] computeHash(String algorithm) {
-        byte[] result = cachedHash;
-        if (result != null)
-            return result;
-        if (hasher == null)
-            return null;
-        cachedHash = result = hasher.generate(algorithm);
-        return result;
-    }
-
-    /**
-     * Computes a hash code for this module reference.
-     *
-     * <p> The hash code is based upon the components of the reference, and
-     * satisfies the general contract of the {@link Object#hashCode
-     * Object.hashCode} method. </p>
-     *
-     * @return The hash-code value for this module reference
-     */
-    @Override
-    public int hashCode() {
-        int hc = hash;
-        if (hc == 0) {
-            hc = descriptor.hashCode();
-            hc = 43 * hc + readerSupplier.hashCode();
-            hc = 43 * hc + Objects.hashCode(location);
-            hc = 43 * hc + Boolean.hashCode(patched);
-            hc = 43 * hc * Objects.hashCode(recordedHashes);
-            hc = 43 * hc + Objects.hashCode(hasher);
-            if (hc == 0)
-                hc = -1;
-            hash = hc;
-        }
-        return hc;
-    }
-
-    private int hash;
-
-    /**
-     * Tests this module reference for equality with the given object.
-     *
-     * <p> If the given object is not a {@code ModuleReference} then this
-     * method returns {@code false}. Two module references are equal if their
-     * module descriptors are equal, their locations are equal or both unknown,
-     * and were created with equal supplier objects to access the module
-     * content. </p>
-     *
-     * <p> This method satisfies the general contract of the {@link
-     * java.lang.Object#equals(Object) Object.equals} method. </p>
-     *
-     * @param   ob
-     *          the object to which this object is to be compared
-     *
-     * @return  {@code true} if, and only if, the given object is a module
-     *          reference that is equal to this module reference
-     */
-    @Override
-    public boolean equals(Object ob) {
-        if (!(ob instanceof ModuleReference))
-            return false;
-        ModuleReference that = (ModuleReference)ob;
-
-        return Objects.equals(this.descriptor, that.descriptor)
-                && Objects.equals(this.location, that.location)
-                && Objects.equals(this.readerSupplier, that.readerSupplier)
-                && this.patched == that.patched
-                && Objects.equals(this.recordedHashes, that.recordedHashes)
-                && Objects.equals(this.hasher, that.hasher);
-    }
-
-    /**
-     * Returns a string describing this module reference.
-     *
-     * @return A string describing this module reference
-     */
-    @Override
-    public String toString() {
-        return ("[module " + descriptor().name()
-                + ", location=" + location + "]");
-    }
-
+    public abstract ModuleReader open() throws IOException;
 }
--- a/src/java.base/share/classes/java/lang/module/ModuleReferences.java	Sat Dec 10 05:22:58 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,447 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package java.lang.module;
-
-import java.io.File;
-import java.io.IOError;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.function.Supplier;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.zip.ZipFile;
-
-import jdk.internal.jmod.JmodFile;
-import jdk.internal.misc.JavaLangAccess;
-import jdk.internal.misc.SharedSecrets;
-import jdk.internal.module.ModuleBootstrap;
-import jdk.internal.module.ModuleHashes;
-import jdk.internal.module.ModuleHashes.HashSupplier;
-import jdk.internal.module.ModulePatcher;
-import jdk.internal.util.jar.VersionedStream;
-import sun.net.www.ParseUtil;
-
-
-/**
- * A factory for creating ModuleReference implementations where the modules are
- * packaged as modular JAR file, JMOD files or where the modules are exploded
- * on the file system.
- */
-
-class ModuleReferences {
-
-    private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
-
-    private ModuleReferences() { }
-
-    /**
-     * Creates a ModuleReference to a module or to patched module when
-     * creating modules for the boot Layer and --patch-module is specified.
-     */
-    private static ModuleReference newModule(ModuleInfo.Attributes attrs,
-                                             URI uri,
-                                             Supplier<ModuleReader> supplier,
-                                             HashSupplier hasher) {
-
-        ModuleReference mref = new ModuleReference(attrs.descriptor(),
-                                                   uri,
-                                                   supplier,
-                                                   attrs.recordedHashes(),
-                                                   hasher);
-        if (JLA.getBootLayer() == null)
-            mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
-
-        return mref;
-    }
-
-    /**
-     * Creates a ModuleReference to a module packaged as a modular JAR.
-     */
-    static ModuleReference newJarModule(ModuleInfo.Attributes attrs, Path file) {
-        URI uri = file.toUri();
-        Supplier<ModuleReader> supplier = () -> new JarModuleReader(file, uri);
-        HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a);
-        return newModule(attrs, uri, supplier, hasher);
-    }
-
-    /**
-     * Creates a ModuleReference to a module packaged as a JMOD.
-     */
-    static ModuleReference newJModModule(ModuleInfo.Attributes attrs, Path file) {
-        URI uri = file.toUri();
-        Supplier<ModuleReader> supplier = () -> new JModModuleReader(file, uri);
-        HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a);
-        return newModule(attrs, uri, supplier, hasher);
-    }
-
-    /**
-     * Creates a ModuleReference to an exploded module.
-     */
-    static ModuleReference newExplodedModule(ModuleInfo.Attributes attrs, Path dir) {
-        Supplier<ModuleReader> supplier = () -> new ExplodedModuleReader(dir);
-        return newModule(attrs, dir.toUri(), supplier, null);
-    }
-
-
-    /**
-     * A base module reader that encapsulates machinery required to close the
-     * module reader safely.
-     */
-    static abstract class SafeCloseModuleReader implements ModuleReader {
-
-        // RW lock to support safe close
-        private final ReadWriteLock lock = new ReentrantReadWriteLock();
-        private final Lock readLock = lock.readLock();
-        private final Lock writeLock = lock.writeLock();
-        private boolean closed;
-
-        SafeCloseModuleReader() { }
-
-        /**
-         * Returns a URL to  resource. This method is invoked by the find
-         * method to do the actual work of finding the resource.
-         */
-        abstract Optional<URI> implFind(String name) throws IOException;
-
-        /**
-         * Returns an input stream for reading a resource. This method is
-         * invoked by the open method to do the actual work of opening
-         * an input stream to the resource.
-         */
-        abstract Optional<InputStream> implOpen(String name) throws IOException;
-
-        /**
-         * Returns a stream of the names of resources in the module. This
-         * method is invoked by the list method to do the actual work of
-         * creating the stream.
-         */
-        abstract Stream<String> implList() throws IOException;
-
-        /**
-         * Closes the module reader. This method is invoked by close to do the
-         * actual work of closing the module reader.
-         */
-        abstract void implClose() throws IOException;
-
-        @Override
-        public final Optional<URI> find(String name) throws IOException {
-            readLock.lock();
-            try {
-                if (!closed) {
-                    return implFind(name);
-                } else {
-                    throw new IOException("ModuleReader is closed");
-                }
-            } finally {
-                readLock.unlock();
-            }
-        }
-
-
-        @Override
-        public final Optional<InputStream> open(String name) throws IOException {
-            readLock.lock();
-            try {
-                if (!closed) {
-                    return implOpen(name);
-                } else {
-                    throw new IOException("ModuleReader is closed");
-                }
-            } finally {
-                readLock.unlock();
-            }
-        }
-
-        @Override
-        public final Stream<String> list() throws IOException {
-            readLock.lock();
-            try {
-                if (!closed) {
-                    return implList();
-                } else {
-                    throw new IOException("ModuleReader is closed");
-                }
-            } finally {
-                readLock.unlock();
-            }
-        }
-
-        @Override
-        public final void close() throws IOException {
-            writeLock.lock();
-            try {
-                if (!closed) {
-                    closed = true;
-                    implClose();
-                }
-            } finally {
-                writeLock.unlock();
-            }
-        }
-    }
-
-
-    /**
-     * A ModuleReader for a modular JAR file.
-     */
-    static class JarModuleReader extends SafeCloseModuleReader {
-        private final JarFile jf;
-        private final URI uri;
-
-        static JarFile newJarFile(Path path) {
-            try {
-                return new JarFile(path.toFile(),
-                                   true,               // verify
-                                   ZipFile.OPEN_READ,
-                                   JarFile.runtimeVersion());
-            } catch (IOException ioe) {
-                throw new UncheckedIOException(ioe);
-            }
-        }
-
-        JarModuleReader(Path path, URI uri) {
-            this.jf = newJarFile(path);
-            this.uri = uri;
-        }
-
-        private JarEntry getEntry(String name) {
-            return jf.getJarEntry(Objects.requireNonNull(name));
-        }
-
-        @Override
-        Optional<URI> implFind(String name) throws IOException {
-            JarEntry je = getEntry(name);
-            if (je != null) {
-                if (jf.isMultiRelease())
-                    name = SharedSecrets.javaUtilJarAccess().getRealName(jf, je);
-                String encodedPath = ParseUtil.encodePath(name, false);
-                String uris = "jar:" + uri + "!/" + encodedPath;
-                return Optional.of(URI.create(uris));
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        Optional<InputStream> implOpen(String name) throws IOException {
-            JarEntry je = getEntry(name);
-            if (je != null) {
-                return Optional.of(jf.getInputStream(je));
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        Stream<String> implList() throws IOException {
-            // take snapshot to avoid async close
-            List<String> names = VersionedStream.stream(jf)
-                    .filter(e -> !e.isDirectory())
-                    .map(JarEntry::getName)
-                    .collect(Collectors.toList());
-            return names.stream();
-        }
-
-        @Override
-        void implClose() throws IOException {
-            jf.close();
-        }
-    }
-
-
-    /**
-     * A ModuleReader for a JMOD file.
-     */
-    static class JModModuleReader extends SafeCloseModuleReader {
-        private final JmodFile jf;
-        private final URI uri;
-
-        static JmodFile newJmodFile(Path path) {
-            try {
-                return new JmodFile(path);
-            } catch (IOException ioe) {
-                throw new UncheckedIOException(ioe);
-            }
-        }
-
-        JModModuleReader(Path path, URI uri) {
-            this.jf = newJmodFile(path);
-            this.uri = uri;
-        }
-
-        private JmodFile.Entry getEntry(String name) {
-            Objects.requireNonNull(name);
-            return jf.getEntry(JmodFile.Section.CLASSES, name);
-        }
-
-        @Override
-        Optional<URI> implFind(String name) {
-            JmodFile.Entry je = getEntry(name);
-            if (je != null) {
-                String encodedPath = ParseUtil.encodePath(name, false);
-                String uris = "jmod:" + uri + "!/" + encodedPath;
-                return Optional.of(URI.create(uris));
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        Optional<InputStream> implOpen(String name) throws IOException {
-            JmodFile.Entry je = getEntry(name);
-            if (je != null) {
-                return Optional.of(jf.getInputStream(je));
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        Stream<String> implList() throws IOException {
-            // take snapshot to avoid async close
-            List<String> names = jf.stream()
-                    .filter(e -> e.section() == JmodFile.Section.CLASSES)
-                    .map(JmodFile.Entry::name)
-                    .collect(Collectors.toList());
-            return names.stream();
-        }
-
-        @Override
-        void implClose() throws IOException {
-            jf.close();
-        }
-    }
-
-
-    /**
-     * A ModuleReader for an exploded module.
-     */
-    static class ExplodedModuleReader implements ModuleReader {
-        private final Path dir;
-        private volatile boolean closed;
-
-        ExplodedModuleReader(Path dir) {
-            this.dir = dir;
-
-            // when running with a security manager then check that the caller
-            // has access to the directory.
-            SecurityManager sm = System.getSecurityManager();
-            if (sm != null) {
-                boolean unused = Files.isDirectory(dir);
-            }
-        }
-
-        /**
-         * Returns a Path to access to the given resource.
-         */
-        private Path toPath(String name) {
-            Path path = Paths.get(name.replace('/', File.separatorChar));
-            if (path.getRoot() == null) {
-                return dir.resolve(path);
-            } else {
-                // drop the root component so that the resource is
-                // located relative to the module directory
-                int n = path.getNameCount();
-                return (n > 0) ? dir.resolve(path.subpath(0, n)) : null;
-            }
-        }
-
-        /**
-         * Throws IOException if the module reader is closed;
-         */
-        private void ensureOpen() throws IOException {
-            if (closed) throw new IOException("ModuleReader is closed");
-        }
-
-        @Override
-        public Optional<URI> find(String name) throws IOException {
-            ensureOpen();
-            Path path = toPath(name);
-            if (path != null && Files.isRegularFile(path)) {
-                try {
-                    return Optional.of(path.toUri());
-                } catch (IOError e) {
-                    throw (IOException) e.getCause();
-                }
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        public Optional<InputStream> open(String name) throws IOException {
-            ensureOpen();
-            Path path = toPath(name);
-            if (path != null && Files.isRegularFile(path)) {
-                return Optional.of(Files.newInputStream(path));
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        public Optional<ByteBuffer> read(String name) throws IOException {
-            ensureOpen();
-            Path path = toPath(name);
-            if (path != null && Files.isRegularFile(path)) {
-                return Optional.of(ByteBuffer.wrap(Files.readAllBytes(path)));
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        public Stream<String> list() throws IOException {
-            ensureOpen();
-            // sym links not followed
-            return Files.find(dir, Integer.MAX_VALUE,
-                              (path, attrs) -> attrs.isRegularFile())
-                    .map(f -> dir.relativize(f)
-                                 .toString()
-                                 .replace(File.separatorChar, '/'));
-        }
-
-        @Override
-        public void close() {
-            closed = true;
-        }
-    }
-
-}
--- a/src/java.base/share/classes/java/lang/module/Resolver.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/Resolver.java	Mon Dec 12 14:31:55 2016 +0000
@@ -46,6 +46,7 @@
 import java.util.stream.Collectors;
 
 import jdk.internal.module.ModuleHashes;
+import jdk.internal.module.ModuleReferenceImpl;
 
 /**
  * The resolver used by {@link Configuration#resolveRequires} and
@@ -438,22 +439,32 @@
      */
     private void checkHashes() {
         for (ModuleReference mref : nameToReference.values()) {
+
             // get the recorded hashes, if any
-            ModuleHashes hashes = mref.recordedHashes();
+            if (!(mref instanceof ModuleReferenceImpl))
+                continue;
+            ModuleHashes hashes = ((ModuleReferenceImpl)mref).recordedHashes();
             if (hashes == null)
                 continue;
 
             ModuleDescriptor descriptor = mref.descriptor();
             String algorithm = hashes.algorithm();
             for (String dn : hashes.names()) {
-                ModuleReference other = nameToReference.get(dn);
-                if (other == null) {
+                ModuleReference mref2 = nameToReference.get(dn);
+                if (mref2 == null) {
                     ResolvedModule resolvedModule = findInParent(dn);
                     if (resolvedModule != null)
-                        other = resolvedModule.reference();
+                        mref2 = resolvedModule.reference();
+                }
+                if (mref2 == null)
+                    continue;
+
+                if (!(mref2 instanceof ModuleReferenceImpl)) {
+                    fail("Unable to compute the hash of module %s", dn);
                 }
 
                 // skip checking the hash if the module has been patched
+                ModuleReferenceImpl other = (ModuleReferenceImpl)mref2;
                 if (other != null && !other.isPatched()) {
                     byte[] recordedHash = hashes.hashFor(dn);
                     byte[] actualHash = other.computeHash(algorithm);
--- a/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java	Sat Dec 10 05:22:58 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,434 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package java.lang.module;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
-import java.net.URI;
-import java.net.URLConnection;
-import java.nio.ByteBuffer;
-import java.util.ArrayDeque;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-import jdk.internal.jimage.ImageLocation;
-import jdk.internal.jimage.ImageReader;
-import jdk.internal.jimage.ImageReaderFactory;
-import jdk.internal.misc.JavaNetUriAccess;
-import jdk.internal.misc.SharedSecrets;
-import jdk.internal.module.ModuleBootstrap;
-import jdk.internal.module.ModuleHashes;
-import jdk.internal.module.ModuleHashes.HashSupplier;
-import jdk.internal.module.SystemModules;
-import jdk.internal.module.ModulePatcher;
-import jdk.internal.perf.PerfCounter;
-
-/**
- * A {@code ModuleFinder} that finds modules that are linked into the
- * run-time image.
- *
- * The modules linked into the run-time image are assumed to have the
- * Packages attribute.
- */
-
-class SystemModuleFinder implements ModuleFinder {
-
-    private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();
-
-    private static final PerfCounter initTime
-        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.initTime");
-    private static final PerfCounter moduleCount
-        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.modules");
-    private static final PerfCounter packageCount
-        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.packages");
-    private static final PerfCounter exportsCount
-        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.exports");
-    // ImageReader used to access all modules in the image
-    private static final ImageReader imageReader;
-
-    // the set of modules in the run-time image
-    private static final Set<ModuleReference> modules;
-
-    // maps module name to module reference
-    private static final Map<String, ModuleReference> nameToModule;
-
-    /**
-     * For now, the module references are created eagerly on the assumption
-     * that service binding will require all modules to be located.
-     */
-    static {
-        long t0 = System.nanoTime();
-        imageReader = ImageReaderFactory.getImageReader();
-
-        String[] names = moduleNames();
-        ModuleDescriptor[] descriptors = descriptors(names);
-
-        int n = names.length;
-        moduleCount.add(n);
-
-        ModuleReference[] mods = new ModuleReference[n];
-
-        @SuppressWarnings(value = {"rawtypes", "unchecked"})
-        Entry<String, ModuleReference>[] map
-            = (Entry<String, ModuleReference>[])new Entry[n];
-
-        for (int i = 0; i < n; i++) {
-            ModuleDescriptor md = descriptors[i];
-
-            // create the ModuleReference
-            ModuleReference mref = toModuleReference(md);
-
-            mods[i] = mref;
-            map[i] = Map.entry(names[i], mref);
-
-            // counters
-            packageCount.add(md.packages().size());
-            exportsCount.add(md.exports().size());
-        }
-
-        modules = Set.of(mods);
-        nameToModule = Map.ofEntries(map);
-
-        initTime.addElapsedTimeFrom(t0);
-    }
-
-    /*
-     * Returns an array of ModuleDescriptor of the given module names.
-     *
-     * This obtains ModuleDescriptors from SystemModules class that is generated
-     * from the jlink system-modules plugin.  ModuleDescriptors have already
-     * been validated at link time.
-     *
-     * If java.base is patched, or fastpath is disabled for troubleshooting
-     * purpose, it will fall back to find system modules via jrt file system.
-     */
-    private static ModuleDescriptor[] descriptors(String[] names) {
-        // fastpath is enabled by default.
-        // It can be disabled for troubleshooting purpose.
-        boolean disabled =
-            System.getProperty("jdk.system.module.finder.disabledFastPath") != null;
-
-        // fast loading of ModuleDescriptor of system modules
-        if (isFastPathSupported() && !disabled)
-            return SystemModules.modules();
-
-        // if fast loading of ModuleDescriptors is disabled
-        // fallback to read module-info.class
-        ModuleDescriptor[] descriptors = new ModuleDescriptor[names.length];
-        for (int i = 0; i < names.length; i++) {
-            String mn = names[i];
-            ImageLocation loc = imageReader.findLocation(mn, "module-info.class");
-            descriptors[i] = ModuleDescriptor.read(imageReader.getResourceBuffer(loc));
-        }
-        return descriptors;
-    }
-
-    private static boolean isFastPathSupported() {
-       return SystemModules.MODULE_NAMES.length > 0;
-    }
-
-    private static String[] moduleNames() {
-        if (isFastPathSupported())
-            // module names recorded at link time
-            return SystemModules.MODULE_NAMES;
-
-        // this happens when java.base is patched with java.base
-        // from an exploded image
-        return imageReader.getModuleNames();
-    }
-
-    private static ModuleReference toModuleReference(ModuleDescriptor md) {
-        String mn = md.name();
-        URI uri = JNUA.create("jrt", "/".concat(mn));
-
-        Supplier<ModuleReader> readerSupplier = new Supplier<>() {
-            @Override
-            public ModuleReader get() {
-                return new ImageModuleReader(mn, uri);
-            }
-        };
-
-        ModuleReference mref = new ModuleReference(md, uri, readerSupplier);
-
-        // may need a reference to a patched module if --patch-module specified
-        mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
-
-        return mref;
-    }
-
-    private static HashSupplier hashSupplier(int index, String name) {
-        if (isFastPathSupported()) {
-            return new HashSupplier() {
-                @Override
-                public byte[] generate(String algorithm) {
-                    return SystemModules.MODULES_TO_HASH[index];
-                }
-            };
-        } else {
-            return Hashes.hashFor(name);
-        }
-    }
-
-    /*
-     * This helper class is only used when SystemModules is patched.
-     * It will get the recorded hashes from module-info.class.
-     */
-    private static class Hashes {
-        static Map<String, byte[]> hashes = new HashMap<>();
-
-        static void add(ModuleHashes recordedHashes) {
-            hashes.putAll(recordedHashes.hashes());
-        }
-
-        static HashSupplier hashFor(String name) {
-            if (!hashes.containsKey(name))
-                return null;
-
-            return new HashSupplier() {
-                @Override
-                public byte[] generate(String algorithm) {
-                    return hashes.get(name);
-                }
-            };
-        }
-    }
-
-    SystemModuleFinder() { }
-
-    @Override
-    public Optional<ModuleReference> find(String name) {
-        Objects.requireNonNull(name);
-        return Optional.ofNullable(nameToModule.get(name));
-    }
-
-    @Override
-    public Set<ModuleReference> findAll() {
-        return modules;
-    }
-
-
-    /**
-     * A ModuleReader for reading resources from a module linked into the
-     * run-time image.
-     */
-    static class ImageModuleReader implements ModuleReader {
-        private final String module;
-        private volatile boolean closed;
-
-        /**
-         * If there is a security manager set then check permission to
-         * connect to the run-time image.
-         */
-        private static void checkPermissionToConnect(URI uri) {
-            SecurityManager sm = System.getSecurityManager();
-            if (sm != null) {
-                try {
-                    URLConnection uc = uri.toURL().openConnection();
-                    sm.checkPermission(uc.getPermission());
-                } catch (IOException ioe) {
-                    throw new UncheckedIOException(ioe);
-                }
-            }
-        }
-
-        ImageModuleReader(String module, URI uri) {
-            checkPermissionToConnect(uri);
-            this.module = module;
-        }
-
-        /**
-         * Returns the ImageLocation for the given resource, {@code null}
-         * if not found.
-         */
-        private ImageLocation findImageLocation(String name) throws IOException {
-            Objects.requireNonNull(name);
-            if (closed)
-                throw new IOException("ModuleReader is closed");
-            if (imageReader != null) {
-                return imageReader.findLocation(module, name);
-            } else {
-                // not an images build
-                return null;
-            }
-        }
-
-        @Override
-        public Optional<URI> find(String name) throws IOException {
-            ImageLocation location = findImageLocation(name);
-            if (location != null) {
-                URI u = URI.create("jrt:/" + module + "/" + name);
-                return Optional.of(u);
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        public Optional<InputStream> open(String name) throws IOException {
-            return read(name).map(this::toInputStream);
-        }
-
-        private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
-            try {
-                int rem = bb.remaining();
-                byte[] bytes = new byte[rem];
-                bb.get(bytes);
-                return new ByteArrayInputStream(bytes);
-            } finally {
-                release(bb);
-            }
-        }
-
-        @Override
-        public Optional<ByteBuffer> read(String name) throws IOException {
-            ImageLocation location = findImageLocation(name);
-            if (location != null) {
-                return Optional.of(imageReader.getResourceBuffer(location));
-            } else {
-                return Optional.empty();
-            }
-        }
-
-        @Override
-        public void release(ByteBuffer bb) {
-            Objects.requireNonNull(bb);
-            ImageReader.releaseByteBuffer(bb);
-        }
-
-        @Override
-        public Stream<String> list() throws IOException {
-            if (closed)
-                throw new IOException("ModuleReader is closed");
-
-            Spliterator<String> s = new ModuleContentSpliterator(module);
-            return StreamSupport.stream(s, false);
-        }
-
-        @Override
-        public void close() {
-            // nothing else to do
-            closed = true;
-        }
-    }
-
-    /**
-     * A Spliterator for traversing the resources of a module linked into the
-     * run-time image.
-     */
-    static class ModuleContentSpliterator implements Spliterator<String> {
-        final String moduleRoot;
-        final Deque<ImageReader.Node> stack;
-        Iterator<ImageReader.Node> iterator;
-
-        ModuleContentSpliterator(String module) throws IOException {
-            moduleRoot = "/modules/" + module;
-            stack = new ArrayDeque<>();
-
-            // push the root node to the stack to get started
-            ImageReader.Node dir = imageReader.findNode(moduleRoot);
-            if (dir == null || !dir.isDirectory())
-                throw new IOException(moduleRoot + " not a directory");
-            stack.push(dir);
-            iterator = Collections.emptyIterator();
-        }
-
-        /**
-         * Returns the name of the next non-directory node or {@code null} if
-         * there are no remaining nodes to visit.
-         */
-        private String next() throws IOException {
-            for (;;) {
-                while (iterator.hasNext()) {
-                    ImageReader.Node node = iterator.next();
-                    String name = node.getName();
-                    if (node.isDirectory()) {
-                        // build node
-                        ImageReader.Node dir = imageReader.findNode(name);
-                        assert dir.isDirectory();
-                        stack.push(dir);
-                    } else {
-                        // strip /modules/$MODULE/ prefix
-                        return name.substring(moduleRoot.length() + 1);
-                    }
-                }
-
-                if (stack.isEmpty()) {
-                    return null;
-                } else {
-                    ImageReader.Node dir = stack.poll();
-                    assert dir.isDirectory();
-                    iterator = dir.getChildren().iterator();
-                }
-            }
-        }
-
-        @Override
-        public boolean tryAdvance(Consumer<? super String> action) {
-            String next;
-            try {
-                next = next();
-            } catch (IOException ioe) {
-                throw new UncheckedIOException(ioe);
-            }
-            if (next != null) {
-                action.accept(next);
-                return true;
-            } else {
-                return false;
-            }
-        }
-
-        @Override
-        public Spliterator<String> trySplit() {
-            return null;
-        }
-
-        @Override
-        public int characteristics() {
-            return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE;
-        }
-
-        @Override
-        public long estimateSize() {
-            return Long.MAX_VALUE;
-        }
-    }
-}
--- a/src/java.base/share/classes/java/lang/reflect/Module.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/java.base/share/classes/java/lang/reflect/Module.java	Mon Dec 12 14:31:55 2016 +0000
@@ -643,6 +643,12 @@
      * <em>open</em>) to the given module. It also has no effect if
      * invoked on an {@link ModuleDescriptor#isOpen open} module. </p>
      *
+     * @apiNote As specified in section 5.4.3 of the <cite>The Java&trade;
+     * Virtual Machine Specification </cite>, if an attempt to resolve a
+     * symbolic reference fails because of a linkage error, then subsequent
+     * attempts to resolve the reference always fail with the same error that
+     * was thrown as a result of the initial resolution attempt.
+     *
      * @param  pn
      *         The package name
      * @param  other
@@ -656,6 +662,7 @@
      * @throws IllegalStateException
      *         If this is a named module and the caller is not this module
      *
+     * @jvms 5.4.3 Resolution
      * @see #isExported(String,Module)
      */
     @CallerSensitive
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Mon Dec 12 14:31:55 2016 +0000
@@ -57,15 +57,22 @@
      * @param strict
      *        Indicates whether module names are checked or not
      */
-    ModuleDescriptor.Builder newModuleBuilder(String mn, boolean strict);
+    ModuleDescriptor.Builder newModuleBuilder(String mn,
+                                              boolean strict,
+                                              boolean open,
+                                              boolean synthetic);
 
     /**
-     * Creates a builder for building an open module with the given module name.
-     *
-     * @param strict
-     *        Indicates whether module names are checked or not
+     * Returns the set of packages that are exported (unconditionally or
+     * unconditionally).
      */
-    ModuleDescriptor.Builder newOpenModuleBuilder(String mn, boolean strict);
+    Set<String> exportedPackages(ModuleDescriptor.Builder builder);
+
+    /**
+     * Returns the set of packages that are opened (unconditionally or
+     * unconditionally).
+     */
+    Set<String> openPackages(ModuleDescriptor.Builder builder);
 
     /**
      * Returns a {@code ModuleDescriptor.Requires} of the given modifiers
@@ -137,11 +144,6 @@
                                          int hashCode);
 
     /**
-     * Returns the hashes recorded for the given module or {@code null}.
-     */
-    ModuleHashes recordedHashes(ModuleReference mref);
-
-    /**
      * Resolves a collection of root modules, with service binding
      * and the empty configuration as the parent. The post resolution
      * checks are optionally run.
@@ -151,18 +153,4 @@
                                          boolean check,
                                          PrintStream traceOutput);
 
-    /**
-     * Creates a ModuleReference to a "patched" module.
-     */
-    ModuleReference newPatchedModule(ModuleDescriptor descriptor,
-                                     URI location,
-                                     Supplier<ModuleReader> readerSupplier);
-
-    /**
-     * Creates a ModuleFinder for a module path.
-     */
-    ModuleFinder newModulePath(Runtime.Version version,
-                               boolean isLinkPhase,
-                               Path... entries);
-
 }
--- a/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java	Mon Dec 12 14:31:55 2016 +0000
@@ -99,14 +99,13 @@
             // module_flags
             int module_flags = cr.readUnsignedShort(off);
             boolean open = ((module_flags & ACC_OPEN) != 0);
+            boolean synthetic = ((module_flags & ACC_SYNTHETIC) != 0);
             off += 2;
 
-            ModuleDescriptor.Builder builder;
-            if (open) {
-                builder = JLMA.newOpenModuleBuilder(mn, false);
-            } else {
-                builder = JLMA.newModuleBuilder(mn, false);
-            }
+            ModuleDescriptor.Builder builder = JLMA.newModuleBuilder(mn,
+                                                                     false,
+                                                                     open,
+                                                                     synthetic);
 
             // module_version
             String module_version = cr.readUTF8(off, buf);
--- a/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java	Mon Dec 12 14:31:55 2016 +0000
@@ -50,7 +50,6 @@
         byte[] generate(String algorithm);
     }
 
-
     private final String algorithm;
     private final Map<String, byte[]> nameToHash;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java	Mon Dec 12 14:31:55 2016 +0000
@@ -0,0 +1,982 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.module;
+
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.InvalidModuleDescriptorException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Builder;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.lang.module.ModuleDescriptor.Exports;
+import java.lang.module.ModuleDescriptor.Opens;
+import java.lang.module.ModuleDescriptor.Version;
+import java.nio.ByteBuffer;
+import java.nio.BufferUnderflowException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import jdk.internal.misc.JavaLangModuleAccess;
+import jdk.internal.misc.SharedSecrets;
+
+import static jdk.internal.module.ClassFileConstants.*;
+
+
+/**
+ * Read module information from a {@code module-info} class file.
+ *
+ * @implNote The rationale for the hand-coded reader is startup performance
+ * and fine control over the throwing of InvalidModuleDescriptorException.
+ */
+
+public final class ModuleInfo {
+
+    private static final JavaLangModuleAccess JLMA
+        = SharedSecrets.getJavaLangModuleAccess();
+
+    // supplies the set of packages when ModulePackages attribute not present
+    private final Supplier<Set<String>> packageFinder;
+
+    // indicates if the ModuleHashes attribute should be parsed
+    private final boolean parseHashes;
+
+    private ModuleInfo(Supplier<Set<String>> pf, boolean ph) {
+        packageFinder = pf;
+        parseHashes = ph;
+    }
+
+    private ModuleInfo(Supplier<Set<String>> pf) {
+        this(pf, true);
+    }
+
+    /**
+     * A holder class for the ModuleDescriptor that is created by reading the
+     * Module and other standard class file attributes. It also holds the objects
+     * that represent the non-standard class file attributes that are read from
+     * the class file.
+     */
+    public static final class Attributes {
+        private final ModuleDescriptor descriptor;
+        private final ModuleHashes recordedHashes;
+        Attributes(ModuleDescriptor descriptor, ModuleHashes recordedHashes) {
+            this.descriptor = descriptor;
+            this.recordedHashes = recordedHashes;
+        }
+        public ModuleDescriptor descriptor() {
+            return descriptor;
+        }
+        public ModuleHashes recordedHashes() {
+            return recordedHashes;
+        }
+    }
+
+
+    /**
+     * Reads a {@code module-info.class} from the given input stream.
+     *
+     * @throws InvalidModuleDescriptorException
+     * @throws IOException
+     */
+    public static Attributes read(InputStream in, Supplier<Set<String>> pf)
+        throws IOException
+    {
+        try {
+            return new ModuleInfo(pf).doRead(new DataInputStream(in));
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            throw invalidModuleDescriptor(e.getMessage());
+        } catch (EOFException x) {
+            throw truncatedModuleDescriptor();
+        }
+    }
+
+    /**
+     * Reads a {@code module-info.class} from the given byte buffer.
+     *
+     * @throws InvalidModuleDescriptorException
+     * @throws UncheckedIOException
+     */
+    public static Attributes read(ByteBuffer bb, Supplier<Set<String>> pf) {
+        try {
+            return new ModuleInfo(pf).doRead(new DataInputWrapper(bb));
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            throw invalidModuleDescriptor(e.getMessage());
+        } catch (EOFException x) {
+            throw truncatedModuleDescriptor();
+        } catch (IOException ioe) {
+            throw new UncheckedIOException(ioe);
+        }
+    }
+
+    /**
+     * Reads a {@code module-info.class} from the given byte buffer
+     * but ignore the {@code ModuleHashes} attribute.
+     *
+     * @throws InvalidModuleDescriptorException
+     * @throws UncheckedIOException
+     */
+    public static Attributes readIgnoringHashes(ByteBuffer bb, Supplier<Set<String>> pf) {
+        try {
+            return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb));
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            throw invalidModuleDescriptor(e.getMessage());
+        } catch (EOFException x) {
+            throw truncatedModuleDescriptor();
+        } catch (IOException ioe) {
+            throw new UncheckedIOException(ioe);
+        }
+    }
+
+    /**
+     * Reads the input as a module-info class file.
+     *
+     * @throws IOException
+     * @throws InvalidModuleDescriptorException
+     * @throws IllegalArgumentException if thrown by the ModuleDescriptor.Builder
+     *         because an identifier is not a legal Java identifier, duplicate
+     *         exports, and many other reasons
+     */
+    private Attributes doRead(DataInput in) throws IOException {
+
+        int magic = in.readInt();
+        if (magic != 0xCAFEBABE)
+            throw invalidModuleDescriptor("Bad magic number");
+
+        int minor_version = in.readUnsignedShort();
+        int major_version = in.readUnsignedShort();
+        if (major_version < 53) {
+            throw invalidModuleDescriptor("Must be >= 53.0");
+        }
+
+        ConstantPool cpool = new ConstantPool(in);
+
+        int access_flags = in.readUnsignedShort();
+        if (access_flags != ACC_MODULE)
+            throw invalidModuleDescriptor("access_flags should be ACC_MODULE");
+
+        int this_class = in.readUnsignedShort();
+        String mn = cpool.getClassName(this_class);
+        if (!"module-info".equals(mn))
+            throw invalidModuleDescriptor("this_class should be module-info");
+
+        int super_class = in.readUnsignedShort();
+        if (super_class > 0)
+            throw invalidModuleDescriptor("bad #super_class");
+
+        int interfaces_count = in.readUnsignedShort();
+        if (interfaces_count > 0)
+            throw invalidModuleDescriptor("Bad #interfaces");
+
+        int fields_count = in.readUnsignedShort();
+        if (fields_count > 0)
+            throw invalidModuleDescriptor("Bad #fields");
+
+        int methods_count = in.readUnsignedShort();
+        if (methods_count > 0)
+            throw invalidModuleDescriptor("Bad #methods");
+
+        int attributes_count = in.readUnsignedShort();
+
+        // the names of the attributes found in the class file
+        Set<String> attributes = new HashSet<>();
+
+        Builder builder = null;
+        Set<String> packages = 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);
+            int length = in.readInt();
+
+            boolean added = attributes.add(attribute_name);
+            if (!added && isAttributeAtMostOnce(attribute_name)) {
+                throw invalidModuleDescriptor("More than one "
+                                              + attribute_name + " attribute");
+            }
+
+            switch (attribute_name) {
+
+                case MODULE :
+                    builder = readModuleAttribute(in, cpool);
+                    break;
+
+                case MODULE_PACKAGES :
+                    packages = readModulePackagesAttribute(in, cpool);
+                    break;
+
+                case MODULE_MAIN_CLASS :
+                    mainClass = readModuleMainClassAttribute(in, cpool);
+                    break;
+
+                case MODULE_TARGET :
+                    osValues = readModuleTargetAttribute(in, cpool);
+                    break;
+
+                case MODULE_HASHES :
+                    if (parseHashes) {
+                        hashes = readModuleHashesAttribute(in, cpool);
+                    } else {
+                        in.skipBytes(length);
+                    }
+                    break;
+
+                default:
+                    if (isAttributeDisallowed(attribute_name)) {
+                        throw invalidModuleDescriptor(attribute_name
+                                                      + " attribute not allowed");
+                    } else {
+                        in.skipBytes(length);
+                    }
+
+            }
+        }
+
+        // the Module attribute is required
+        if (builder == null) {
+            throw invalidModuleDescriptor(MODULE + " attribute not found");
+        }
+
+        // If the ModulePackages attribute is not present then the packageFinder
+        // is used to find the set of packages
+        boolean usedPackageFinder = false;
+        if (packages == null && packageFinder != null) {
+            try {
+                packages = new HashSet<>(packageFinder.get());
+            } catch (UncheckedIOException x) {
+                throw x.getCause();
+            }
+            usedPackageFinder = true;
+        }
+        if (packages != null) {
+            Set<String> exportedPackages = JLMA.exportedPackages(builder);
+            Set<String> openPackages = JLMA.openPackages(builder);
+            if (packages.containsAll(exportedPackages)
+                    && packages.containsAll(openPackages)) {
+                packages.removeAll(exportedPackages);
+                packages.removeAll(openPackages);
+            } else {
+                // the set of packages is not complete
+                Set<String> exportedAndOpenPackages = new HashSet<>();
+                exportedAndOpenPackages.addAll(exportedPackages);
+                exportedAndOpenPackages.addAll(openPackages);
+                for (String pn : exportedAndOpenPackages) {
+                    if (!packages.contains(pn)) {
+                        String tail;
+                        if (usedPackageFinder) {
+                            tail = " not found by package finder";
+                        } else {
+                            tail = " missing from ModulePackages attribute";
+                        }
+                        throw invalidModuleDescriptor("Package " + pn + tail);
+                    }
+                }
+                assert false; // should not get here
+            }
+            builder.contains(packages);
+        }
+
+        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]);
+        }
+
+        ModuleDescriptor descriptor = builder.build();
+        return new Attributes(descriptor, hashes);
+    }
+
+    /**
+     * Reads the Module attribute, returning the ModuleDescriptor.Builder to
+     * build the corresponding ModuleDescriptor.
+     */
+    private Builder readModuleAttribute(DataInput in, ConstantPool cpool)
+        throws IOException
+    {
+        // module_name
+        int module_name_index = in.readUnsignedShort();
+        String mn = cpool.getModuleName(module_name_index);
+
+        int module_flags = in.readUnsignedShort();
+        boolean open = ((module_flags & ACC_OPEN) != 0);
+        boolean synthetic = ((module_flags & ACC_SYNTHETIC) != 0);
+
+        Builder builder = JLMA.newModuleBuilder(mn, false, open, synthetic);
+
+        int module_version_index = in.readUnsignedShort();
+        if (module_version_index != 0) {
+            String vs = cpool.getUtf8(module_version_index);
+            builder.version(vs);
+        }
+
+        int requires_count = in.readUnsignedShort();
+        boolean requiresJavaBase = false;
+        for (int i=0; i<requires_count; i++) {
+            int requires_index = in.readUnsignedShort();
+            String dn = cpool.getModuleName(requires_index);
+
+            int requires_flags = in.readUnsignedShort();
+            Set<Requires.Modifier> mods;
+            if (requires_flags == 0) {
+                mods = Collections.emptySet();
+            } else {
+                mods = new HashSet<>();
+                if ((requires_flags & ACC_TRANSITIVE) != 0)
+                    mods.add(Requires.Modifier.TRANSITIVE);
+                if ((requires_flags & ACC_STATIC_PHASE) != 0)
+                    mods.add(Requires.Modifier.STATIC);
+                if ((requires_flags & ACC_SYNTHETIC) != 0)
+                    mods.add(Requires.Modifier.SYNTHETIC);
+                if ((requires_flags & ACC_MANDATED) != 0)
+                    mods.add(Requires.Modifier.MANDATED);
+            }
+
+            int requires_version_index = in.readUnsignedShort();
+            Version compiledVersion = null;
+            if (requires_version_index != 0) {
+                String vs = cpool.getUtf8(requires_version_index);
+                compiledVersion = Version.parse(vs);
+            }
+
+            if (compiledVersion == null) {
+                builder.requires(mods, dn);
+            } else {
+                builder.requires(mods, dn, compiledVersion);
+            }
+
+            if (dn.equals("java.base"))
+                requiresJavaBase = true;
+        }
+        if (mn.equals("java.base")) {
+            if (requires_count > 0) {
+                throw invalidModuleDescriptor("The requires table for java.base"
+                                              + " must be 0 length");
+            }
+        } else if (!requiresJavaBase) {
+            throw invalidModuleDescriptor("The requires table must have"
+                                          + " an entry for java.base");
+        }
+
+        int exports_count = in.readUnsignedShort();
+        if (exports_count > 0) {
+            for (int i=0; i<exports_count; i++) {
+                int exports_index = in.readUnsignedShort();
+                String pkg = cpool.getPackageName(exports_index);
+
+                Set<Exports.Modifier> mods;
+                int exports_flags = in.readUnsignedShort();
+                if (exports_flags == 0) {
+                    mods = Collections.emptySet();
+                } else {
+                    mods = new HashSet<>();
+                    if ((exports_flags & ACC_SYNTHETIC) != 0)
+                        mods.add(Exports.Modifier.SYNTHETIC);
+                    if ((exports_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);
+                    for (int j=0; j<exports_to_count; j++) {
+                        int exports_to_index = in.readUnsignedShort();
+                        targets.add(cpool.getModuleName(exports_to_index));
+                    }
+                    builder.exports(mods, pkg, targets);
+                } else {
+                    builder.exports(mods, pkg);
+                }
+            }
+        }
+
+        int opens_count = in.readUnsignedShort();
+        if (opens_count > 0) {
+            if (open) {
+                throw invalidModuleDescriptor("The opens table for an open"
+                                              + " module must be 0 length");
+            }
+            for (int i=0; i<opens_count; i++) {
+                int opens_index = in.readUnsignedShort();
+                String pkg = cpool.getPackageName(opens_index);
+
+                Set<Opens.Modifier> mods;
+                int opens_flags = in.readUnsignedShort();
+                if (opens_flags == 0) {
+                    mods = Collections.emptySet();
+                } else {
+                    mods = new HashSet<>();
+                    if ((opens_flags & ACC_SYNTHETIC) != 0)
+                        mods.add(Opens.Modifier.SYNTHETIC);
+                    if ((opens_flags & ACC_MANDATED) != 0)
+                        mods.add(Opens.Modifier.MANDATED);
+                }
+
+                int open_to_count = in.readUnsignedShort();
+                if (open_to_count > 0) {
+                    Set<String> targets = new HashSet<>(open_to_count);
+                    for (int j=0; j<open_to_count; j++) {
+                        int opens_to_index = in.readUnsignedShort();
+                        targets.add(cpool.getModuleName(opens_to_index));
+                    }
+                    builder.opens(mods, pkg, targets);
+                } else {
+                    builder.opens(mods, pkg);
+                }
+            }
+        }
+
+        int uses_count = in.readUnsignedShort();
+        if (uses_count > 0) {
+            for (int i=0; i<uses_count; i++) {
+                int index = in.readUnsignedShort();
+                String sn = cpool.getClassName(index);
+                builder.uses(sn);
+            }
+        }
+
+        int provides_count = in.readUnsignedShort();
+        if (provides_count > 0) {
+            for (int i=0; i<provides_count; i++) {
+                int index = in.readUnsignedShort();
+                String sn = cpool.getClassName(index);
+                int with_count = in.readUnsignedShort();
+                List<String> providers = new ArrayList<>(with_count);
+                for (int j=0; j<with_count; j++) {
+                    index = in.readUnsignedShort();
+                    String pn = cpool.getClassName(index);
+                    providers.add(pn);
+                }
+                builder.provides(sn, providers);
+            }
+        }
+
+        return builder;
+    }
+
+    /**
+     * Reads the ModulePackages attribute
+     */
+    private Set<String> readModulePackagesAttribute(DataInput in, ConstantPool cpool)
+        throws IOException
+    {
+        int package_count = in.readUnsignedShort();
+        Set<String> packages = new HashSet<>(package_count);
+        for (int i=0; i<package_count; i++) {
+            int index = in.readUnsignedShort();
+            String pn = cpool.getPackageName(index);
+            boolean added = packages.add(pn);
+            if (!added) {
+                throw invalidModuleDescriptor("Package " + pn + " in ModulePackages"
+                                              + "attribute more than once");
+            }
+        }
+        return packages;
+    }
+
+    /**
+     * Reads the ModuleMainClass attribute
+     */
+    private String readModuleMainClassAttribute(DataInput in, ConstantPool cpool)
+        throws IOException
+    {
+        int index = in.readUnsignedShort();
+        return cpool.getClassName(index);
+    }
+
+    /**
+     * Reads the ModuleTarget attribute
+     */
+    private String[] readModuleTargetAttribute(DataInput in, ConstantPool cpool)
+        throws IOException
+    {
+        String[] values = new String[3];
+
+        int name_index = in.readUnsignedShort();
+        if (name_index != 0)
+            values[0] = cpool.getUtf8(name_index);
+
+        int arch_index = in.readUnsignedShort();
+        if (arch_index != 0)
+            values[1] = cpool.getUtf8(arch_index);
+
+        int version_index = in.readUnsignedShort();
+        if (version_index != 0)
+            values[2] = cpool.getUtf8(version_index);
+
+        return values;
+    }
+
+
+    /**
+     * Reads the ModuleHashes attribute
+     */
+    private ModuleHashes readModuleHashesAttribute(DataInput in, ConstantPool cpool)
+        throws IOException
+    {
+        int algorithm_index = in.readUnsignedShort();
+        String algorithm = cpool.getUtf8(algorithm_index);
+
+        int hash_count = in.readUnsignedShort();
+        Map<String, byte[]> map = new HashMap<>(hash_count);
+        for (int i=0; i<hash_count; i++) {
+            int module_name_index = in.readUnsignedShort();
+            String mn = cpool.getModuleName(module_name_index);
+            int hash_length = in.readUnsignedShort();
+            if (hash_length == 0) {
+                throw invalidModuleDescriptor("hash_length == 0");
+            }
+            byte[] hash = new byte[hash_length];
+            in.readFully(hash);
+            map.put(mn, hash);
+        }
+
+        return new ModuleHashes(algorithm, map);
+    }
+
+
+    /**
+     * Returns true if the given attribute can be present at most once
+     * in the class file. Returns false otherwise.
+     */
+    private static boolean isAttributeAtMostOnce(String name) {
+
+        if (name.equals(MODULE) ||
+                name.equals(SOURCE_FILE) ||
+                name.equals(SDE) ||
+                name.equals(MODULE_PACKAGES) ||
+                name.equals(MODULE_MAIN_CLASS) ||
+                name.equals(MODULE_TARGET) ||
+                name.equals(MODULE_HASHES))
+            return true;
+
+        return false;
+    }
+
+    /**
+     * 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, 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;
+        if (notAllowed == null) {
+            notAllowed = Set.of(
+                    "ConstantValue",
+                    "Code",
+                    "StackMapTable",
+                    "Exceptions",
+                    "EnclosingMethod",
+                    "Signature",
+                    "LineNumberTable",
+                    "LocalVariableTable",
+                    "LocalVariableTypeTable",
+                    "RuntimeVisibleParameterAnnotations",
+                    "RuntimeInvisibleParameterAnnotations",
+                    "RuntimeVisibleTypeAnnotations",
+                    "RuntimeInvisibleTypeAnnotations",
+                    "Synthetic",
+                    "AnnotationDefault",
+                    "BootstrapMethods",
+                    "MethodParameters");
+            predefinedNotAllowed = notAllowed;
+        }
+        return notAllowed.contains(name);
+    }
+
+    // lazily created set the pre-defined attributes that are not allowed
+    private static volatile Set<String> predefinedNotAllowed;
+
+
+    /**
+     * The constant pool in a class file.
+     */
+    private static class ConstantPool {
+        static final int CONSTANT_Utf8 = 1;
+        static final int CONSTANT_Integer = 3;
+        static final int CONSTANT_Float = 4;
+        static final int CONSTANT_Long = 5;
+        static final int CONSTANT_Double = 6;
+        static final int CONSTANT_Class = 7;
+        static final int CONSTANT_String = 8;
+        static final int CONSTANT_Fieldref = 9;
+        static final int CONSTANT_Methodref = 10;
+        static final int CONSTANT_InterfaceMethodref = 11;
+        static final int CONSTANT_NameAndType = 12;
+        static final int CONSTANT_MethodHandle = 15;
+        static final int CONSTANT_MethodType = 16;
+        static final int CONSTANT_InvokeDynamic = 18;
+        static final int CONSTANT_Module = 19;
+        static final int CONSTANT_Package = 20;
+
+        private static class Entry {
+            protected Entry(int tag) {
+                this.tag = tag;
+            }
+            final int tag;
+        }
+
+        private static class IndexEntry extends Entry {
+            IndexEntry(int tag, int index) {
+                super(tag);
+                this.index = index;
+            }
+            final int index;
+        }
+
+        private static class Index2Entry extends Entry {
+            Index2Entry(int tag, int index1, int index2) {
+                super(tag);
+                this.index1 = index1;
+                this.index2 = index2;
+            }
+            final int index1,  index2;
+        }
+
+        private static class ValueEntry extends Entry {
+            ValueEntry(int tag, Object value) {
+                super(tag);
+                this.value = value;
+            }
+            final Object value;
+        }
+
+        final Entry[] pool;
+
+        ConstantPool(DataInput in) throws IOException {
+            int count = in.readUnsignedShort();
+            pool = new Entry[count];
+
+            for (int i = 1; i < count; i++) {
+                int tag = in.readUnsignedByte();
+                switch (tag) {
+
+                    case CONSTANT_Utf8:
+                        String svalue = in.readUTF();
+                        pool[i] = new ValueEntry(tag, svalue);
+                        break;
+
+                    case CONSTANT_Class:
+                    case CONSTANT_Package:
+                    case CONSTANT_Module:
+                    case CONSTANT_String:
+                        int index = in.readUnsignedShort();
+                        pool[i] = new IndexEntry(tag, index);
+                        break;
+
+                    case CONSTANT_Double:
+                        double dvalue = in.readDouble();
+                        pool[i] = new ValueEntry(tag, dvalue);
+                        i++;
+                        break;
+
+                    case CONSTANT_Fieldref:
+                    case CONSTANT_InterfaceMethodref:
+                    case CONSTANT_Methodref:
+                    case CONSTANT_InvokeDynamic:
+                    case CONSTANT_NameAndType:
+                        int index1 = in.readUnsignedShort();
+                        int index2 = in.readUnsignedShort();
+                        pool[i] = new Index2Entry(tag, index1, index2);
+                        break;
+
+                    case CONSTANT_MethodHandle:
+                        int refKind = in.readUnsignedByte();
+                        index = in.readUnsignedShort();
+                        pool[i] = new Index2Entry(tag, refKind, index);
+                        break;
+
+                    case CONSTANT_MethodType:
+                        index = in.readUnsignedShort();
+                        pool[i] = new IndexEntry(tag, index);
+                        break;
+
+                    case CONSTANT_Float:
+                        float fvalue = in.readFloat();
+                        pool[i] = new ValueEntry(tag, fvalue);
+                        break;
+
+                    case CONSTANT_Integer:
+                        int ivalue = in.readInt();
+                        pool[i] = new ValueEntry(tag, ivalue);
+                        break;
+
+                    case CONSTANT_Long:
+                        long lvalue = in.readLong();
+                        pool[i] = new ValueEntry(tag, lvalue);
+                        i++;
+                        break;
+
+                    default:
+                        throw invalidModuleDescriptor("Bad constant pool entry: "
+                                                      + i);
+                }
+            }
+        }
+
+        String getClassName(int index) {
+            checkIndex(index);
+            Entry e = pool[index];
+            if (e.tag != CONSTANT_Class) {
+                throw invalidModuleDescriptor("CONSTANT_Class expected at entry: "
+                                              + index);
+            }
+            String value = getUtf8(((IndexEntry) e).index);
+            checkUnqualifiedName("CONSTANT_Class", index, value);
+            return value.replace('/', '.');  // internal form -> binary name
+        }
+
+        String getPackageName(int index) {
+            checkIndex(index);
+            Entry e = pool[index];
+            if (e.tag != CONSTANT_Package) {
+                throw invalidModuleDescriptor("CONSTANT_Package expected at entry: "
+                                              + index);
+            }
+            String value = getUtf8(((IndexEntry) e).index);
+            checkUnqualifiedName("CONSTANT_Package", index, value);
+            return value.replace('/', '.');  // internal form -> binary name
+        }
+
+        String getModuleName(int index) {
+            checkIndex(index);
+            Entry e = pool[index];
+            if (e.tag != CONSTANT_Module) {
+                throw invalidModuleDescriptor("CONSTANT_Module expected at entry: "
+                                              + index);
+            }
+            String value = getUtf8(((IndexEntry) e).index);
+            checkModuleName(index, value);
+            return value;
+        }
+
+        String getUtf8(int index) {
+            checkIndex(index);
+            Entry e = pool[index];
+            if (e.tag != CONSTANT_Utf8) {
+                throw invalidModuleDescriptor("CONSTANT_Utf8 expected at entry: "
+                                              + index);
+            }
+            return (String) (((ValueEntry) e).value);
+        }
+
+        void checkIndex(int index) {
+            if (index < 1 || index >= pool.length)
+                throw invalidModuleDescriptor("Index into constant pool out of range");
+        }
+
+        void checkUnqualifiedName(String what, int index, String value) {
+            int len = value.length();
+            if (len == 0) {
+                throw invalidModuleDescriptor(what + " at entry " + index
+                                              + " has zero length");
+            }
+            for (int i=0; i<len; i++) {
+                char c = value.charAt(i);
+                if (c == '.' || c == ';' || c == '[') {
+                    throw invalidModuleDescriptor(what + " at entry " + index
+                                                  + " has illegal character: '"
+                                                  + c + "'");
+                }
+            }
+        }
+
+        void checkModuleName(int index, String value) {
+            int len = value.length();
+            if (len == 0) {
+                throw invalidModuleDescriptor("Module name is zero length");
+            }
+            // TBD catch illegal characters
+        }
+    }
+
+    /**
+     * A DataInput implementation that reads from a ByteBuffer.
+     */
+    private static class DataInputWrapper implements DataInput {
+        private final ByteBuffer bb;
+
+        DataInputWrapper(ByteBuffer bb) {
+            this.bb = bb;
+        }
+
+        @Override
+        public void readFully(byte b[]) throws IOException {
+            readFully(b, 0, b.length);
+        }
+
+        @Override
+        public void readFully(byte b[], int off, int len) throws IOException {
+            try {
+                bb.get(b, off, len);
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public int skipBytes(int n) {
+            int skip = Math.min(n, bb.remaining());
+            bb.position(bb.position() + skip);
+            return skip;
+        }
+
+        @Override
+        public boolean readBoolean() throws IOException {
+            try {
+                int ch = bb.get();
+                return (ch != 0);
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public byte readByte() throws IOException {
+            try {
+                return bb.get();
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public int readUnsignedByte() throws IOException {
+            try {
+                return ((int) bb.get()) & 0xff;
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public short readShort() throws IOException {
+            try {
+                return bb.getShort();
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public int readUnsignedShort() throws IOException {
+            try {
+                return ((int) bb.getShort()) & 0xffff;
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public char readChar() throws IOException {
+            try {
+                return bb.getChar();
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public int readInt() throws IOException {
+            try {
+                return bb.getInt();
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public long readLong() throws IOException {
+            try {
+                return bb.getLong();
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public float readFloat() throws IOException {
+            try {
+                return bb.getFloat();
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public double readDouble() throws IOException {
+            try {
+                return bb.getDouble();
+            } catch (BufferUnderflowException e) {
+                throw new EOFException(e.getMessage());
+            }
+        }
+
+        @Override
+        public String readLine() {
+            throw new RuntimeException("not implemented");
+        }
+
+        @Override
+        public String readUTF() throws IOException {
+            // ### Need to measure the performance and feasibility of using
+            // the UTF-8 decoder instead.
+            return DataInputStream.readUTF(this);
+        }
+    }
+
+    /**
+     * Returns an InvalidModuleDescriptorException with the given detail
+     * message
+     */
+    private static InvalidModuleDescriptorException
+    invalidModuleDescriptor(String msg) {
+        return new InvalidModuleDescriptorException(msg);
+    }
+
+    /**
+     * Returns an InvalidModuleDescriptorException with a detail message to
+     * indicate that the class file is truncated.
+     */
+    private static InvalidModuleDescriptorException truncatedModuleDescriptor() {
+        return invalidModuleDescriptor("Truncated module-info.class");
+    }
+
+}
--- a/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Mon Dec 12 14:31:55 2016 +0000
@@ -149,9 +149,12 @@
 
         // return a module reference to the patched module
         URI location = mref.location().orElse(null);
-        return JLMA.newPatchedModule(descriptor,
-                                     location,
-                                     () -> new PatchedModuleReader(paths, mref));
+        return new ModuleReferenceImpl(descriptor,
+                                       location,
+                                       () -> new PatchedModuleReader(paths, mref),
+                                       true,
+                                       null,
+                                       null);
 
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ModulePath.java	Mon Dec 12 14:31:55 2016 +0000
@@ -0,0 +1,686 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.module;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UncheckedIOException;
+import java.lang.module.FindException;
+import java.lang.module.InvalidModuleDescriptorException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+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.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.zip.ZipFile;
+
+import jdk.internal.jmod.JmodFile;
+import jdk.internal.jmod.JmodFile.Section;
+import jdk.internal.perf.PerfCounter;
+import jdk.internal.util.jar.VersionedStream;
+
+
+/**
+ * A {@code ModuleFinder} that locates modules on the file system by searching
+ * a sequence of directories or packaged modules.
+ *
+ * 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 created for link-time then it additionally locates
+ * modules in JMOD files.
+ */
+
+public 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;
+
+    // map of module name to module reference map for modules already located
+    private final Map<String, ModuleReference> cachedModules = new HashMap<>();
+
+    public 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);
+        }
+    }
+
+    public ModulePath(Path... entries) {
+        this(JarFile.runtimeVersion(), false, entries);
+    }
+
+    @Override
+    public Optional<ModuleReference> find(String name) {
+        Objects.requireNonNull(name);
+
+        // try cached modules
+        ModuleReference m = cachedModules.get(name);
+        if (m != null)
+            return Optional.of(m);
+
+        // the module may not have been encountered yet
+        while (hasNextEntry()) {
+            scanNextEntry();
+            m = cachedModules.get(name);
+            if (m != null)
+                return Optional.of(m);
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    public Set<ModuleReference> findAll() {
+        // need to ensure that all entries have been scanned
+        while (hasNextEntry()) {
+            scanNextEntry();
+        }
+        return cachedModules.values().stream().collect(Collectors.toSet());
+    }
+
+    /**
+     * Returns {@code true} if there are additional entries to scan
+     */
+    private boolean hasNextEntry() {
+        return next < entries.length;
+    }
+
+    /**
+     * Scans the next entry on the module path. A no-op if all entries have
+     * already been scanned.
+     *
+     * @throws FindException if an error occurs scanning the next entry
+     */
+    private void scanNextEntry() {
+        if (hasNextEntry()) {
+
+            long t0 = System.nanoTime();
+
+            Path entry = entries[next];
+            Map<String, ModuleReference> modules = scan(entry);
+            next++;
+
+            // update cache, ignoring duplicates
+            int initialSize = cachedModules.size();
+            for (Map.Entry<String, ModuleReference> e : modules.entrySet()) {
+                cachedModules.putIfAbsent(e.getKey(), e.getValue());
+            }
+
+            // update counters
+            int added = cachedModules.size() - initialSize;
+            moduleCount.add(added);
+
+            scanTime.addElapsedTimeFrom(t0);
+        }
+    }
+
+
+    /**
+     * Scan the given module path entry. If the entry is a directory then it is
+     * a directory of modules or an exploded module. If the entry is a regular
+     * file then it is assumed to be a packaged module.
+     *
+     * @throws FindException if an error occurs scanning the entry
+     */
+    private Map<String, ModuleReference> scan(Path entry) {
+
+        BasicFileAttributes attrs;
+        try {
+            attrs = Files.readAttributes(entry, BasicFileAttributes.class);
+        } catch (NoSuchFileException e) {
+            return Collections.emptyMap();
+        } catch (IOException ioe) {
+            throw new FindException(ioe);
+        }
+
+        try {
+
+            if (attrs.isDirectory()) {
+                Path mi = entry.resolve(MODULE_INFO);
+                if (!Files.exists(mi)) {
+                    // does not exist or unable to determine so assume a
+                    // directory of modules
+                    return scanDirectory(entry);
+                }
+            }
+
+            // packaged or exploded module
+            ModuleReference mref = readModule(entry, attrs);
+            if (mref != null) {
+                String name = mref.descriptor().name();
+                return Collections.singletonMap(name, mref);
+            } else {
+                // skipped
+                return Collections.emptyMap();
+            }
+
+        } catch (IOException ioe) {
+            throw new FindException(ioe);
+        }
+    }
+
+
+    /**
+     * Scans the given directory for packaged or exploded modules.
+     *
+     * @return a map of module name to ModuleReference for the modules found
+     *         in the directory
+     *
+     * @throws IOException if an I/O error occurs
+     * @throws FindException if an error occurs scanning the entry or the
+     *         directory contains two or more modules with the same name
+     */
+    private Map<String, ModuleReference> scanDirectory(Path dir)
+        throws IOException
+    {
+        // The map of name -> mref of modules found in this directory.
+        Map<String, ModuleReference> nameToReference = new HashMap<>();
+
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
+            for (Path entry : stream) {
+                BasicFileAttributes attrs;
+                try {
+                    attrs = Files.readAttributes(entry, BasicFileAttributes.class);
+                } catch (NoSuchFileException ignore) {
+                    // file has been removed or moved, ignore for now
+                    continue;
+                }
+
+                ModuleReference mref = readModule(entry, attrs);
+
+                // module found
+                if (mref != null) {
+                    // can have at most one version of a module in the directory
+                    String name = mref.descriptor().name();
+                    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
+                                                 + " (" + fn1 + " and " + fn2 + ")");
+                    }
+                }
+            }
+        }
+
+        return nameToReference;
+    }
+
+
+    /**
+     * Locates a packaged or exploded module, returning a {@code ModuleReference}
+     * to the module. Returns {@code null} if the entry is skipped because it is
+     * to a directory that does not contain a module-info.class or it's a hidden
+     * file.
+     *
+     * @throws IOException if an I/O error occurs
+     * @throws FindException if the file is not recognized as a module or an
+     *         error occurs parsing its module descriptor
+     */
+    private ModuleReference readModule(Path entry, BasicFileAttributes attrs)
+        throws IOException
+    {
+        try {
+
+            if (attrs.isDirectory()) {
+                return readExplodedModule(entry); // may return null
+            }
+
+            String fn = entry.getFileName().toString();
+            if (attrs.isRegularFile()) {
+                if (fn.endsWith(".jar")) {
+                    return readJar(entry);
+                } else if (fn.endsWith(".jmod")) {
+                    if (isLinkPhase)
+                        return readJMod(entry);
+                    throw new FindException("JMOD files not supported: " + entry);
+                }
+            }
+
+            // skip hidden files
+            if (fn.startsWith(".") || Files.isHidden(entry)) {
+                return null;
+            } else {
+                throw new FindException("Unrecognized module: " + entry);
+            }
+
+        } catch (InvalidModuleDescriptorException e) {
+            throw new FindException("Error reading module: " + entry, e);
+        }
+    }
+
+
+    /**
+     * 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(JmodFile jf) {
+        return jf.stream()
+            .filter(e -> e.section() == Section.CLASSES)
+            .map(JmodFile.Entry::name)
+            .map(this::toPackageName)
+            .flatMap(Optional::stream)
+            .collect(Collectors.toSet());
+    }
+
+    /**
+     * Returns a {@code ModuleReference} to a module in jmod file on the
+     * file system.
+     *
+     * @throws IOException
+     * @throws InvalidModuleDescriptorException
+     */
+    private ModuleReference readJMod(Path file) throws IOException {
+        try (JmodFile jf = new JmodFile(file)) {
+            ModuleInfo.Attributes attrs;
+            try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
+                attrs  = ModuleInfo.read(in, () -> jmodPackages(jf));
+            }
+            return ModuleReferences.newJModModule(attrs, file);
+        }
+    }
+
+
+    // -- JAR files --
+
+    private static final String SERVICES_PREFIX = "META-INF/services/";
+
+    /**
+     * Returns the service type corresponding to the name of a services
+     * configuration file if it is 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".
+     */
+    private Optional<String> toServiceName(String cf) {
+        assert cf.startsWith(SERVICES_PREFIX);
+        int index = cf.lastIndexOf("/") + 1;
+        if (index < cf.length()) {
+            String prefix = cf.substring(0, index);
+            if (prefix.equals(SERVICES_PREFIX)) {
+                String sn = cf.substring(index);
+                if (Checks.isJavaIdentifier(sn))
+                    return Optional.of(sn);
+            }
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Reads the next line from the given reader and trims it of comments and
+     * leading/trailing white space.
+     *
+     * Returns null if the reader is at EOF.
+     */
+    private String nextLine(BufferedReader reader) throws IOException {
+        String ln = reader.readLine();
+        if (ln != null) {
+            int ci = ln.indexOf('#');
+            if (ci >= 0)
+                ln = ln.substring(0, ci);
+            ln = ln.trim();
+        }
+        return ln;
+    }
+
+    /**
+     * Treat the given JAR file as a module as follows:
+     *
+     * 1. The module name (and optionally the version) is derived from the file
+     *    name of the JAR file
+     * 2. All packages are exported and open
+     * 3. It has no non-exported/non-open 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
+     *    is mapped to the module descriptor mainClass
+     */
+    private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
+        throws IOException
+    {
+        // Derive module name and version from JAR file name
+
+        String fn = jf.getName();
+        int i = fn.lastIndexOf(File.separator);
+        if (i != -1)
+            fn = fn.substring(i+1);
+
+        // drop .jar
+        String mn = fn.substring(0, fn.length()-4);
+        String vs = null;
+
+        // find first occurrence of -${NUMBER}. or -${NUMBER}$
+        Matcher matcher = Patterns.DASH_VERSION.matcher(mn);
+        if (matcher.find()) {
+            int start = matcher.start();
+
+            // attempt to parse the tail as a version string
+            try {
+                String tail = mn.substring(start+1);
+                ModuleDescriptor.Version.parse(tail);
+                vs = tail;
+            } catch (IllegalArgumentException ignore) { }
+
+            mn = mn.substring(0, start);
+        }
+
+        // finally clean up the module name
+        mn = cleanModuleName(mn);
+
+        // Builder throws IAE if module name is empty or invalid
+        ModuleDescriptor.Builder builder
+            = ModuleDescriptor.automaticModule(mn)
+                .requires(Set.of(Requires.Modifier.MANDATED), "java.base");
+        if (vs != null)
+            builder.version(vs);
+
+        // scan the names of the entries in the JAR file
+        Map<Boolean, Set<String>> map = VersionedStream.stream(jf)
+                .filter(e -> !e.isDirectory())
+                .map(JarEntry::getName)
+                .collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX),
+                                                   Collectors.toSet()));
+
+        Set<String> resources = map.get(Boolean.FALSE);
+        Set<String> configFiles = map.get(Boolean.TRUE);
+        // all packages are exported and open
+        resources.stream()
+                .map(this::toPackageName)
+                .flatMap(Optional::stream)
+                .distinct()
+                .forEach(pn -> builder.exports(pn).opens(pn));
+
+        // map names of service configuration files to service names
+        Set<String> serviceNames = configFiles.stream()
+                .map(this::toServiceName)
+                .flatMap(Optional::stream)
+                .collect(Collectors.toSet());
+
+        // parse each service configuration file
+        for (String sn : serviceNames) {
+            JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
+            List<String> providerClasses = new ArrayList<>();
+            try (InputStream in = jf.getInputStream(entry)) {
+                BufferedReader reader
+                    = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+                String cn;
+                while ((cn = nextLine(reader)) != null) {
+                    if (cn.length() > 0) {
+                        providerClasses.add(cn);
+                    }
+                }
+            }
+            if (!providerClasses.isEmpty())
+                builder.provides(sn, providerClasses);
+        }
+
+        // Main-Class attribute if it exists
+        Manifest man = jf.getManifest();
+        if (man != null) {
+            Attributes attrs = man.getMainAttributes();
+            String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
+            if (mainClass != null)
+                builder.mainClass(mainClass.replace("/", "."));
+        }
+
+        return builder.build();
+    }
+
+    /**
+     * Patterns used to derive the module name from a JAR file name.
+     */
+    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("^\\.");
+        static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
+    }
+
+    /**
+     * 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(".");
+
+        // collapse repeating dots
+        mn = Patterns.REPEATING_DOTS.matcher(mn).replaceAll(".");
+
+        // drop leading dots
+        if (mn.length() > 0 && mn.charAt(0) == '.')
+            mn = Patterns.LEADING_DOTS.matcher(mn).replaceAll("");
+
+        // drop trailing dots
+        int len = mn.length();
+        if (len > 0 && mn.charAt(len-1) == '.')
+            mn = Patterns.TRAILING_DOTS.matcher(mn).replaceAll("");
+
+        return mn;
+    }
+
+    private Set<String> jarPackages(JarFile jf) {
+        return VersionedStream.stream(jf)
+                .filter(e -> !e.isDirectory())
+                .map(JarEntry::getName)
+                .map(this::toPackageName)
+                .flatMap(Optional::stream)
+                .collect(Collectors.toSet());
+    }
+
+    /**
+     * Returns a {@code ModuleReference} to a module in modular JAR file on
+     * the file system.
+     *
+     * @throws IOException
+     * @throws FindException
+     * @throws InvalidModuleDescriptorException
+     */
+    private ModuleReference readJar(Path file) throws IOException {
+        try (JarFile jf = new JarFile(file.toFile(),
+                                      true,               // verify
+                                      ZipFile.OPEN_READ,
+                                      releaseVersion))
+        {
+            ModuleInfo.Attributes attrs;
+            JarEntry entry = jf.getJarEntry(MODULE_INFO);
+            if (entry == null) {
+
+                // no module-info.class so treat it as automatic module
+                try {
+                    ModuleDescriptor md = deriveModuleDescriptor(jf);
+                    attrs = new ModuleInfo.Attributes(md, null);
+                } catch (IllegalArgumentException iae) {
+                    throw new FindException(
+                        "Unable to derive module descriptor for: "
+                        + jf.getName(), iae);
+                }
+
+            } else {
+                attrs = ModuleInfo.read(jf.getInputStream(entry),
+                                        () -> jarPackages(jf));
+            }
+
+            return ModuleReferences.newJarModule(attrs, file);
+        }
+    }
+
+
+    // -- exploded directories --
+
+    private Set<String> explodedPackages(Path dir) {
+        try {
+            return Files.find(dir, Integer.MAX_VALUE,
+                              ((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);
+        }
+    }
+
+    /**
+     * Returns a {@code ModuleReference} to an exploded module on the file
+     * system or {@code null} if {@code module-info.class} not found.
+     *
+     * @throws IOException
+     * @throws InvalidModuleDescriptorException
+     */
+    private ModuleReference readExplodedModule(Path dir) throws IOException {
+        Path mi = dir.resolve(MODULE_INFO);
+        ModuleInfo.Attributes attrs;
+        try (InputStream in = Files.newInputStream(mi)) {
+            attrs = ModuleInfo.read(new BufferedInputStream(in),
+                                    () -> explodedPackages(dir));
+        } catch (NoSuchFileException e) {
+            // for now
+            return null;
+        }
+        return ModuleReferences.newExplodedModule(attrs, 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();
+        }
+
+        String pn = name.substring(0, index).replace('/', '.');
+        if (Checks.isJavaIdentifier(pn)) {
+            return Optional.of(pn);
+        } else {
+            // not a valid package name
+            return Optional.empty();
+        }
+    }
+
+    /**
+     * 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 {
+            // not a valid package name
+            return Optional.empty();
+        }
+    }
+
+    private static final PerfCounter scanTime
+        = PerfCounter.newPerfCounter("jdk.module.finder.modulepath.scanTime");
+    private static final PerfCounter moduleCount
+        = PerfCounter.newPerfCounter("jdk.module.finder.modulepath.modules");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleReferenceImpl.java	Mon Dec 12 14:31:55 2016 +0000
@@ -0,0 +1,166 @@
+/*
+ * 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.module;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.net.URI;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+/**
+ * A ModuleReference implementation that supports referencing a module that
+ * is patched and/or can be tied to other modules by means of hashes.
+ */
+
+public class ModuleReferenceImpl extends ModuleReference {
+
+    private final Supplier<ModuleReader> readerSupplier;
+
+    // true if this is a reference to a patched module
+    private final boolean patched;
+
+    // the hashes of other modules recorded in this module
+    private final ModuleHashes recordedHashes;
+
+    // the function that computes the hash of this module
+    private final ModuleHashes.HashSupplier hasher;
+
+    // cached hash of this module to avoid needing to compute it many times
+    private byte[] cachedHash;
+
+    /**
+     * Constructs a new instance of this class.
+     */
+    ModuleReferenceImpl(ModuleDescriptor descriptor,
+                        URI location,
+                        Supplier<ModuleReader> readerSupplier,
+                        boolean patched,
+                        ModuleHashes recordedHashes,
+                        ModuleHashes.HashSupplier hasher)
+    {
+        super(descriptor, location);
+        this.readerSupplier = readerSupplier;
+        this.patched = patched;
+        this.recordedHashes = recordedHashes;
+        this.hasher = hasher;
+    }
+
+    ModuleReferenceImpl(ModuleDescriptor descriptor,
+                        URI location,
+                        Supplier<ModuleReader> readerSupplier) {
+        this(descriptor, location, readerSupplier, false, null, null);
+    }
+
+    @Override
+    public ModuleReader open() throws IOException {
+        try {
+            return readerSupplier.get();
+        } catch (UncheckedIOException e) {
+            throw e.getCause();
+        }
+    }
+
+    /**
+     * Returns {@code true} if this module has been patched via --patch-module.
+     */
+    public boolean isPatched() {
+        return patched;
+    }
+
+    /**
+     * Returns the hashes recorded in this module or {@code null} if there
+     * are no hashes recorded.
+     */
+    public ModuleHashes recordedHashes() {
+        return recordedHashes;
+    }
+
+    /**
+     * Returns the supplier that computes the hash of this module.
+     */
+    ModuleHashes.HashSupplier hasher() {
+        return hasher;
+    }
+
+    /**
+     * Computes the hash of this module. Returns {@code null} if the hash
+     * cannot be computed.
+     *
+     * @throws java.io.UncheckedIOException if an I/O error occurs
+     */
+    public byte[] computeHash(String algorithm) {
+        byte[] result = cachedHash;
+        if (result != null)
+            return result;
+        if (hasher == null)
+            return null;
+        cachedHash = result = hasher.generate(algorithm);
+        return result;
+    }
+
+    @Override
+    public int hashCode() {
+        int hc = hash;
+        if (hc == 0) {
+            hc = descriptor().hashCode();
+            hc = 43 * hc + readerSupplier.hashCode();
+            hc = 43 * hc + Objects.hashCode(location());
+            hc = 43 * hc + Boolean.hashCode(patched);
+            hc = 43 * hc * Objects.hashCode(recordedHashes);
+            hc = 43 * hc + Objects.hashCode(hasher);
+            if (hc == 0)
+                hc = -1;
+            hash = hc;
+        }
+        return hc;
+    }
+
+    private int hash;
+
+    @Override
+    public boolean equals(Object ob) {
+        if (!(ob instanceof ModuleReferenceImpl))
+            return false;
+        ModuleReferenceImpl that = (ModuleReferenceImpl)ob;
+
+        return Objects.equals(this.descriptor(), that.descriptor())
+                && Objects.equals(this.location(), that.location())
+                && Objects.equals(this.readerSupplier, that.readerSupplier)
+                && this.patched == that.patched
+                && Objects.equals(this.recordedHashes, that.recordedHashes)
+                && Objects.equals(this.hasher, that.hasher);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java	Mon Dec 12 14:31:55 2016 +0000
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.module;
+
+import java.io.File;
+import java.io.IOError;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.Supplier;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.zip.ZipFile;
+
+import jdk.internal.jmod.JmodFile;
+import jdk.internal.misc.JavaLangAccess;
+import jdk.internal.misc.SharedSecrets;
+import jdk.internal.module.ModuleHashes.HashSupplier;
+import jdk.internal.util.jar.VersionedStream;
+import sun.net.www.ParseUtil;
+
+
+/**
+ * A factory for creating ModuleReference implementations where the modules are
+ * packaged as modular JAR file, JMOD files or where the modules are exploded
+ * on the file system.
+ */
+
+class ModuleReferences {
+
+    private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
+
+    private ModuleReferences() { }
+
+    /**
+     * Creates a ModuleReference to a module or to patched module when
+     * creating modules for the boot Layer and --patch-module is specified.
+     */
+    private static ModuleReference newModule(ModuleInfo.Attributes attrs,
+                                             URI uri,
+                                             Supplier<ModuleReader> supplier,
+                                             HashSupplier hasher) {
+
+        ModuleReference mref = new ModuleReferenceImpl(attrs.descriptor(),
+                                                       uri,
+                                                       supplier,
+                                                       false,
+                                                       attrs.recordedHashes(),
+                                                       hasher);
+        if (JLA.getBootLayer() == null)
+            mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
+
+        return mref;
+    }
+
+    /**
+     * Creates a ModuleReference to a module packaged as a modular JAR.
+     */
+    static ModuleReference newJarModule(ModuleInfo.Attributes attrs, Path file) {
+        URI uri = file.toUri();
+        Supplier<ModuleReader> supplier = () -> new JarModuleReader(file, uri);
+        HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a);
+        return newModule(attrs, uri, supplier, hasher);
+    }
+
+    /**
+     * Creates a ModuleReference to a module packaged as a JMOD.
+     */
+    static ModuleReference newJModModule(ModuleInfo.Attributes attrs, Path file) {
+        URI uri = file.toUri();
+        Supplier<ModuleReader> supplier = () -> new JModModuleReader(file, uri);
+        HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a);
+        return newModule(attrs, uri, supplier, hasher);
+    }
+
+    /**
+     * Creates a ModuleReference to an exploded module.
+     */
+    static ModuleReference newExplodedModule(ModuleInfo.Attributes attrs, Path dir) {
+        Supplier<ModuleReader> supplier = () -> new ExplodedModuleReader(dir);
+        return newModule(attrs, dir.toUri(), supplier, null);
+    }
+
+
+    /**
+     * A base module reader that encapsulates machinery required to close the
+     * module reader safely.
+     */
+    static abstract class SafeCloseModuleReader implements ModuleReader {
+
+        // RW lock to support safe close
+        private final ReadWriteLock lock = new ReentrantReadWriteLock();
+        private final Lock readLock = lock.readLock();
+        private final Lock writeLock = lock.writeLock();
+        private boolean closed;
+
+        SafeCloseModuleReader() { }
+
+        /**
+         * Returns a URL to  resource. This method is invoked by the find
+         * method to do the actual work of finding the resource.
+         */
+        abstract Optional<URI> implFind(String name) throws IOException;
+
+        /**
+         * Returns an input stream for reading a resource. This method is
+         * invoked by the open method to do the actual work of opening
+         * an input stream to the resource.
+         */
+        abstract Optional<InputStream> implOpen(String name) throws IOException;
+
+        /**
+         * Returns a stream of the names of resources in the module. This
+         * method is invoked by the list method to do the actual work of
+         * creating the stream.
+         */
+        abstract Stream<String> implList() throws IOException;
+
+        /**
+         * Closes the module reader. This method is invoked by close to do the
+         * actual work of closing the module reader.
+         */
+        abstract void implClose() throws IOException;
+
+        @Override
+        public final Optional<URI> find(String name) throws IOException {
+            readLock.lock();
+            try {
+                if (!closed) {
+                    return implFind(name);
+                } else {
+                    throw new IOException("ModuleReader is closed");
+                }
+            } finally {
+                readLock.unlock();
+            }
+        }
+
+
+        @Override
+        public final Optional<InputStream> open(String name) throws IOException {
+            readLock.lock();
+            try {
+                if (!closed) {
+                    return implOpen(name);
+                } else {
+                    throw new IOException("ModuleReader is closed");
+                }
+            } finally {
+                readLock.unlock();
+            }
+        }
+
+        @Override
+        public final Stream<String> list() throws IOException {
+            readLock.lock();
+            try {
+                if (!closed) {
+                    return implList();
+                } else {
+                    throw new IOException("ModuleReader is closed");
+                }
+            } finally {
+                readLock.unlock();
+            }
+        }
+
+        @Override
+        public final void close() throws IOException {
+            writeLock.lock();
+            try {
+                if (!closed) {
+                    closed = true;
+                    implClose();
+                }
+            } finally {
+                writeLock.unlock();
+            }
+        }
+    }
+
+
+    /**
+     * A ModuleReader for a modular JAR file.
+     */
+    static class JarModuleReader extends SafeCloseModuleReader {
+        private final JarFile jf;
+        private final URI uri;
+
+        static JarFile newJarFile(Path path) {
+            try {
+                return new JarFile(path.toFile(),
+                                   true,               // verify
+                                   ZipFile.OPEN_READ,
+                                   JarFile.runtimeVersion());
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+        }
+
+        JarModuleReader(Path path, URI uri) {
+            this.jf = newJarFile(path);
+            this.uri = uri;
+        }
+
+        private JarEntry getEntry(String name) {
+            return jf.getJarEntry(Objects.requireNonNull(name));
+        }
+
+        @Override
+        Optional<URI> implFind(String name) throws IOException {
+            JarEntry je = getEntry(name);
+            if (je != null) {
+                if (jf.isMultiRelease())
+                    name = SharedSecrets.javaUtilJarAccess().getRealName(jf, je);
+                String encodedPath = ParseUtil.encodePath(name, false);
+                String uris = "jar:" + uri + "!/" + encodedPath;
+                return Optional.of(URI.create(uris));
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        Optional<InputStream> implOpen(String name) throws IOException {
+            JarEntry je = getEntry(name);
+            if (je != null) {
+                return Optional.of(jf.getInputStream(je));
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        Stream<String> implList() throws IOException {
+            // take snapshot to avoid async close
+            List<String> names = VersionedStream.stream(jf)
+                    .filter(e -> !e.isDirectory())
+                    .map(JarEntry::getName)
+                    .collect(Collectors.toList());
+            return names.stream();
+        }
+
+        @Override
+        void implClose() throws IOException {
+            jf.close();
+        }
+    }
+
+
+    /**
+     * A ModuleReader for a JMOD file.
+     */
+    static class JModModuleReader extends SafeCloseModuleReader {
+        private final JmodFile jf;
+        private final URI uri;
+
+        static JmodFile newJmodFile(Path path) {
+            try {
+                return new JmodFile(path);
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+        }
+
+        JModModuleReader(Path path, URI uri) {
+            this.jf = newJmodFile(path);
+            this.uri = uri;
+        }
+
+        private JmodFile.Entry getEntry(String name) {
+            Objects.requireNonNull(name);
+            return jf.getEntry(JmodFile.Section.CLASSES, name);
+        }
+
+        @Override
+        Optional<URI> implFind(String name) {
+            JmodFile.Entry je = getEntry(name);
+            if (je != null) {
+                String encodedPath = ParseUtil.encodePath(name, false);
+                String uris = "jmod:" + uri + "!/" + encodedPath;
+                return Optional.of(URI.create(uris));
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        Optional<InputStream> implOpen(String name) throws IOException {
+            JmodFile.Entry je = getEntry(name);
+            if (je != null) {
+                return Optional.of(jf.getInputStream(je));
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        Stream<String> implList() throws IOException {
+            // take snapshot to avoid async close
+            List<String> names = jf.stream()
+                    .filter(e -> e.section() == JmodFile.Section.CLASSES)
+                    .map(JmodFile.Entry::name)
+                    .collect(Collectors.toList());
+            return names.stream();
+        }
+
+        @Override
+        void implClose() throws IOException {
+            jf.close();
+        }
+    }
+
+
+    /**
+     * A ModuleReader for an exploded module.
+     */
+    static class ExplodedModuleReader implements ModuleReader {
+        private final Path dir;
+        private volatile boolean closed;
+
+        ExplodedModuleReader(Path dir) {
+            this.dir = dir;
+
+            // when running with a security manager then check that the caller
+            // has access to the directory.
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                boolean unused = Files.isDirectory(dir);
+            }
+        }
+
+        /**
+         * Returns a Path to access to the given resource.
+         */
+        private Path toPath(String name) {
+            Path path = Paths.get(name.replace('/', File.separatorChar));
+            if (path.getRoot() == null) {
+                return dir.resolve(path);
+            } else {
+                // drop the root component so that the resource is
+                // located relative to the module directory
+                int n = path.getNameCount();
+                return (n > 0) ? dir.resolve(path.subpath(0, n)) : null;
+            }
+        }
+
+        /**
+         * Throws IOException if the module reader is closed;
+         */
+        private void ensureOpen() throws IOException {
+            if (closed) throw new IOException("ModuleReader is closed");
+        }
+
+        @Override
+        public Optional<URI> find(String name) throws IOException {
+            ensureOpen();
+            Path path = toPath(name);
+            if (path != null && Files.isRegularFile(path)) {
+                try {
+                    return Optional.of(path.toUri());
+                } catch (IOError e) {
+                    throw (IOException) e.getCause();
+                }
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        public Optional<InputStream> open(String name) throws IOException {
+            ensureOpen();
+            Path path = toPath(name);
+            if (path != null && Files.isRegularFile(path)) {
+                return Optional.of(Files.newInputStream(path));
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        public Optional<ByteBuffer> read(String name) throws IOException {
+            ensureOpen();
+            Path path = toPath(name);
+            if (path != null && Files.isRegularFile(path)) {
+                return Optional.of(ByteBuffer.wrap(Files.readAllBytes(path)));
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        public Stream<String> list() throws IOException {
+            ensureOpen();
+            // sym links not followed
+            return Files.find(dir, Integer.MAX_VALUE,
+                              (path, attrs) -> attrs.isRegularFile())
+                    .map(f -> dir.relativize(f)
+                                 .toString()
+                                 .replace(File.separatorChar, '/'));
+        }
+
+        @Override
+        public void close() {
+            closed = true;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/SystemModuleFinder.java	Mon Dec 12 14:31:55 2016 +0000
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.module;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.net.URI;
+import java.net.URLConnection;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import jdk.internal.jimage.ImageLocation;
+import jdk.internal.jimage.ImageReader;
+import jdk.internal.jimage.ImageReaderFactory;
+import jdk.internal.misc.JavaNetUriAccess;
+import jdk.internal.misc.SharedSecrets;
+import jdk.internal.module.ModuleHashes.HashSupplier;
+import jdk.internal.perf.PerfCounter;
+
+/**
+ * A {@code ModuleFinder} that finds modules that are linked into the
+ * run-time image.
+ *
+ * The modules linked into the run-time image are assumed to have the
+ * Packages attribute.
+ */
+
+public class SystemModuleFinder implements ModuleFinder {
+
+    private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();
+
+    private static final PerfCounter initTime
+        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.initTime");
+    private static final PerfCounter moduleCount
+        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.modules");
+    private static final PerfCounter packageCount
+        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.packages");
+    private static final PerfCounter exportsCount
+        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.exports");
+    // ImageReader used to access all modules in the image
+    private static final ImageReader imageReader;
+
+    // the set of modules in the run-time image
+    private static final Set<ModuleReference> modules;
+
+    // maps module name to module reference
+    private static final Map<String, ModuleReference> nameToModule;
+
+    /**
+     * For now, the module references are created eagerly on the assumption
+     * that service binding will require all modules to be located.
+     */
+    static {
+        long t0 = System.nanoTime();
+        imageReader = ImageReaderFactory.getImageReader();
+
+        String[] names = moduleNames();
+        ModuleDescriptor[] descriptors = descriptors(names);
+
+        int n = names.length;
+        moduleCount.add(n);
+
+        ModuleReference[] mods = new ModuleReference[n];
+
+        @SuppressWarnings(value = {"rawtypes", "unchecked"})
+        Entry<String, ModuleReference>[] map
+            = (Entry<String, ModuleReference>[])new Entry[n];
+
+        for (int i = 0; i < n; i++) {
+            ModuleDescriptor md = descriptors[i];
+
+            // create the ModuleReference
+            ModuleReference mref = toModuleReference(md);
+
+            mods[i] = mref;
+            map[i] = Map.entry(names[i], mref);
+
+            // counters
+            packageCount.add(md.packages().size());
+            exportsCount.add(md.exports().size());
+        }
+
+        modules = Set.of(mods);
+        nameToModule = Map.ofEntries(map);
+
+        initTime.addElapsedTimeFrom(t0);
+    }
+
+    /*
+     * Returns an array of ModuleDescriptor of the given module names.
+     *
+     * This obtains ModuleDescriptors from SystemModules class that is generated
+     * from the jlink system-modules plugin.  ModuleDescriptors have already
+     * been validated at link time.
+     *
+     * If java.base is patched, or fastpath is disabled for troubleshooting
+     * purpose, it will fall back to find system modules via jrt file system.
+     */
+    private static ModuleDescriptor[] descriptors(String[] names) {
+        // fastpath is enabled by default.
+        // It can be disabled for troubleshooting purpose.
+        boolean disabled =
+            System.getProperty("jdk.system.module.finder.disabledFastPath") != null;
+
+        // fast loading of ModuleDescriptor of system modules
+        if (isFastPathSupported() && !disabled)
+            return SystemModules.modules();
+
+        // if fast loading of ModuleDescriptors is disabled
+        // fallback to read module-info.class
+        ModuleDescriptor[] descriptors = new ModuleDescriptor[names.length];
+        for (int i = 0; i < names.length; i++) {
+            String mn = names[i];
+            ImageLocation loc = imageReader.findLocation(mn, "module-info.class");
+            descriptors[i] = ModuleDescriptor.read(imageReader.getResourceBuffer(loc));
+        }
+        return descriptors;
+    }
+
+    private static boolean isFastPathSupported() {
+       return SystemModules.MODULE_NAMES.length > 0;
+    }
+
+    private static String[] moduleNames() {
+        if (isFastPathSupported())
+            // module names recorded at link time
+            return SystemModules.MODULE_NAMES;
+
+        // this happens when java.base is patched with java.base
+        // from an exploded image
+        return imageReader.getModuleNames();
+    }
+
+    private static ModuleReference toModuleReference(ModuleDescriptor md) {
+        String mn = md.name();
+        URI uri = JNUA.create("jrt", "/".concat(mn));
+
+        Supplier<ModuleReader> readerSupplier = new Supplier<>() {
+            @Override
+            public ModuleReader get() {
+                return new ImageModuleReader(mn, uri);
+            }
+        };
+
+        ModuleReference mref = new ModuleReferenceImpl(md, uri, readerSupplier);
+
+        // may need a reference to a patched module if --patch-module specified
+        mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
+
+        return mref;
+    }
+
+    private static HashSupplier hashSupplier(int index, String name) {
+        if (isFastPathSupported()) {
+            return new HashSupplier() {
+                @Override
+                public byte[] generate(String algorithm) {
+                    return SystemModules.MODULES_TO_HASH[index];
+                }
+            };
+        } else {
+            return Hashes.hashFor(name);
+        }
+    }
+
+    /*
+     * This helper class is only used when SystemModules is patched.
+     * It will get the recorded hashes from module-info.class.
+     */
+    private static class Hashes {
+        static Map<String, byte[]> hashes = new HashMap<>();
+
+        static void add(ModuleHashes recordedHashes) {
+            hashes.putAll(recordedHashes.hashes());
+        }
+
+        static HashSupplier hashFor(String name) {
+            if (!hashes.containsKey(name))
+                return null;
+
+            return new HashSupplier() {
+                @Override
+                public byte[] generate(String algorithm) {
+                    return hashes.get(name);
+                }
+            };
+        }
+    }
+
+    public SystemModuleFinder() { }
+
+    @Override
+    public Optional<ModuleReference> find(String name) {
+        Objects.requireNonNull(name);
+        return Optional.ofNullable(nameToModule.get(name));
+    }
+
+    @Override
+    public Set<ModuleReference> findAll() {
+        return modules;
+    }
+
+
+    /**
+     * A ModuleReader for reading resources from a module linked into the
+     * run-time image.
+     */
+    static class ImageModuleReader implements ModuleReader {
+        private final String module;
+        private volatile boolean closed;
+
+        /**
+         * If there is a security manager set then check permission to
+         * connect to the run-time image.
+         */
+        private static void checkPermissionToConnect(URI uri) {
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                try {
+                    URLConnection uc = uri.toURL().openConnection();
+                    sm.checkPermission(uc.getPermission());
+                } catch (IOException ioe) {
+                    throw new UncheckedIOException(ioe);
+                }
+            }
+        }
+
+        ImageModuleReader(String module, URI uri) {
+            checkPermissionToConnect(uri);
+            this.module = module;
+        }
+
+        /**
+         * Returns the ImageLocation for the given resource, {@code null}
+         * if not found.
+         */
+        private ImageLocation findImageLocation(String name) throws IOException {
+            Objects.requireNonNull(name);
+            if (closed)
+                throw new IOException("ModuleReader is closed");
+            if (imageReader != null) {
+                return imageReader.findLocation(module, name);
+            } else {
+                // not an images build
+                return null;
+            }
+        }
+
+        @Override
+        public Optional<URI> find(String name) throws IOException {
+            ImageLocation location = findImageLocation(name);
+            if (location != null) {
+                URI u = URI.create("jrt:/" + module + "/" + name);
+                return Optional.of(u);
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        public Optional<InputStream> open(String name) throws IOException {
+            return read(name).map(this::toInputStream);
+        }
+
+        private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
+            try {
+                int rem = bb.remaining();
+                byte[] bytes = new byte[rem];
+                bb.get(bytes);
+                return new ByteArrayInputStream(bytes);
+            } finally {
+                release(bb);
+            }
+        }
+
+        @Override
+        public Optional<ByteBuffer> read(String name) throws IOException {
+            ImageLocation location = findImageLocation(name);
+            if (location != null) {
+                return Optional.of(imageReader.getResourceBuffer(location));
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        public void release(ByteBuffer bb) {
+            Objects.requireNonNull(bb);
+            ImageReader.releaseByteBuffer(bb);
+        }
+
+        @Override
+        public Stream<String> list() throws IOException {
+            if (closed)
+                throw new IOException("ModuleReader is closed");
+
+            Spliterator<String> s = new ModuleContentSpliterator(module);
+            return StreamSupport.stream(s, false);
+        }
+
+        @Override
+        public void close() {
+            // nothing else to do
+            closed = true;
+        }
+    }
+
+    /**
+     * A Spliterator for traversing the resources of a module linked into the
+     * run-time image.
+     */
+    static class ModuleContentSpliterator implements Spliterator<String> {
+        final String moduleRoot;
+        final Deque<ImageReader.Node> stack;
+        Iterator<ImageReader.Node> iterator;
+
+        ModuleContentSpliterator(String module) throws IOException {
+            moduleRoot = "/modules/" + module;
+            stack = new ArrayDeque<>();
+
+            // push the root node to the stack to get started
+            ImageReader.Node dir = imageReader.findNode(moduleRoot);
+            if (dir == null || !dir.isDirectory())
+                throw new IOException(moduleRoot + " not a directory");
+            stack.push(dir);
+            iterator = Collections.emptyIterator();
+        }
+
+        /**
+         * Returns the name of the next non-directory node or {@code null} if
+         * there are no remaining nodes to visit.
+         */
+        private String next() throws IOException {
+            for (;;) {
+                while (iterator.hasNext()) {
+                    ImageReader.Node node = iterator.next();
+                    String name = node.getName();
+                    if (node.isDirectory()) {
+                        // build node
+                        ImageReader.Node dir = imageReader.findNode(name);
+                        assert dir.isDirectory();
+                        stack.push(dir);
+                    } else {
+                        // strip /modules/$MODULE/ prefix
+                        return name.substring(moduleRoot.length() + 1);
+                    }
+                }
+
+                if (stack.isEmpty()) {
+                    return null;
+                } else {
+                    ImageReader.Node dir = stack.poll();
+                    assert dir.isDirectory();
+                    iterator = dir.getChildren().iterator();
+                }
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(Consumer<? super String> action) {
+            String next;
+            try {
+                next = next();
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+            if (next != null) {
+                action.accept(next);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public Spliterator<String> trySplit() {
+            return null;
+        }
+
+        @Override
+        public int characteristics() {
+            return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE;
+        }
+
+        @Override
+        public long estimateSize() {
+            return Long.MAX_VALUE;
+        }
+    }
+}
--- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	Mon Dec 12 14:31:55 2016 +0000
@@ -47,7 +47,6 @@
 import java.util.*;
 import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.Supplier;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -60,6 +59,7 @@
 import jdk.internal.misc.JavaLangModuleAccess;
 import jdk.internal.module.Checks;
 import jdk.internal.module.ModuleHashes;
+import jdk.internal.module.ModuleInfo;
 import jdk.internal.module.ModuleInfoExtender;
 import jdk.internal.util.jar.JarIndex;
 
@@ -2001,7 +2001,10 @@
     private void printModuleDescriptor(InputStream entryInputStream)
         throws IOException
     {
-        ModuleDescriptor md = ModuleDescriptor.read(entryInputStream);
+        ModuleInfo.Attributes attrs = ModuleInfo.read(entryInputStream, null);
+        ModuleDescriptor md = attrs.descriptor();
+        ModuleHashes hashes = attrs.recordedHashes();
+
         StringBuilder sb = new StringBuilder();
         sb.append("\n");
         if (md.isOpen())
@@ -2048,9 +2051,24 @@
 
         md.osVersion().ifPresent(v -> sb.append("\n  operating-system-version " + v));
 
+        if (hashes != null) {
+            hashes.names().stream().sorted().forEach(
+                    mod -> sb.append("\n  hashes ").append(mod).append(" ")
+                             .append(hashes.algorithm()).append(" ")
+                             .append(toHex(hashes.hashFor(mod))));
+        }
+
         output(sb.toString());
     }
 
+    private static String toHex(byte[] ba) {
+        StringBuilder sb = new StringBuilder(ba.length);
+        for (byte b: ba) {
+            sb.append(String.format("%02x", b & 0xff));
+        }
+        return sb.toString();
+    }
+
     private static String toBinaryName(String classname) {
         return (classname.replace('.', '/')) + ".class";
     }
@@ -2227,13 +2245,12 @@
             // Create a module finder that finds the modular JAR
             // being created/updated
             URI uri = Paths.get(fname).toUri();
-            ModuleReference mref = new ModuleReference(descriptor, uri,
-                new Supplier<>() {
-                    @Override
-                    public ModuleReader get() {
-                        throw new UnsupportedOperationException("should not reach here");
-                    }
-                });
+            ModuleReference mref = new ModuleReference(descriptor, uri) {
+                @Override
+                public ModuleReader open() {
+                    throw new UnsupportedOperationException("should not reach here");
+                }
+            };
 
             // Compose a module finder with the module path and
             // the modular JAR being created or updated
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Mon Dec 12 14:31:55 2016 +0000
@@ -53,6 +53,7 @@
 import jdk.tools.jlink.plugin.PluginException;
 import jdk.tools.jlink.builder.DefaultImageBuilder;
 import jdk.tools.jlink.plugin.Plugin;
+import jdk.internal.module.ModulePath;
 import jdk.internal.misc.SharedSecrets;
 
 /**
@@ -344,9 +345,7 @@
      */
     private ModuleFinder modulePathFinder() {
         Path[] entries = options.modulePath.toArray(new Path[0]);
-        ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
-            .newModulePath(Runtime.version(), true, entries);
-
+        ModuleFinder finder = new ModulePath(Runtime.version(), true, entries);
         if (!options.limitMods.isEmpty()) {
             finder = limitFinder(finder, options.limitMods, Collections.emptySet());
         }
@@ -364,8 +363,7 @@
                                                Set<String> roots)
     {
         Path[] entries = paths.toArray(new Path[0]);
-        ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
-            .newModulePath(Runtime.version(), true, entries);
+        ModuleFinder finder = new ModulePath(Runtime.version(), true, entries);
 
         // if limitmods is specified then limit the universe
         if (!limitMods.isEmpty()) {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolConfiguration.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolConfiguration.java	Mon Dec 12 14:31:55 2016 +0000
@@ -28,6 +28,7 @@
 import java.lang.module.Configuration;
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReader;
 import java.lang.module.ModuleReference;
 import java.io.IOException;
 import java.io.UncheckedIOException;
@@ -78,10 +79,12 @@
     }
 
     private static ModuleReference moduleReference(ModuleDescriptor desc) {
-        return new ModuleReference(desc, null, () -> {
-            IOException ioe = new IOException("<module reader unsupported>");
-            throw new UncheckedIOException(ioe);
-        });
+        return new ModuleReference(desc, null) {
+            @Override
+            public ModuleReader open() {
+                throw new UnsupportedOperationException();
+            }
+        };
     }
 
     private static Map<String, ModuleReference> allModRefs(ResourcePool pool) {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java	Mon Dec 12 14:31:55 2016 +0000
@@ -58,7 +58,7 @@
 import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
 import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;
 import jdk.tools.jlink.internal.plugins.StripDebugPlugin;
-import jdk.internal.misc.SharedSecrets;
+import jdk.internal.module.ModulePath;
 
 /**
  *
@@ -765,9 +765,7 @@
     static Layer createPluginsLayer(List<Path> paths) {
 
         Path[] dirs = paths.toArray(new Path[0]);
-        ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
-            .newModulePath(Runtime.version(), true, dirs);
-
+        ModuleFinder finder = new ModulePath(Runtime.version(), true, dirs);
         Configuration bootConfiguration = Layer.boot().configuration();
         try {
             Configuration cf = bootConfiguration
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java	Mon Dec 12 14:31:55 2016 +0000
@@ -44,6 +44,8 @@
 import jdk.internal.misc.JavaLangModuleAccess;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.module.Checks;
+import jdk.internal.module.ModuleHashes;
+import jdk.internal.module.ModuleInfo;
 import jdk.internal.module.ModuleInfoExtender;
 import jdk.internal.module.SystemModules;
 import jdk.internal.org.objectweb.asm.ClassWriter;
@@ -127,7 +129,9 @@
             assert module.name().equals(data.moduleName());
             try {
                 ByteArrayInputStream bain = new ByteArrayInputStream(data.contentBytes());
-                ModuleDescriptor md = ModuleDescriptor.read(bain);
+                ModuleInfo.Attributes attrs = ModuleInfo.read(bain, null);
+                ModuleDescriptor md = attrs.descriptor();
+                ModuleHashes hashes = attrs.recordedHashes();
                 validateNames(md);
 
                 Set<String> packages = module.packages();
@@ -142,6 +146,11 @@
                     data = data.copyWithContent(minfoWriter.getBytes());
                 }
                 out.add(data);
+
+                if (hashes != null) {
+                    generator.addHashes(hashes);
+                }
+
             } catch (IOException e) {
                 throw new PluginException(e);
             }
@@ -385,6 +394,13 @@
             dedupSetBuilder.stringSet(md.uses());
         }
 
+        /**
+         * Adds the module hashes to the modulesToHash map
+         */
+        public void addHashes(ModuleHashes hashes) {
+            modulesToHash.putAll(hashes.hashes());
+        }
+
         /*
          * Generate bytecode for SystemModules
          */
--- a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Mon Dec 12 14:31:55 2016 +0000
@@ -101,9 +101,10 @@
 import jdk.internal.joptsimple.ValueConverter;
 import jdk.internal.loader.ResourceHelper;
 import jdk.internal.misc.JavaLangModuleAccess;
-import jdk.internal.misc.SharedSecrets;
 import jdk.internal.module.ModuleHashes;
+import jdk.internal.module.ModuleInfo;
 import jdk.internal.module.ModuleInfoExtender;
+import jdk.internal.module.ModulePath;
 import jdk.tools.jlink.internal.Utils;
 
 import static java.util.stream.Collectors.joining;
@@ -255,8 +256,8 @@
     private boolean describe() throws IOException {
         try (JmodFile jf = new JmodFile(options.jmodFile)) {
             try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
-                ModuleDescriptor md = ModuleDescriptor.read(in);
-                printModuleDescriptor(md);
+                ModuleInfo.Attributes attrs = ModuleInfo.read(in, null);
+                printModuleDescriptor(attrs.descriptor(), attrs.recordedHashes());
                 return true;
             } catch (IOException e) {
                 throw new CommandException("err.module.descriptor.not.found");
@@ -270,9 +271,7 @@
                   .collect(joining(" "));
     }
 
-    private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
-
-    private void printModuleDescriptor(ModuleDescriptor md)
+    private void printModuleDescriptor(ModuleDescriptor md, ModuleHashes hashes)
         throws IOException
     {
         StringBuilder sb = new StringBuilder();
@@ -318,9 +317,24 @@
 
         md.osVersion().ifPresent(v -> sb.append("\n  operating-system-version " + v));
 
+        if (hashes != null) {
+            hashes.names().stream().sorted().forEach(
+                    mod -> sb.append("\n  hashes ").append(mod).append(" ")
+                             .append(hashes.algorithm()).append(" ")
+                             .append(toHex(hashes.hashFor(mod))));
+        }
+
         out.println(sb.toString());
     }
 
+    private String toHex(byte[] ba) {
+        StringBuilder sb = new StringBuilder(ba.length);
+        for (byte b: ba) {
+            sb.append(String.format("%02x", b & 0xff));
+        }
+        return sb.toString();
+    }
+
     private boolean create() throws IOException {
         JmodFileWriter jmod = new JmodFileWriter();
 
@@ -497,12 +511,12 @@
                 }
 
                 URI uri = options.jmodFile.toUri();
-                ModuleReference mref = new ModuleReference(descriptor, uri, new Supplier<>() {
+                ModuleReference mref = new ModuleReference(descriptor, uri) {
                     @Override
-                    public ModuleReader get() {
+                    public ModuleReader open() {
                         throw new UnsupportedOperationException();
                     }
-                });
+                };
 
                 // compose a module finder with the module path and also
                 // a module finder that can find the jmod file being created
@@ -1309,7 +1323,7 @@
                 options.manPages = opts.valuesOf(manPages);
             if (opts.has(modulePath)) {
                 Path[] dirs = opts.valuesOf(modulePath).toArray(new Path[0]);
-                options.moduleFinder = JLMA.newModulePath(Runtime.version(), true, dirs);
+                options.moduleFinder = new ModulePath(Runtime.version(), true, dirs);
             }
             if (opts.has(moduleVersion))
                 options.moduleVersion = opts.valueOf(moduleVersion);
--- a/test/java/lang/module/ModuleDescriptorTest.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/test/java/lang/module/ModuleDescriptorTest.java	Mon Dec 12 14:31:55 2016 +0000
@@ -24,6 +24,7 @@
 /**
  * @test
  * @modules java.base/java.lang.module:open
+ *          java.base/jdk.internal.misc
  *          java.base/jdk.internal.module
  * @run testng ModuleDescriptorTest
  * @summary Basic test for java.lang.module.ModuleDescriptor and its builder
@@ -41,19 +42,18 @@
 import java.lang.module.ModuleDescriptor.Provides;
 import java.lang.module.ModuleDescriptor.Requires.Modifier;
 import java.lang.module.ModuleDescriptor.Version;
-import java.lang.reflect.Constructor;
 import java.lang.reflect.Module;
 import java.nio.ByteBuffer;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
 import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
 
+import jdk.internal.misc.SharedSecrets;
 import jdk.internal.module.ModuleInfoWriter;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
@@ -76,6 +76,7 @@
             { "1foo.bar",       null },
             { "foo.1bar",       null },
             { "foo.[bar]",      null },
+            { "foo..bar",       null },
             { "foo.bar.1",      null },
             { "foo.bar.1gus",   null },
             { "foo.bar.[gus]",  null },
@@ -1029,11 +1030,8 @@
      * Java Language.
      */
     public void testRead2() throws Exception {
-        // use non-public constructor to create a Builder that is not strict
-        Constructor<?> ctor = Builder.class.getDeclaredConstructor(String.class, boolean.class);
-        ctor.setAccessible(true);
-
-        Builder builder = (ModuleDescriptor.Builder) ctor.newInstance("m?1", false);
+        Builder builder = SharedSecrets.getJavaLangModuleAccess()
+                .newModuleBuilder("m?1", false, false, false);
         ModuleDescriptor descriptor = builder
                 .requires("java.base")
                 .requires("-m1")
--- a/test/java/lang/module/ModuleReader/ModuleReaderTest.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/test/java/lang/module/ModuleReader/ModuleReaderTest.java	Mon Dec 12 14:31:55 2016 +0000
@@ -24,7 +24,7 @@
 /**
  * @test
  * @library /lib/testlibrary
- * @modules java.base/jdk.internal.misc
+ * @modules java.base/jdk.internal.module
  *          jdk.compiler
  * @build ModuleReaderTest CompilerUtils JarUtils
  * @run testng ModuleReaderTest
@@ -53,7 +53,7 @@
 import java.util.stream.Collectors;
 import java.util.spi.ToolProvider;
 
-import jdk.internal.misc.SharedSecrets;
+import jdk.internal.module.ModulePath;
 
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
@@ -216,9 +216,7 @@
      */
     void test(Path mp) throws IOException {
 
-        ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
-            .newModulePath(Runtime.version(), true, mp);
-
+        ModuleFinder finder = new ModulePath(Runtime.version(), true, mp);
         ModuleReference mref = finder.find(TEST_MODULE).get();
         ModuleReader reader = mref.open();
 
--- a/test/java/lang/module/ModuleReferenceTest.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/test/java/lang/module/ModuleReferenceTest.java	Mon Dec 12 14:31:55 2016 +0000
@@ -31,7 +31,6 @@
 import java.lang.module.ModuleReader;
 import java.lang.module.ModuleReference;
 import java.net.URI;
-import java.util.function.Supplier;
 
 import org.testng.annotations.Test;
 import static org.testng.Assert.*;
@@ -39,8 +38,13 @@
 @Test
 public class ModuleReferenceTest {
 
-    private Supplier<ModuleReader> makeSupplier() {
-        return () -> { throw new UnsupportedOperationException(); };
+    private ModuleReference newModuleReference(ModuleDescriptor descriptor, URI uri) {
+        return new ModuleReference(descriptor, uri) {
+            @Override
+            public ModuleReader open() {
+                throw new UnsupportedOperationException();
+            }
+        };
     }
 
     public void testBasic() throws Exception {
@@ -53,25 +57,16 @@
 
         URI uri = URI.create("module:/m");
 
-        Supplier<ModuleReader> supplier = makeSupplier();
-
-        ModuleReference mref = new ModuleReference(descriptor, uri, supplier);
+        ModuleReference mref = newModuleReference(descriptor, uri);
 
         assertTrue(mref.descriptor().equals(descriptor));
         assertTrue(mref.location().get().equals(uri));
-
-        // check that the supplier is called
-        try {
-            mref.open();
-            assertTrue(false);
-        } catch (UnsupportedOperationException expected) { }
     }
 
-
     @Test(expectedExceptions = { NullPointerException.class })
     public void testNullDescriptor() throws Exception {
         URI location = URI.create("module:/m");
-        new ModuleReference(null, location, makeSupplier());
+        newModuleReference(null, location);
     }
 
     public void testNullLocation() {
@@ -79,55 +74,8 @@
             = ModuleDescriptor.module("m")
                 .exports("p")
                 .build();
-        Supplier<ModuleReader> supplier = makeSupplier();
-        ModuleReference mref = new ModuleReference(descriptor, null, supplier);
+        ModuleReference mref = newModuleReference(descriptor, null);
         assertTrue(!mref.location().isPresent());
     }
 
-    @Test(expectedExceptions = { NullPointerException.class })
-    public void testNullSupplier() throws Exception {
-        ModuleDescriptor descriptor = ModuleDescriptor.module("m").build();
-        URI location = URI.create("module:/m");
-        new ModuleReference(descriptor, location, null);
-    }
-
-
-    public void testEqualsAndHashCode() {
-        ModuleDescriptor descriptor1
-            = ModuleDescriptor.module("m1")
-                .exports("p")
-                .build();
-        ModuleDescriptor descriptor2
-            = ModuleDescriptor.module("m1")
-                .exports("p")
-                .build();
-
-        URI uri = URI.create("module:/m1");
-        Supplier<ModuleReader> supplier = makeSupplier();
-
-        ModuleReference mref1 = new ModuleReference(descriptor1, uri, supplier);
-        ModuleReference mref2 = new ModuleReference(descriptor2, uri, supplier);
-        ModuleReference mref3 = new ModuleReference(descriptor1, null, supplier);
-
-        assertTrue(mref1.equals(mref1));
-        assertTrue(mref1.equals(mref2));
-        assertTrue(mref2.equals(mref1));
-        assertTrue(mref1.hashCode() == mref2.hashCode());
-
-        assertTrue(mref3.equals(mref3));
-        assertFalse(mref3.equals(mref1));
-        assertFalse(mref1.equals(mref3));
-    }
-
-
-    public void testToString() {
-        ModuleDescriptor descriptor = ModuleDescriptor.module("m1").build();
-        URI uri = URI.create("module:/m1");
-        Supplier<ModuleReader> supplier = makeSupplier();
-        ModuleReference mref = new ModuleReference(descriptor, uri, supplier);
-        String s = mref.toString();
-        assertTrue(s.contains("m1"));
-        assertTrue(s.contains(uri.toString()));
-    }
-
 }
--- a/test/lib/testlibrary/ModuleUtils.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/test/lib/testlibrary/ModuleUtils.java	Mon Dec 12 14:31:55 2016 +0000
@@ -32,7 +32,6 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 
 
 /**
@@ -58,12 +57,13 @@
 
             URI uri = URI.create("module:/" + name);
 
-            Supplier<ModuleReader> supplier = () -> {
-                throw new UnsupportedOperationException();
+            ModuleReference mref = new ModuleReference(descriptor, uri) {
+                @Override
+                public ModuleReader open() {
+                    throw new UnsupportedOperationException();
+                }
             };
 
-            ModuleReference mref = new ModuleReference(descriptor, uri, supplier);
-
             namesToReference.put(name, mref);
         }
 
--- a/test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java	Mon Dec 12 14:31:55 2016 +0000
@@ -25,16 +25,18 @@
 
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleDescriptor.Exports;
-import java.lang.module.ModuleDescriptor.Requires;
 import java.lang.module.ModuleDescriptor.Provides;
+import java.lang.module.ModuleReference;
+import java.lang.module.ResolvedModule;
+import java.lang.reflect.Module;
 import java.util.Optional;
 import java.util.StringJoiner;
 import java.util.HashSet;
 import java.util.Set;
-
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.misc.JavaLangModuleAccess;
 import jdk.internal.module.ModuleHashes;
+import jdk.internal.module.ModuleReferenceImpl;
 import jdk.test.bar.internal.Message;
 
 public class Bar {
@@ -71,9 +73,14 @@
         if (!sj.toString().equals(""))
             System.out.println("contains:" + sj.toString());
 
-        ModuleDescriptor foo = jdk.test.foo.Foo.class.getModule().getDescriptor();
-        JavaLangModuleAccess jlma = SharedSecrets.getJavaLangModuleAccess();
-        Optional<ModuleHashes> oHashes = jlma.hashes(foo);
-        System.out.println("hashes:" + oHashes.get().hashFor("bar"));
+
+        Module foo = jdk.test.foo.Foo.class.getModule();
+        Optional<ResolvedModule> om = foo.getLayer().configuration().findModule(foo.getName());
+        assert om.isPresent();
+        ModuleReference mref = om.get().reference();
+        assert mref instanceof ModuleReferenceImpl;
+        ModuleHashes hashes = ((ModuleReferenceImpl) mref).recordedHashes();
+        assert hashes != null;
+        System.out.println("hashes:" + hashes.hashFor("bar"));
     }
 }
--- a/test/tools/jmod/hashes/HashesTest.java	Sat Dec 10 05:22:58 2016 +0000
+++ b/test/tools/jmod/hashes/HashesTest.java	Mon Dec 12 14:31:55 2016 +0000
@@ -50,14 +50,13 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.Optional;
 import java.util.Set;
 import java.util.spi.ToolProvider;
 import java.util.stream.Collectors;
 
-import jdk.internal.misc.SharedSecrets;
-import jdk.internal.misc.JavaLangModuleAccess;
+import jdk.internal.module.ModuleInfo;
 import jdk.internal.module.ModuleHashes;
+import jdk.internal.module.ModulePath;
 
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
@@ -103,54 +102,53 @@
     @Test
     public void test() throws Exception {
         for (String mn : modules) {
-            assertFalse(hashes(mn).isPresent());
+            assertTrue(hashes(mn) == null);
         }
 
         // hash m1 in m2
         jmod("m2", "--module-path", jmods.toString(), "--hash-modules", "m1");
-        checkHashes(hashes("m2").get(), "m1");
+        checkHashes(hashes("m2"), "m1");
 
         // hash m1 in m2
         jmod("m2", "--module-path", jmods.toString(), "--hash-modules", ".*");
-        checkHashes(hashes("m2").get(), "m1");
+        checkHashes(hashes("m2"), "m1");
 
         // create m2.jmod with no hash
         jmod("m2");
         // run jmod hash command to hash m1 in m2 and m3
         runJmod(Arrays.asList("hash", "--module-path", jmods.toString(),
                 "--hash-modules", ".*"));
-        checkHashes(hashes("m2").get(), "m1");
-        checkHashes(hashes("m3").get(), "m1");
+        checkHashes(hashes("m2"), "m1");
+        checkHashes(hashes("m3"), "m1");
 
         jmod("org.bar");
         jmod("org.foo");
 
         jmod("org.bar", "--module-path", jmods.toString(), "--hash-modules", "org.*");
-        checkHashes(hashes("org.bar").get(), "org.foo");
+        checkHashes(hashes("org.bar"), "org.foo");
 
         jmod("m3", "--module-path", jmods.toString(), "--hash-modules", ".*");
-        checkHashes(hashes("m3").get(), "org.foo", "org.bar", "m1");
+        checkHashes(hashes("m3"), "org.foo", "org.bar", "m1");
     }
 
     private void checkHashes(ModuleHashes hashes, String... hashModules) {
         assertTrue(hashes.names().equals(Set.of(hashModules)));
     }
 
-    private Optional<ModuleHashes> hashes(String name) throws Exception {
-        ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
-            .newModulePath(Runtime.version(), true, jmods.resolve(name + ".jmod"));
+    private ModuleHashes hashes(String name) throws Exception {
+        ModuleFinder finder = new ModulePath(Runtime.version(),
+                                             true,
+                                             jmods.resolve(name + ".jmod"));
         ModuleReference mref = finder.find(name).orElseThrow(RuntimeException::new);
         ModuleReader reader = mref.open();
         try (InputStream in = reader.open("module-info.class").get()) {
-            ModuleDescriptor md = ModuleDescriptor.read(in);
-            JavaLangModuleAccess jmla = SharedSecrets.getJavaLangModuleAccess();
-            Optional<ModuleHashes> hashes = jmla.hashes(md);
+            ModuleHashes hashes = ModuleInfo.read(in, null).recordedHashes();
             System.out.format("hashes in module %s %s%n", name,
-                              hashes.isPresent() ? "present" : "absent");
-            if (hashes.isPresent()) {
-                hashes.get().names().stream()
+                    (hashes != null) ? "present" : "absent");
+            if (hashes != null) {
+                hashes.names().stream()
                     .sorted()
-                    .forEach(n -> System.out.format("  %s %s%n", n, hashes.get().hashFor(n)));
+                    .forEach(n -> System.out.format("  %s %s%n", n, hashes.hashFor(n)));
             }
             return hashes;
         } finally {