jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java
changeset 42338 a60f280f803c
parent 41121 91734a3ed04b
--- a/jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java	Wed Nov 23 16:16:35 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java	Thu Dec 01 08:57:53 2016 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,13 +31,17 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UncheckedIOException;
-import java.lang.module.ModuleDescriptor.Requires.Modifier;
+import java.lang.module.ModuleDescriptor.Builder;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.lang.module.ModuleDescriptor.Exports;
+import java.lang.module.ModuleDescriptor.Opens;
 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.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Supplier;
@@ -56,15 +60,12 @@
 
 final class ModuleInfo {
 
-    // supplies the set of packages when ConcealedPackages not present
+    // supplies the set of packages when ModulePackages attribute not present
     private final Supplier<Set<String>> packageFinder;
 
-    // indicates if the Hashes attribute should be parsed
+    // indicates if the ModuleHashes attribute should be parsed
     private final boolean parseHashes;
 
-    // the builder, created when parsing
-    private ModuleDescriptor.Builder builder;
-
     private ModuleInfo(Supplier<Set<String>> pf, boolean ph) {
         packageFinder = pf;
         parseHashes = ph;
@@ -86,9 +87,8 @@
     {
         try {
             return new ModuleInfo(pf).doRead(new DataInputStream(in));
-        } catch (IllegalArgumentException iae) {
-            // IllegalArgumentException means a malformed class
-            throw invalidModuleDescriptor(iae.getMessage());
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            throw invalidModuleDescriptor(e.getMessage());
         } catch (EOFException x) {
             throw truncatedModuleDescriptor();
         }
@@ -105,9 +105,8 @@
     {
         try {
             return new ModuleInfo(pf).doRead(new DataInputWrapper(bb));
-        } catch (IllegalArgumentException iae) {
-            // IllegalArgumentException means a malformed class
-            throw invalidModuleDescriptor(iae.getMessage());
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            throw invalidModuleDescriptor(e.getMessage());
         } catch (EOFException x) {
             throw truncatedModuleDescriptor();
         } catch (IOException ioe) {
@@ -117,7 +116,7 @@
 
     /**
      * Reads a {@code module-info.class} from the given byte buffer
-     * but ignore the {@code Hashes} attribute.
+     * but ignore the {@code ModuleHashes} attribute.
      *
      * @throws InvalidModuleDescriptorException
      * @throws UncheckedIOException
@@ -127,8 +126,8 @@
     {
         try {
             return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb));
-        } catch (IllegalArgumentException iae) {
-            throw invalidModuleDescriptor(iae.getMessage());
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            throw invalidModuleDescriptor(e.getMessage());
         } catch (EOFException x) {
             throw truncatedModuleDescriptor();
         } catch (IOException ioe) {
@@ -164,12 +163,8 @@
             throw invalidModuleDescriptor("access_flags should be ACC_MODULE");
 
         int this_class = in.readUnsignedShort();
-        String mn = cpool.getClassName(this_class);
-        int suffix = mn.indexOf("/module-info");
-        if (suffix < 1)
-            throw invalidModuleDescriptor("this_class not of form name/module-info");
-        mn = mn.substring(0, suffix).replace('/', '.');
-        builder = new ModuleDescriptor.Builder(mn);
+        if (this_class != 0)
+            throw invalidModuleDescriptor("this_class must be 0");
 
         int super_class = in.readUnsignedShort();
         if (super_class > 0)
@@ -192,6 +187,13 @@
         // the names of the attributes found in the class file
         Set<String> attributes = new HashSet<>();
 
+        Builder builder = null;
+        Set<String> packages = null;
+        String version = null;
+        String mainClass = null;
+        String[] osValues = null;
+        ModuleHashes hashes = null;
+
         for (int i = 0; i < attributes_count ; i++) {
             int name_index = in.readUnsignedShort();
             String attribute_name = cpool.getUtf8(name_index);
@@ -206,28 +208,28 @@
             switch (attribute_name) {
 
                 case MODULE :
-                    readModuleAttribute(mn, in, cpool);
+                    builder = readModuleAttribute(in, cpool);
                     break;
 
-                case CONCEALED_PACKAGES :
-                    readConcealedPackagesAttribute(in, cpool);
+                case MODULE_PACKAGES :
+                    packages = readModulePackagesAttribute(in, cpool);
                     break;
 
-                case VERSION :
-                    readVersionAttribute(in, cpool);
+                case MODULE_VERSION :
+                    version = readModuleVersionAttribute(in, cpool);
                     break;
 
-                case MAIN_CLASS :
-                    readMainClassAttribute(in, cpool);
+                case MODULE_MAIN_CLASS :
+                    mainClass = readModuleMainClassAttribute(in, cpool);
                     break;
 
-                case TARGET_PLATFORM :
-                    readTargetPlatformAttribute(in, cpool);
+                case MODULE_TARGET :
+                    osValues = readModuleTargetAttribute(in, cpool);
                     break;
 
-                case HASHES :
+                case MODULE_HASHES :
                     if (parseHashes) {
-                        readHashesAttribute(in, cpool);
+                        hashes = readModuleHashesAttribute(in, cpool);
                     } else {
                         in.skipBytes(length);
                     }
@@ -245,53 +247,91 @@
         }
 
         // the Module attribute is required
-        if (!attributes.contains(MODULE)) {
+        if (builder == null) {
             throw invalidModuleDescriptor(MODULE + " attribute not found");
         }
 
-        // If the ConcealedPackages attribute is not present then the
-        // packageFinder is used to to find any non-exported packages.
-        if (!attributes.contains(CONCEALED_PACKAGES) && packageFinder != null) {
-            Set<String> pkgs;
+        // If the 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 {
-                pkgs = new HashSet<>(packageFinder.get());
+                packages = new HashSet<>(packageFinder.get());
             } catch (UncheckedIOException x) {
                 throw x.getCause();
             }
-            pkgs.removeAll(builder.exportedPackages());
-            builder.conceals(pkgs);
+            usedPackageFinder = true;
+        }
+        if (packages != null) {
+            for (String pn : builder.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);
         }
 
-        // Was the Synthetic attribute present?
-        if (attributes.contains(SYNTHETIC))
-            builder.synthetic(true);
+        if (version != null)
+            builder.version(version);
+        if (mainClass != null)
+            builder.mainClass(mainClass);
+        if (osValues != null) {
+            if (osValues[0] != null) builder.osName(osValues[0]);
+            if (osValues[1] != null) builder.osArch(osValues[1]);
+            if (osValues[2] != null) builder.osVersion(osValues[2]);
+        }
+        if (hashes != null)
+            builder.hashes(hashes);
 
         return builder.build();
     }
 
     /**
-     * Reads the Module attribute.
+     * Reads the Module attribute, returning the ModuleDescriptor.Builder to
+     * build the corresponding ModuleDescriptor.
      */
-    private void readModuleAttribute(String mn, DataInput in, ConstantPool cpool)
+    private Builder readModuleAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
+        // module_name
+        int module_name_index = in.readUnsignedShort();
+        String mn = cpool.getUtf8AsBinaryName(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 requires_count = in.readUnsignedShort();
         boolean requiresJavaBase = false;
         for (int i=0; i<requires_count; i++) {
             int index = in.readUnsignedShort();
             int flags = in.readUnsignedShort();
-            String dn = cpool.getUtf8(index);
-            Set<Modifier> mods;
+            String dn = cpool.getUtf8AsBinaryName(index);
+            Set<Requires.Modifier> mods;
             if (flags == 0) {
                 mods = Collections.emptySet();
             } else {
                 mods = new HashSet<>();
-                if ((flags & ACC_PUBLIC) != 0)
-                    mods.add(Modifier.PUBLIC);
+                if ((flags & ACC_TRANSITIVE) != 0)
+                    mods.add(Requires.Modifier.TRANSITIVE);
+                if ((flags & ACC_STATIC_PHASE) != 0)
+                    mods.add(Requires.Modifier.STATIC);
                 if ((flags & ACC_SYNTHETIC) != 0)
-                    mods.add(Modifier.SYNTHETIC);
+                    mods.add(Requires.Modifier.SYNTHETIC);
                 if ((flags & ACC_MANDATED) != 0)
-                    mods.add(Modifier.MANDATED);
+                    mods.add(Requires.Modifier.MANDATED);
             }
             builder.requires(mods, dn);
             if (dn.equals("java.base"))
@@ -311,17 +351,66 @@
         if (exports_count > 0) {
             for (int i=0; i<exports_count; i++) {
                 int index = in.readUnsignedShort();
-                String pkg = cpool.getUtf8(index).replace('/', '.');
+                String pkg = cpool.getUtf8AsBinaryName(index);
+
+                Set<Exports.Modifier> mods;
+                int flags = in.readUnsignedShort();
+                if (flags == 0) {
+                    mods = Collections.emptySet();
+                } else {
+                    mods = new HashSet<>();
+                    if ((flags & ACC_SYNTHETIC) != 0)
+                        mods.add(Exports.Modifier.SYNTHETIC);
+                    if ((flags & ACC_MANDATED) != 0)
+                        mods.add(Exports.Modifier.MANDATED);
+                }
+
                 int exports_to_count = in.readUnsignedShort();
                 if (exports_to_count > 0) {
                     Set<String> targets = new HashSet<>(exports_to_count);
                     for (int j=0; j<exports_to_count; j++) {
                         int exports_to_index = in.readUnsignedShort();
-                        targets.add(cpool.getUtf8(exports_to_index));
+                        targets.add(cpool.getUtf8AsBinaryName(exports_to_index));
                     }
-                    builder.exports(pkg, targets);
+                    builder.exports(mods, pkg, targets);
                 } else {
-                    builder.exports(pkg);
+                    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 index = in.readUnsignedShort();
+                String pkg = cpool.getUtf8AsBinaryName(index);
+
+                Set<Opens.Modifier> mods;
+                int flags = in.readUnsignedShort();
+                if (flags == 0) {
+                    mods = Collections.emptySet();
+                } else {
+                    mods = new HashSet<>();
+                    if ((flags & ACC_SYNTHETIC) != 0)
+                        mods.add(Opens.Modifier.SYNTHETIC);
+                    if ((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.getUtf8AsBinaryName(opens_to_index));
+                    }
+                    builder.opens(mods, pkg, targets);
+                } else {
+                    builder.opens(mods, pkg);
                 }
             }
         }
@@ -330,111 +419,114 @@
         if (uses_count > 0) {
             for (int i=0; i<uses_count; i++) {
                 int index = in.readUnsignedShort();
-                String sn = cpool.getClassName(index).replace('/', '.');
+                String sn = cpool.getClassNameAsBinaryName(index);
                 builder.uses(sn);
             }
         }
 
         int provides_count = in.readUnsignedShort();
         if (provides_count > 0) {
-            Map<String, Set<String>> pm = new HashMap<>();
             for (int i=0; i<provides_count; i++) {
                 int index = in.readUnsignedShort();
-                int with_index = in.readUnsignedShort();
-                String sn = cpool.getClassName(index).replace('/', '.');
-                String cn = cpool.getClassName(with_index).replace('/', '.');
-                // computeIfAbsent
-                Set<String> providers = pm.get(sn);
-                if (providers == null) {
-                    providers = new LinkedHashSet<>(); // preserve order
-                    pm.put(sn, providers);
+                String sn = cpool.getClassNameAsBinaryName(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.getClassNameAsBinaryName(index);
+                    providers.add(pn);
                 }
-                providers.add(cn);
-            }
-            for (Map.Entry<String, Set<String>> e : pm.entrySet()) {
-                builder.provides(e.getKey(), e.getValue());
+                builder.provides(sn, providers);
             }
         }
+
+        return builder;
     }
 
     /**
-     * Reads the ConcealedPackages attribute
+     * Reads the ModulePackages attribute
      */
-    private void readConcealedPackagesAttribute(DataInput in, ConstantPool cpool)
+    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.getUtf8(index).replace('/', '.');
-            builder.conceals(pn);
+            String pn = cpool.getUtf8AsBinaryName(index);
+            packages.add(pn);
         }
+        return packages;
     }
 
     /**
-     * Reads the Version attribute
+     * Reads the ModuleVersion attribute
      */
-    private void readVersionAttribute(DataInput in, ConstantPool cpool)
+    private String readModuleVersionAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
         int index = in.readUnsignedShort();
-        builder.version(cpool.getUtf8(index));
+        return cpool.getUtf8(index);
     }
 
     /**
-     * Reads the MainClass attribute
+     * Reads the ModuleMainClass attribute
      */
-    private void readMainClassAttribute(DataInput in, ConstantPool cpool)
+    private String readModuleMainClassAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
         int index = in.readUnsignedShort();
-        builder.mainClass(cpool.getClassName(index).replace('/', '.'));
+        return cpool.getClassNameAsBinaryName(index);
     }
 
     /**
-     * Reads the TargetPlatform attribute
+     * Reads the ModuleTarget attribute
      */
-    private void readTargetPlatformAttribute(DataInput in, ConstantPool cpool)
+    private String[] readModuleTargetAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
+        String[] values = new String[3];
+
         int name_index = in.readUnsignedShort();
         if (name_index != 0)
-            builder.osName(cpool.getUtf8(name_index));
+            values[0] = cpool.getUtf8(name_index);
 
         int arch_index = in.readUnsignedShort();
         if (arch_index != 0)
-            builder.osArch(cpool.getUtf8(arch_index));
+            values[1] = cpool.getUtf8(arch_index);
 
         int version_index = in.readUnsignedShort();
         if (version_index != 0)
-            builder.osVersion(cpool.getUtf8(version_index));
+            values[2] = cpool.getUtf8(version_index);
+
+        return values;
     }
 
 
     /**
-     * Reads the Hashes attribute
-     *
-     * @apiNote For now the hash is stored in base64 as a UTF-8 string, this
-     * should be changed to be an array of u1.
+     * Reads the ModuleHashes attribute
      */
-    private void readHashesAttribute(DataInput in, ConstantPool cpool)
+    private ModuleHashes readModuleHashesAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
-        int index = in.readUnsignedShort();
-        String algorithm = cpool.getUtf8(index);
+        int algorithm_index = in.readUnsignedShort();
+        String algorithm = cpool.getUtf8(algorithm_index);
 
         int hash_count = in.readUnsignedShort();
-
-        Map<String, String> map = new HashMap<>(hash_count);
+        Map<String, byte[]> map = new HashMap<>(hash_count);
         for (int i=0; i<hash_count; i++) {
-            index = in.readUnsignedShort();
-            String dn = cpool.getUtf8(index);
-            index = in.readUnsignedShort();
-            String hash = cpool.getUtf8(index);
-            map.put(dn, hash);
+            int module_name_index = in.readUnsignedShort();
+            String mn = cpool.getUtf8AsBinaryName(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);
         }
 
-        builder.hashes(new ModuleHashes(algorithm, map));
+        return new ModuleHashes(algorithm, map);
     }
 
 
@@ -447,11 +539,11 @@
         if (name.equals(MODULE) ||
                 name.equals(SOURCE_FILE) ||
                 name.equals(SDE) ||
-                name.equals(CONCEALED_PACKAGES) ||
-                name.equals(VERSION) ||
-                name.equals(MAIN_CLASS) ||
-                name.equals(TARGET_PLATFORM) ||
-                name.equals(HASHES))
+                name.equals(MODULE_PACKAGES) ||
+                name.equals(MODULE_VERSION) ||
+                name.equals(MODULE_MAIN_CLASS) ||
+                name.equals(MODULE_TARGET) ||
+                name.equals(MODULE_HASHES))
             return true;
 
         return false;
@@ -461,8 +553,8 @@
      * Return true if the given attribute name is the name of a pre-defined
      * attribute that is not allowed in the class file.
      *
-     * Except for Module, InnerClasses, Synthetic, SourceFile, SourceDebugExtension,
-     * and Deprecated, none of the pre-defined attributes in JVMS 4.7 may appear.
+     * Except for Module, InnerClasses, SourceFile, SourceDebugExtension, and
+     * Deprecated, none of the pre-defined attributes in JVMS 4.7 may appear.
      */
     private static boolean isAttributeDisallowed(String name) {
         Set<String> notAllowed = predefinedNotAllowed;
@@ -477,12 +569,11 @@
                     "LineNumberTable",
                     "LocalVariableTable",
                     "LocalVariableTypeTable",
-                    "RuntimeVisibleAnnotations",
-                    "RuntimeInvisibleAnnotations",
                     "RuntimeVisibleParameterAnnotations",
                     "RuntimeInvisibleParameterAnnotations",
                     "RuntimeVisibleTypeAnnotations",
                     "RuntimeInvisibleTypeAnnotations",
+                    "Synthetic",
                     "AnnotationDefault",
                     "BootstrapMethods",
                     "MethodParameters");
@@ -495,7 +586,6 @@
     private static volatile Set<String> predefinedNotAllowed;
 
 
-
     /**
      * The constant pool in a class file.
      */
@@ -628,6 +718,11 @@
             return getUtf8(((IndexEntry) e).index);
         }
 
+        String getClassNameAsBinaryName(int index) {
+            String value = getClassName(index);
+            return value.replace('/', '.');  // internal form -> binary name
+        }
+
         String getUtf8(int index) {
             checkIndex(index);
             Entry e = pool[index];
@@ -638,6 +733,11 @@
             return (String) (((ValueEntry) e).value);
         }
 
+        String getUtf8AsBinaryName(int index) {
+            String value = getUtf8(index);
+            return value.replace('/', '.');  // internal -> binary name
+        }
+
         void checkIndex(int index) {
             if (index < 1 || index >= pool.length)
                 throw invalidModuleDescriptor("Index into constant pool out of range");