Showing changes in java/12/java.base/java/lang/Package.java (new version) from java/8/java/lang/Package.java (old version). +282 -347
 /*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, 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;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
-import java.io.InputStream;
-import java.util.Enumeration;
-
-import java.util.StringTokenizer;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.net.URL;
 import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.Objects;
 
-import java.util.jar.JarInputStream;
-import java.util.jar.Manifest;
-import java.util.jar.Attributes;
-import java.util.jar.Attributes.Name;
-import java.util.jar.JarException;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Iterator;
+import jdk.internal.loader.BootLoader;
+import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.Reflection;
 
-import sun.net.www.ParseUtil;
-import sun.reflect.CallerSensitive;
-import sun.reflect.Reflection;
-
-import java.lang.annotation.Annotation;
 
 /**
- * {@code Package} objects contain version information
- * about the implementation and specification of a Java package.
- * This versioning information is retrieved and made available
- * by the {@link ClassLoader} instance that
- * loaded the class(es).  Typically, it is stored in the manifest that is
- * distributed with the classes.
+ * Represents metadata about a run-time package associated with a class loader.
+ * Metadata includes annotations, versioning, and sealing.
+ * <p>
+ * Annotations for the run-time package are read from {@code package-info.class}
+ * at the same code source as classes in the run-time package.
+ * <p>
+ * The set of classes that make up the run-time package may implement a
+ * particular specification. The specification title, version, and vendor
+ * (indicating the owner/maintainer of the specification) can be provided
+ * when the {@code Package} is defined. An application can ask if the
+ * {@code Package} is compatible with a particular specification version
+ * by using the {@link #isCompatibleWith Package.isCompatibleWith(String)}
+ * method. In addition, information about the actual classes that make up the
+ * run-time package can be provided when the Package is defined.
+ * This information consists of an implementation title, version, and vendor
+ * (indicating the supplier of the classes).
+ * <p>
+ * A {@code Package} may be explicitly defined with
+ * the {@link ClassLoader#definePackage(String, String, String, String,
+ * String, String, String, URL)} method.
+ * The caller supplies the specification and implementation titles, versions, and
+ * vendors. The caller also indicates whether the package is
+ * {@linkplain java.util.jar.Attributes.Name#SEALED sealed}.
+ * If a {@code Package} is not explicitly defined for a run-time package when
+ * a class in that run-time package is defined, then a {@code Package} is
+ * automatically defined by the class's defining class loader, as follows.
+ * <p>
+ * A {@code Package} automatically defined for classes in a named module has
+ * the following properties:
+ * <ul>
+ * <li>The name of the package is derived from the {@linkplain Class#getName() binary names}
+ *     of the classes. Since classes in a named module must be in a named package,
+ *     the derived name is never empty.</li>
+ * <li>The package is sealed with the {@linkplain java.lang.module.ModuleReference#location()
+ *     module location} as the code source, if known.</li>
+ * <li>The specification and implementation titles, versions, and vendors
+ *     are unspecified.</li>
+ * <li>Any annotations on the package are read from {@code package-info.class}
+ *     as specified above.</li>
+ * </ul>
+ * <p>
+ * A {@code Package} automatically defined for classes in an unnamed module
+ * has the following properties:
+ * <ul>
+ * <li>The name of the package is either {@code ""} (for classes in an unnamed package)
+ *     or derived from the {@linkplain Class#getName() binary names} of the classes
+ *     (for classes in a named package).</li>
+ * <li>The package is not sealed.</li>
+ * <li>The specification and implementation titles, versions, and vendors
+ *     are unspecified.</li>
+ * <li>Any annotations on the package are read from {@code package-info.class}
+ *     as specified above.</li>
+ * </ul>
  *
- * <p>The set of classes that make up the package may implement a
- * particular specification and if so the specification title, version number,
- * and vendor strings identify that specification.
- * An application can ask if the package is
- * compatible with a particular version, see the {@link
- * #isCompatibleWith isCompatibleWith}
- * method for details.
+ * <p>
+ * A {@code Package} can be obtained with the {@link Package#getPackage
+ * Package.getPackage(String)} and {@link ClassLoader#getDefinedPackage
+ * ClassLoader.getDefinedPackage(String)} methods.
+ * Every {@code Package} defined by a class loader can be obtained
+ * with the {@link Package#getPackages Package.getPackages()} and
+ * {@link ClassLoader#getDefinedPackages} methods.
  *
- * <p>Specification version numbers use a syntax that consists of nonnegative
- * decimal integers separated by periods ".", for example "2.0" or
- * "1.2.3.4.5.6.7".  This allows an extensible number to be used to represent
- * major, minor, micro, etc. versions.  The version specification is described
- * by the following formal grammar:
- * <blockquote>
- * <dl>
- * <dt><i>SpecificationVersion:</i>
- * <dd><i>Digits RefinedVersion<sub>opt</sub></i>
-
- * <dt><i>RefinedVersion:</i>
- * <dd>{@code .} <i>Digits</i>
- * <dd>{@code .} <i>Digits RefinedVersion</i>
+ * @implNote
+ * The <a href="ClassLoader.html#builtinLoaders">builtin class loaders</a>
+ * do not explicitly define {@code Package} objects for packages in
+ * <em>named modules</em>.  Instead those packages are automatically defined
+ * and have no specification and implementation versioning information.
  *
- * <dt><i>Digits:</i>
- * <dd><i>Digit</i>
- * <dd><i>Digits</i>
+ * @jvms 5.3 Run-time package
+ * @see <a href="{@docRoot}/../specs/jar/jar.html#package-sealing">
+ * The JAR File Specification: Package Sealing</a>
+ * @see ClassLoader#definePackage(String, String, String, String, String, String, String, URL)
  *
- * <dt><i>Digit:</i>
- * <dd>any character for which {@link Character#isDigit} returns {@code true},
- * e.g. 0, 1, 2, ...
- * </dl>
- * </blockquote>
- *
- * <p>The implementation title, version, and vendor strings identify an
- * implementation and are made available conveniently to enable accurate
- * reporting of the packages involved when a problem occurs. The contents
- * all three implementation strings are vendor specific. The
- * implementation version strings have no specified syntax and should
- * only be compared for equality with desired version identifiers.
- *
- * <p>Within each {@code ClassLoader} instance all classes from the same
- * java package have the same Package object.  The static methods allow a package
- * to be found by name or the set of all packages known to the current class
- * loader to be found.
- *
- * @see ClassLoader#definePackage
+ * @since 1.2
+ * @revised 9
+ * @spec JPMS
  */
-public class Package implements java.lang.reflect.AnnotatedElement {
+public class Package extends NamedPackage implements java.lang.reflect.AnnotatedElement {
     /**
      * Return the name of this package.
      *
      * @return  The fully-qualified name of this package as defined in section 6.5.3 of
      *          <cite>The Java&trade; Language Specification</cite>,
      *          for example, {@code java.lang}
      */
     public String getName() {
-        return pkgName;
+        return packageName();
     }
 
-
     /**
      * Return the title of the specification that this package implements.
-     * @return the specification title, null is returned if it is not known.
+     * @return the specification title, {@code null} is returned if it is not known.
      */
     public String getSpecificationTitle() {
-        return specTitle;
+        return versionInfo.specTitle;
     }
 
     /**
      * Returns the version number of the specification
      * that this package implements.
-     * This version string must be a sequence of nonnegative decimal
+     * This version string must be a sequence of non-negative decimal
      * integers separated by "."'s and may have leading zeros.
      * When version strings are compared the most significant
      * numbers are compared.
-     * @return the specification version, null is returned if it is not known.
+     *
+     *
+     * <p>Specification version numbers use a syntax that consists of non-negative
+     * decimal integers separated by periods ".", for example "2.0" or
+     * "1.2.3.4.5.6.7".  This allows an extensible number to be used to represent
+     * major, minor, micro, etc. versions.  The version specification is described
+     * by the following formal grammar:
+     * <blockquote>
+     * <dl>
+     * <dt><i>SpecificationVersion:</i>
+     * <dd><i>Digits RefinedVersion<sub>opt</sub></i>
+
+     * <dt><i>RefinedVersion:</i>
+     * <dd>{@code .} <i>Digits</i>
+     * <dd>{@code .} <i>Digits RefinedVersion</i>
+     *
+     * <dt><i>Digits:</i>
+     * <dd><i>Digit</i>
+     * <dd><i>Digits</i>
+     *
+     * <dt><i>Digit:</i>
+     * <dd>any character for which {@link Character#isDigit} returns {@code true},
+     * e.g. 0, 1, 2, ...
+     * </dl>
+     * </blockquote>
+     *
+     * @return the specification version, {@code null} is returned if it is not known.
      */
     public String getSpecificationVersion() {
-        return specVersion;
+        return versionInfo.specVersion;
     }
 
     /**
      * Return the name of the organization, vendor,
      * or company that owns and maintains the specification
      * of the classes that implement this package.
-     * @return the specification vendor, null is returned if it is not known.
+     * @return the specification vendor, {@code null} is returned if it is not known.
      */
     public String getSpecificationVendor() {
-        return specVendor;
+        return versionInfo.specVendor;
     }
 
     /**
      * Return the title of this package.
-     * @return the title of the implementation, null is returned if it is not known.
+     * @return the title of the implementation, {@code null} is returned if it is not known.
      */
     public String getImplementationTitle() {
-        return implTitle;
+        return versionInfo.implTitle;
     }
 
     /**
      * Return the version of this implementation. It consists of any string
      * assigned by the vendor of this implementation and does
      * not have any particular syntax specified or expected by the Java
      * runtime. It may be compared for equality with other
      * package version strings used for this implementation
      * by this vendor for this package.
-     * @return the version of the implementation, null is returned if it is not known.
+     * @return the version of the implementation, {@code null} is returned if it is not known.
      */
     public String getImplementationVersion() {
-        return implVersion;
+        return versionInfo.implVersion;
     }
 
     /**
-     * Returns the name of the organization,
-     * vendor or company that provided this implementation.
-     * @return the vendor that implemented this package..
+     * Returns the vendor that implemented this package, {@code null}
+     * is returned if it is not known.
+     * @return the vendor that implemented this package, {@code null}
+     * is returned if it is not known.
+     *
+     * @revised 9
+     * @spec JPMS
      */
     public String getImplementationVendor() {
-        return implVendor;
+        return versionInfo.implVendor;
     }
 
     /**
      * Returns true if this package is sealed.
      *
      * @return true if the package is sealed, false otherwise
      */
     public boolean isSealed() {
-        return sealBase != null;
+        return module().isNamed() || versionInfo.sealBase != null;
     }
 
     /**
      * Returns true if this package is sealed with respect to the specified
-     * code source url.
+     * code source {@code url}.
      *
-     * @param url the code source url
-     * @return true if this package is sealed with respect to url
+     * @param url the code source URL
+     * @return true if this package is sealed with respect to the given {@code url}
      */
     public boolean isSealed(URL url) {
+        Objects.requireNonNull(url);
+
+        URL sealBase = null;
+        if (versionInfo != VersionInfo.NULL_VERSION_INFO) {
+            sealBase = versionInfo.sealBase;
+        } else {
+            try {
+                URI uri = location();
+                sealBase = uri != null ? uri.toURL() : null;
+            } catch (MalformedURLException e) {
+            }
+        }
         return url.equals(sealBase);
     }
 
     /**
      * Compare this package's specification version with a
      * desired version. It returns true if
      * this packages specification version number is greater than or equal
      * to the desired version number. <p>
      *
      * Version numbers are compared by sequentially comparing corresponding
      * components of the desired and specification strings.
      * Each component is converted as a decimal integer and the values
      * compared.
      * If the specification value is greater than the desired
      * value true is returned. If the value is less false is returned.
      * If the values are equal the period is skipped and the next pair of
      * components is compared.
      *
      * @param desired the version string of the desired version.
      * @return true if this package's version number is greater
      *          than or equal to the desired version number
      *
-     * @exception NumberFormatException if the desired or current version
-     *          is not of the correct dotted form.
+     * @exception NumberFormatException if the current version is not known or
+     *          the desired or current version is not of the correct dotted form.
      */
     public boolean isCompatibleWith(String desired)
         throws NumberFormatException
     {
-        if (specVersion == null || specVersion.length() < 1) {
+        if (versionInfo.specVersion == null || versionInfo.specVersion.length() < 1) {
             throw new NumberFormatException("Empty version string");
         }
 
-        String [] sa = specVersion.split("\\.", -1);
+        String [] sa = versionInfo.specVersion.split("\\.", -1);
         int [] si = new int[sa.length];
         for (int i = 0; i < sa.length; i++) {
             si[i] = Integer.parseInt(sa[i]);
             if (si[i] < 0)
-                throw NumberFormatException.forInputString("" + si[i]);
+                throw NumberFormatException.forInputString("" + si[i], 10);
         }
 
         String [] da = desired.split("\\.", -1);
         int [] di = new int[da.length];
         for (int i = 0; i < da.length; i++) {
             di[i] = Integer.parseInt(da[i]);
             if (di[i] < 0)
-                throw NumberFormatException.forInputString("" + di[i]);
+                throw NumberFormatException.forInputString("" + di[i], 10);
         }
 
         int len = Math.max(di.length, si.length);
         for (int i = 0; i < len; i++) {
             int d = (i < di.length ? di[i] : 0);
             int s = (i < si.length ? si[i] : 0);
             if (s < d)
                 return false;
             if (s > d)
                 return true;
         }
         return true;
     }
 
     /**
-     * Find a package by name in the callers {@code ClassLoader} instance.
-     * The callers {@code ClassLoader} instance is used to find the package
-     * instance corresponding to the named class. If the callers
-     * {@code ClassLoader} instance is null then the set of packages loaded
-     * by the system {@code ClassLoader} instance is searched to find the
-     * named package. <p>
+     * Finds a package by name in the caller's class loader and its
+     * ancestors.
+     * <p>
+     * If the caller's class loader defines a {@code Package} of the given name,
+     * the {@code Package} is returned. Otherwise, the ancestors of the
+     * caller's class loader are searched recursively (parent by parent)
+     * for a {@code Package} of the given name.
+     * <p>
+     * Calling this method is equivalent to calling {@link ClassLoader#getPackage}
+     * on a {@code ClassLoader} instance which is the caller's class loader.
      *
-     * Packages have attributes for versions and specifications only if the class
-     * loader created the package instance with the appropriate attributes. Typically,
-     * those attributes are defined in the manifests that accompany the classes.
+     * @param name A package name, such as "{@code java.lang}".
+     * @return The {@code Package} of the given name defined by the caller's
+     *         class loader or its ancestors, or {@code null} if not found.
      *
-     * @param name a package name, for example, java.lang.
-     * @return the package of the requested name. It may be null if no package
-     *          information is available from the archive or codebase.
+     * @throws NullPointerException
+     *         if {@code name} is {@code null}.
+     *
+     * @deprecated
+     * If multiple class loaders delegate to each other and define classes
+     * with the same package name, and one such loader relies on the lookup
+     * behavior of {@code getPackage} to return a {@code Package} from
+     * a parent loader, then the properties exposed by the {@code Package}
+     * may not be as expected in the rest of the program.
+     * For example, the {@code Package} will only expose annotations from the
+     * {@code package-info.class} file defined by the parent loader, even if
+     * annotations exist in a {@code package-info.class} file defined by
+     * a child loader.  A more robust approach is to use the
+     * {@link ClassLoader#getDefinedPackage} method which returns
+     * a {@code Package} for the specified class loader.
+     *
+     * @see ClassLoader#getDefinedPackage
+     *
+     * @revised 9
+     * @spec JPMS
      */
     @CallerSensitive
+    @Deprecated(since="9")
+    @SuppressWarnings("deprecation")
     public static Package getPackage(String name) {
         ClassLoader l = ClassLoader.getClassLoader(Reflection.getCallerClass());
-        if (l != null) {
-            return l.getPackage(name);
-        } else {
-            return getSystemPackage(name);
-        }
+        return l != null ? l.getPackage(name) : BootLoader.getDefinedPackage(name);
     }
 
     /**
-     * Get all the packages currently known for the caller's {@code ClassLoader}
-     * instance.  Those packages correspond to classes loaded via or accessible by
-     * name to that {@code ClassLoader} instance.  If the caller's
-     * {@code ClassLoader} instance is the bootstrap {@code ClassLoader}
-     * instance, which may be represented by {@code null} in some implementations,
-     * only packages corresponding to classes loaded by the bootstrap
-     * {@code ClassLoader} instance will be returned.
+     * Returns all of the {@code Package}s defined by the caller's class loader
+     * and its ancestors.  The returned array may contain more than one
+     * {@code Package} object of the same package name, each defined by
+     * a different class loader in the class loader hierarchy.
+     * <p>
+     * Calling this method is equivalent to calling {@link ClassLoader#getPackages}
+     * on a {@code ClassLoader} instance which is the caller's class loader.
      *
-     * @return a new array of packages known to the callers {@code ClassLoader}
-     * instance.  An zero length array is returned if none are known.
+     * @return  The array of {@code Package} objects defined by this
+     *          class loader and its ancestors
+     *
+     * @see ClassLoader#getDefinedPackages
+     *
+     * @revised 9
+     * @spec JPMS
      */
     @CallerSensitive
     public static Package[] getPackages() {
-        ClassLoader l = ClassLoader.getClassLoader(Reflection.getCallerClass());
-        if (l != null) {
-            return l.getPackages();
-        } else {
-            return getSystemPackages();
-        }
-    }
-
-    /**
-     * Get the package for the specified class.
-     * The class's class loader is used to find the package instance
-     * corresponding to the specified class. If the class loader
-     * is the bootstrap class loader, which may be represented by
-     * {@code null} in some implementations, then the set of packages
-     * loaded by the bootstrap class loader is searched to find the package.
-     * <p>
-     * Packages have attributes for versions and specifications only
-     * if the class loader created the package
-     * instance with the appropriate attributes. Typically those
-     * attributes are defined in the manifests that accompany
-     * the classes.
-     *
-     * @param c the class to get the package of.
-     * @return the package of the class. It may be null if no package
-     *          information is available from the archive or codebase.  */
-    static Package getPackage(Class<?> c) {
-        String name = c.getName();
-        int i = name.lastIndexOf('.');
-        if (i != -1) {
-            name = name.substring(0, i);
-            ClassLoader cl = c.getClassLoader();
-            if (cl != null) {
-                return cl.getPackage(name);
-            } else {
-                return getSystemPackage(name);
-            }
-        } else {
-            return null;
-        }
+        ClassLoader cl = ClassLoader.getClassLoader(Reflection.getCallerClass());
+        return cl != null ? cl.getPackages() : BootLoader.packages().toArray(Package[]::new);
     }
 
     /**
      * Return the hash code computed from the package name.
      * @return the hash code computed from the package name.
      */
+    @Override
     public int hashCode(){
-        return pkgName.hashCode();
+        return packageName().hashCode();
     }
 
     /**
      * Returns the string representation of this Package.
      * Its value is the string "package " and the package name.
      * If the package title is defined it is appended.
      * If the package version is defined it is appended.
      * @return the string representation of the package.
      */
+    @Override
     public String toString() {
-        String spec = specTitle;
-        String ver =  specVersion;
-        if (spec != null && spec.length() > 0)
+        String spec = versionInfo.specTitle;
+        String ver =  versionInfo.specVersion;
+        if (spec != null && !spec.isEmpty())
             spec = ", " + spec;
         else
             spec = "";
-        if (ver != null && ver.length() > 0)
+        if (ver != null && !ver.isEmpty())
             ver = ", version " + ver;
         else
             ver = "";
-        return "package " + pkgName + spec + ver;
+        return "package " + packageName() + spec + ver;
     }
 
     private Class<?> getPackageInfo() {
         if (packageInfo == null) {
-            try {
-                packageInfo = Class.forName(pkgName + ".package-info", false, loader);
-            } catch (ClassNotFoundException ex) {
+            // find package-info.class defined by loader
+            String cn = packageName() + ".package-info";
+            Module module = module();
+            PrivilegedAction<ClassLoader> pa = module::getClassLoader;
+            ClassLoader loader = AccessController.doPrivileged(pa);
+            Class<?> c;
+            if (loader != null) {
+                c = loader.loadClass(module, cn);
+            } else {
+                c = BootLoader.loadClass(module, cn);
+            }
+
+            if (c != null) {
+                packageInfo = c;
+            } else {
                 // store a proxy for the package info that has no annotations
                 class PackageInfoProxy {}
                 packageInfo = PackageInfoProxy.class;
             }
         }
         return packageInfo;
     }
 
     /**
      * @throws NullPointerException {@inheritDoc}
      * @since 1.5
      */
     public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
         return getPackageInfo().getAnnotation(annotationClass);
     }
 
     /**
      * {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
      * @since 1.5
      */
     @Override
     public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
         return AnnotatedElement.super.isAnnotationPresent(annotationClass);
     }
 
     /**
      * @throws NullPointerException {@inheritDoc}
      * @since 1.8
      */
     @Override
     public  <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass) {
         return getPackageInfo().getAnnotationsByType(annotationClass);
     }
 
     /**
      * @since 1.5
      */
     public Annotation[] getAnnotations() {
         return getPackageInfo().getAnnotations();
     }
 
     /**
      * @throws NullPointerException {@inheritDoc}
      * @since 1.8
      */
     @Override
     public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) {
         return getPackageInfo().getDeclaredAnnotation(annotationClass);
     }
 
     /**
      * @throws NullPointerException {@inheritDoc}
      * @since 1.8
      */
     @Override
     public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass) {
         return getPackageInfo().getDeclaredAnnotationsByType(annotationClass);
     }
 
     /**
      * @since 1.5
      */
     public Annotation[] getDeclaredAnnotations()  {
         return getPackageInfo().getDeclaredAnnotations();
     }
 
     /**
-     * Construct a package instance with the specified version
-     * information.
+     * Construct a package instance for an unnamed module
+     * with the specified version information.
+     *
+     * @apiNote
+     * This method should not be called to define a Package for named module.
+     *
      * @param name the name of the package
      * @param spectitle the title of the specification
      * @param specversion the version of the specification
      * @param specvendor the organization that maintains the specification
      * @param impltitle the title of the implementation
      * @param implversion the version of the implementation
      * @param implvendor the organization that maintains the implementation
+     * @param sealbase code source where this Package comes from
+     * @param loader defining class loader
      */
     Package(String name,
             String spectitle, String specversion, String specvendor,
             String impltitle, String implversion, String implvendor,
             URL sealbase, ClassLoader loader)
     {
-        pkgName = name;
-        implTitle = impltitle;
-        implVersion = implversion;
-        implVendor = implvendor;
-        specTitle = spectitle;
-        specVersion = specversion;
-        specVendor = specvendor;
-        sealBase = sealbase;
-        this.loader = loader;
+        super(Objects.requireNonNull(name),
+              loader != null ? loader.getUnnamedModule()
+                             : BootLoader.getUnnamedModule());
+
+        this.versionInfo = VersionInfo.getInstance(spectitle, specversion,
+                                                   specvendor, impltitle,
+                                                   implversion, implvendor,
+                                                   sealbase);
+    }
+
+    Package(String name, Module module) {
+        super(name, module);
+        this.versionInfo = VersionInfo.NULL_VERSION_INFO;
     }
 
     /*
-     * Construct a package using the attributes from the specified manifest.
-     *
-     * @param name the package name
-     * @param man the optional manifest for the package
-     * @param url the optional code source url for the package
+     * Versioning information.  Only for packages in unnamed modules.
      */
-    private Package(String name, Manifest man, URL url, ClassLoader loader) {
-        String path = name.replace('.', '/').concat("/");
-        String sealed = null;
-        String specTitle= null;
-        String specVersion= null;
-        String specVendor= null;
-        String implTitle= null;
-        String implVersion= null;
-        String implVendor= null;
-        URL sealBase= null;
-        Attributes attr = man.getAttributes(path);
-        if (attr != null) {
-            specTitle   = attr.getValue(Name.SPECIFICATION_TITLE);
-            specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
-            specVendor  = attr.getValue(Name.SPECIFICATION_VENDOR);
-            implTitle   = attr.getValue(Name.IMPLEMENTATION_TITLE);
-            implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
-            implVendor  = attr.getValue(Name.IMPLEMENTATION_VENDOR);
-            sealed      = attr.getValue(Name.SEALED);
-        }
-        attr = man.getMainAttributes();
-        if (attr != null) {
-            if (specTitle == null) {
-                specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
-            }
-            if (specVersion == null) {
-                specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
-            }
-            if (specVendor == null) {
-                specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
-            }
-            if (implTitle == null) {
-                implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
-            }
-            if (implVersion == null) {
-                implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
-            }
-            if (implVendor == null) {
-                implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
-            }
-            if (sealed == null) {
-                sealed = attr.getValue(Name.SEALED);
-            }
-        }
-        if ("true".equalsIgnoreCase(sealed)) {
-            sealBase = url;
-        }
-        pkgName = name;
-        this.specTitle = specTitle;
-        this.specVersion = specVersion;
-        this.specVendor = specVendor;
-        this.implTitle = implTitle;
-        this.implVersion = implVersion;
-        this.implVendor = implVendor;
-        this.sealBase = sealBase;
-        this.loader = loader;
-    }
+    static class VersionInfo {
+        static final VersionInfo NULL_VERSION_INFO
+            = new VersionInfo(null, null, null, null, null, null, null);
 
-    /*
-     * Returns the loaded system package for the specified name.
-     */
-    static Package getSystemPackage(String name) {
-        synchronized (pkgs) {
-            Package pkg = pkgs.get(name);
-            if (pkg == null) {
-                name = name.replace('.', '/').concat("/");
-                String fn = getSystemPackage0(name);
-                if (fn != null) {
-                    pkg = defineSystemPackage(name, fn);
-                }
+        private final String specTitle;
+        private final String specVersion;
+        private final String specVendor;
+        private final String implTitle;
+        private final String implVersion;
+        private final String implVendor;
+        private final URL sealBase;
+
+        static VersionInfo getInstance(String spectitle, String specversion,
+                                       String specvendor, String impltitle,
+                                       String implversion, String implvendor,
+                                       URL sealbase) {
+            if (spectitle == null && specversion == null &&
+                    specvendor == null && impltitle == null &&
+                    implversion == null && implvendor == null &&
+                    sealbase == null) {
+                return NULL_VERSION_INFO;
             }
-            return pkg;
+            return new VersionInfo(spectitle, specversion, specvendor,
+                    impltitle, implversion, implvendor,
+                    sealbase);
         }
-    }
 
-    /*
-     * Return an array of loaded system packages.
-     */
-    static Package[] getSystemPackages() {
-        // First, update the system package map with new package names
-        String[] names = getSystemPackages0();
-        synchronized (pkgs) {
-            for (int i = 0; i < names.length; i++) {
-                defineSystemPackage(names[i], getSystemPackage0(names[i]));
-            }
-            return pkgs.values().toArray(new Package[pkgs.size()]);
-        }
-    }
-
-    private static Package defineSystemPackage(final String iname,
-                                               final String fn)
-    {
-        return AccessController.doPrivileged(new PrivilegedAction<Package>() {
-            public Package run() {
-                String name = iname;
-                // Get the cached code source url for the file name
-                URL url = urls.get(fn);
-                if (url == null) {
-                    // URL not found, so create one
-                    File file = new File(fn);
-                    try {
-                        url = ParseUtil.fileToEncodedURL(file);
-                    } catch (MalformedURLException e) {
-                    }
-                    if (url != null) {
-                        urls.put(fn, url);
-                        // If loading a JAR file, then also cache the manifest
-                        if (file.isFile()) {
-                            mans.put(fn, loadManifest(fn));
-                        }
-                    }
-                }
-                // Convert to "."-separated package name
-                name = name.substring(0, name.length() - 1).replace('/', '.');
-                Package pkg;
-                Manifest man = mans.get(fn);
-                if (man != null) {
-                    pkg = new Package(name, man, url, null);
-                } else {
-                    pkg = new Package(name, null, null, null,
-                                      null, null, null, null, null);
-                }
-                pkgs.put(name, pkg);
-                return pkg;
-            }
-        });
-    }
-
-    /*
-     * Returns the Manifest for the specified JAR file name.
-     */
-    private static Manifest loadManifest(String fn) {
-        try (FileInputStream fis = new FileInputStream(fn);
-             JarInputStream jis = new JarInputStream(fis, false))
+        private VersionInfo(String spectitle, String specversion,
+                            String specvendor, String impltitle,
+                            String implversion, String implvendor,
+                            URL sealbase)
         {
-            return jis.getManifest();
-        } catch (IOException e) {
-            return null;
+            this.implTitle = impltitle;
+            this.implVersion = implversion;
+            this.implVendor = implvendor;
+            this.specTitle = spectitle;
+            this.specVersion = specversion;
+            this.specVendor = specvendor;
+            this.sealBase = sealbase;
         }
     }
 
-    // The map of loaded system packages
-    private static Map<String, Package> pkgs = new HashMap<>(31);
-
-    // Maps each directory or zip file name to its corresponding url
-    private static Map<String, URL> urls = new HashMap<>(10);
-
-    // Maps each code source url for a jar file to its manifest
-    private static Map<String, Manifest> mans = new HashMap<>(10);
-
-    private static native String getSystemPackage0(String name);
-    private static native String[] getSystemPackages0();
-
-    /*
-     * Private storage for the package name and attributes.
-     */
-    private final String pkgName;
-    private final String specTitle;
-    private final String specVersion;
-    private final String specVendor;
-    private final String implTitle;
-    private final String implVersion;
-    private final String implVendor;
-    private final URL sealBase;
-    private transient final ClassLoader loader;
-    private transient Class<?> packageInfo;
+    private final VersionInfo versionInfo;
+    private Class<?> packageInfo;
 }