1 /*
   2  * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.internal.module;
  27 
  28 import java.io.DataInput;
  29 import java.io.DataInputStream;
  30 import java.io.EOFException;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.UncheckedIOException;
  34 import java.lang.module.InvalidModuleDescriptorException;
  35 import java.lang.module.ModuleDescriptor;
  36 import java.lang.module.ModuleDescriptor.Builder;
  37 import java.lang.module.ModuleDescriptor.Requires;
  38 import java.lang.module.ModuleDescriptor.Exports;
  39 import java.lang.module.ModuleDescriptor.Opens;
  40 import java.nio.ByteBuffer;
  41 import java.nio.BufferUnderflowException;
  42 import java.util.ArrayList;
  43 import java.util.Collections;
  44 import java.util.HashMap;
  45 import java.util.HashSet;
  46 import java.util.List;
  47 import java.util.Map;
  48 import java.util.Set;
  49 import java.util.function.Supplier;
  50 
  51 import jdk.internal.access.JavaLangModuleAccess;
  52 import jdk.internal.access.SharedSecrets;
  53 
  54 import static jdk.internal.module.ClassFileConstants.*;
  55 
  56 
  57 /**
  58  * Read module information from a {@code module-info} class file.
  59  *
  60  * @implNote The rationale for the hand-coded reader is startup performance
  61  * and fine control over the throwing of InvalidModuleDescriptorException.
  62  */
  63 
  64 public final class ModuleInfo {
  65 
  66     private final int JAVA_MIN_SUPPORTED_VERSION = 53;
  67     private final int JAVA_MAX_SUPPORTED_VERSION = 55;
  68 
  69     private static final JavaLangModuleAccess JLMA
  70         = SharedSecrets.getJavaLangModuleAccess();
  71 
  72     // supplies the set of packages when ModulePackages attribute not present
  73     private final Supplier<Set<String>> packageFinder;
  74 
  75     // indicates if the ModuleHashes attribute should be parsed
  76     private final boolean parseHashes;
  77 
  78     private ModuleInfo(Supplier<Set<String>> pf, boolean ph) {
  79         packageFinder = pf;
  80         parseHashes = ph;
  81     }
  82 
  83     private ModuleInfo(Supplier<Set<String>> pf) {
  84         this(pf, true);
  85     }
  86 
  87     /**
  88      * A holder class for the ModuleDescriptor that is created by reading the
  89      * Module and other standard class file attributes. It also holds the objects
  90      * that represent the non-standard class file attributes that are read from
  91      * the class file.
  92      */
  93     public static final class Attributes {
  94         private final ModuleDescriptor descriptor;
  95         private final ModuleTarget target;
  96         private final ModuleHashes recordedHashes;
  97         private final ModuleResolution moduleResolution;
  98         Attributes(ModuleDescriptor descriptor,
  99                    ModuleTarget target,
 100                    ModuleHashes recordedHashes,
 101                    ModuleResolution moduleResolution) {
 102             this.descriptor = descriptor;
 103             this.target = target;
 104             this.recordedHashes = recordedHashes;
 105             this.moduleResolution = moduleResolution;
 106         }
 107         public ModuleDescriptor descriptor() {
 108             return descriptor;
 109         }
 110         public ModuleTarget target() {
 111             return target;
 112         }
 113         public ModuleHashes recordedHashes() {
 114             return recordedHashes;
 115         }
 116         public ModuleResolution moduleResolution() {
 117             return moduleResolution;
 118         }
 119     }
 120 
 121 
 122     /**
 123      * Reads a {@code module-info.class} from the given input stream.
 124      *
 125      * @throws InvalidModuleDescriptorException
 126      * @throws IOException
 127      */
 128     public static Attributes read(InputStream in, Supplier<Set<String>> pf)
 129         throws IOException
 130     {
 131         try {
 132             return new ModuleInfo(pf).doRead(new DataInputStream(in));
 133         } catch (IllegalArgumentException | IllegalStateException e) {
 134             throw invalidModuleDescriptor(e.getMessage());
 135         } catch (EOFException x) {
 136             throw truncatedModuleDescriptor();
 137         }
 138     }
 139 
 140     /**
 141      * Reads a {@code module-info.class} from the given byte buffer.
 142      *
 143      * @throws InvalidModuleDescriptorException
 144      * @throws UncheckedIOException
 145      */
 146     public static Attributes read(ByteBuffer bb, Supplier<Set<String>> pf) {
 147         try {
 148             return new ModuleInfo(pf).doRead(new DataInputWrapper(bb));
 149         } catch (IllegalArgumentException | IllegalStateException e) {
 150             throw invalidModuleDescriptor(e.getMessage());
 151         } catch (EOFException x) {
 152             throw truncatedModuleDescriptor();
 153         } catch (IOException ioe) {
 154             throw new UncheckedIOException(ioe);
 155         }
 156     }
 157 
 158     /**
 159      * Reads a {@code module-info.class} from the given byte buffer
 160      * but ignore the {@code ModuleHashes} attribute.
 161      *
 162      * @throws InvalidModuleDescriptorException
 163      * @throws UncheckedIOException
 164      */
 165     public static Attributes readIgnoringHashes(ByteBuffer bb, Supplier<Set<String>> pf) {
 166         try {
 167             return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb));
 168         } catch (IllegalArgumentException | IllegalStateException e) {
 169             throw invalidModuleDescriptor(e.getMessage());
 170         } catch (EOFException x) {
 171             throw truncatedModuleDescriptor();
 172         } catch (IOException ioe) {
 173             throw new UncheckedIOException(ioe);
 174         }
 175     }
 176 
 177     /**
 178      * Reads the input as a module-info class file.
 179      *
 180      * @throws IOException
 181      * @throws InvalidModuleDescriptorException
 182      * @throws IllegalArgumentException if thrown by the ModuleDescriptor.Builder
 183      *         because an identifier is not a legal Java identifier, duplicate
 184      *         exports, and many other reasons
 185      */
 186     private Attributes doRead(DataInput in) throws IOException {
 187 
 188         int magic = in.readInt();
 189         if (magic != 0xCAFEBABE)
 190             throw invalidModuleDescriptor("Bad magic number");
 191 
 192         int minor_version = in.readUnsignedShort();
 193         int major_version = in.readUnsignedShort();
 194         if (major_version < JAVA_MIN_SUPPORTED_VERSION ||
 195                 major_version > JAVA_MAX_SUPPORTED_VERSION) {
 196             throw invalidModuleDescriptor("Unsupported major.minor version "
 197                                           + major_version + "." + minor_version);
 198         }
 199 
 200         ConstantPool cpool = new ConstantPool(in);
 201 
 202         int access_flags = in.readUnsignedShort();
 203         if (access_flags != ACC_MODULE)
 204             throw invalidModuleDescriptor("access_flags should be ACC_MODULE");
 205 
 206         int this_class = in.readUnsignedShort();
 207         String mn = cpool.getClassName(this_class);
 208         if (!"module-info".equals(mn))
 209             throw invalidModuleDescriptor("this_class should be module-info");
 210 
 211         int super_class = in.readUnsignedShort();
 212         if (super_class > 0)
 213             throw invalidModuleDescriptor("bad #super_class");
 214 
 215         int interfaces_count = in.readUnsignedShort();
 216         if (interfaces_count > 0)
 217             throw invalidModuleDescriptor("Bad #interfaces");
 218 
 219         int fields_count = in.readUnsignedShort();
 220         if (fields_count > 0)
 221             throw invalidModuleDescriptor("Bad #fields");
 222 
 223         int methods_count = in.readUnsignedShort();
 224         if (methods_count > 0)
 225             throw invalidModuleDescriptor("Bad #methods");
 226 
 227         int attributes_count = in.readUnsignedShort();
 228 
 229         // the names of the attributes found in the class file
 230         Set<String> attributes = new HashSet<>();
 231 
 232         Builder builder = null;
 233         Set<String> allPackages = null;
 234         String mainClass = null;
 235         ModuleTarget moduleTarget = null;
 236         ModuleHashes moduelHashes = null;
 237         ModuleResolution moduleResolution = null;
 238 
 239         for (int i = 0; i < attributes_count ; i++) {
 240             int name_index = in.readUnsignedShort();
 241             String attribute_name = cpool.getUtf8(name_index);
 242             int length = in.readInt();
 243 
 244             boolean added = attributes.add(attribute_name);
 245             if (!added && isAttributeAtMostOnce(attribute_name)) {
 246                 throw invalidModuleDescriptor("More than one "
 247                                               + attribute_name + " attribute");
 248             }
 249 
 250             switch (attribute_name) {
 251 
 252                 case MODULE :
 253                     builder = readModuleAttribute(in, cpool, major_version);
 254                     break;
 255 
 256                 case MODULE_PACKAGES :
 257                     allPackages = readModulePackagesAttribute(in, cpool);
 258                     break;
 259 
 260                 case MODULE_MAIN_CLASS :
 261                     mainClass = readModuleMainClassAttribute(in, cpool);
 262                     break;
 263 
 264                 case MODULE_TARGET :
 265                     moduleTarget = readModuleTargetAttribute(in, cpool);
 266                     break;
 267 
 268                 case MODULE_HASHES :
 269                     if (parseHashes) {
 270                         moduelHashes = readModuleHashesAttribute(in, cpool);
 271                     } else {
 272                         in.skipBytes(length);
 273                     }
 274                     break;
 275 
 276                 case MODULE_RESOLUTION :
 277                     moduleResolution = readModuleResolution(in, cpool);
 278                     break;
 279 
 280                 default:
 281                     if (isAttributeDisallowed(attribute_name)) {
 282                         throw invalidModuleDescriptor(attribute_name
 283                                                       + " attribute not allowed");
 284                     } else {
 285                         in.skipBytes(length);
 286                     }
 287 
 288             }
 289         }
 290 
 291         // the Module attribute is required
 292         if (builder == null) {
 293             throw invalidModuleDescriptor(MODULE + " attribute not found");
 294         }
 295 
 296         // ModuleMainClass  attribute
 297         if (mainClass != null) {
 298             builder.mainClass(mainClass);
 299         }
 300 
 301         // If the ModulePackages attribute is not present then the packageFinder
 302         // is used to find the set of packages
 303         boolean usedPackageFinder = false;
 304         if (allPackages == null && packageFinder != null) {
 305             try {
 306                 allPackages = packageFinder.get();
 307             } catch (UncheckedIOException x) {
 308                 throw x.getCause();
 309             }
 310             usedPackageFinder = true;
 311         }
 312         if (allPackages != null) {
 313             Set<String> knownPackages = JLMA.packages(builder);
 314             if (!allPackages.containsAll(knownPackages)) {
 315                 Set<String> missingPackages = new HashSet<>(knownPackages);
 316                 missingPackages.removeAll(allPackages);
 317                 assert !missingPackages.isEmpty();
 318                 String missingPackage = missingPackages.iterator().next();
 319                 String tail;
 320                 if (usedPackageFinder) {
 321                     tail = " not found in module";
 322                 } else {
 323                     tail = " missing from ModulePackages class file attribute";
 324                 }
 325                 throw invalidModuleDescriptor("Package " + missingPackage + tail);
 326 
 327             }
 328             builder.packages(allPackages);
 329         }
 330 
 331         ModuleDescriptor descriptor = builder.build();
 332         return new Attributes(descriptor,
 333                               moduleTarget,
 334                               moduelHashes,
 335                               moduleResolution);
 336     }
 337 
 338     /**
 339      * Reads the Module attribute, returning the ModuleDescriptor.Builder to
 340      * build the corresponding ModuleDescriptor.
 341      */
 342     private Builder readModuleAttribute(DataInput in, ConstantPool cpool, int major)
 343         throws IOException
 344     {
 345         // module_name
 346         int module_name_index = in.readUnsignedShort();
 347         String mn = cpool.getModuleName(module_name_index);
 348 
 349         int module_flags = in.readUnsignedShort();
 350 
 351         Set<ModuleDescriptor.Modifier> modifiers = new HashSet<>();
 352         boolean open = ((module_flags & ACC_OPEN) != 0);
 353         if (open)
 354             modifiers.add(ModuleDescriptor.Modifier.OPEN);
 355         if ((module_flags & ACC_SYNTHETIC) != 0)
 356             modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC);
 357         if ((module_flags & ACC_MANDATED) != 0)
 358             modifiers.add(ModuleDescriptor.Modifier.MANDATED);
 359 
 360         Builder builder = JLMA.newModuleBuilder(mn, false, modifiers);
 361 
 362         int module_version_index = in.readUnsignedShort();
 363         if (module_version_index != 0) {
 364             String vs = cpool.getUtf8(module_version_index);
 365             builder.version(vs);
 366         }
 367 
 368         int requires_count = in.readUnsignedShort();
 369         boolean requiresJavaBase = false;
 370         for (int i=0; i<requires_count; i++) {
 371             int requires_index = in.readUnsignedShort();
 372             String dn = cpool.getModuleName(requires_index);
 373 
 374             int requires_flags = in.readUnsignedShort();
 375             Set<Requires.Modifier> mods;
 376             if (requires_flags == 0) {
 377                 mods = Collections.emptySet();
 378             } else {
 379                 mods = new HashSet<>();
 380                 if ((requires_flags & ACC_TRANSITIVE) != 0)
 381                     mods.add(Requires.Modifier.TRANSITIVE);
 382                 if ((requires_flags & ACC_STATIC_PHASE) != 0)
 383                     mods.add(Requires.Modifier.STATIC);
 384                 if ((requires_flags & ACC_SYNTHETIC) != 0)
 385                     mods.add(Requires.Modifier.SYNTHETIC);
 386                 if ((requires_flags & ACC_MANDATED) != 0)
 387                     mods.add(Requires.Modifier.MANDATED);
 388             }
 389 
 390             int requires_version_index = in.readUnsignedShort();
 391             if (requires_version_index == 0) {
 392                 builder.requires(mods, dn);
 393             } else {
 394                 String vs = cpool.getUtf8(requires_version_index);
 395                 JLMA.requires(builder, mods, dn, vs);
 396             }
 397 
 398             if (dn.equals("java.base")) {
 399                 if (major >= 54
 400                     && (mods.contains(Requires.Modifier.TRANSITIVE)
 401                         || mods.contains(Requires.Modifier.STATIC))) {
 402                     String flagName;
 403                     if (mods.contains(Requires.Modifier.TRANSITIVE)) {
 404                         flagName = "ACC_TRANSITIVE";
 405                     } else {
 406                         flagName = "ACC_STATIC_PHASE";
 407                     }
 408                     throw invalidModuleDescriptor("The requires entry for java.base"
 409                                                   + " has " + flagName + " set");
 410                 }
 411                 requiresJavaBase = true;
 412             }
 413         }
 414         if (mn.equals("java.base")) {
 415             if (requires_count > 0) {
 416                 throw invalidModuleDescriptor("The requires table for java.base"
 417                                               + " must be 0 length");
 418             }
 419         } else if (!requiresJavaBase) {
 420             throw invalidModuleDescriptor("The requires table must have"
 421                                           + " an entry for java.base");
 422         }
 423 
 424         int exports_count = in.readUnsignedShort();
 425         if (exports_count > 0) {
 426             for (int i=0; i<exports_count; i++) {
 427                 int exports_index = in.readUnsignedShort();
 428                 String pkg = cpool.getPackageName(exports_index);
 429 
 430                 Set<Exports.Modifier> mods;
 431                 int exports_flags = in.readUnsignedShort();
 432                 if (exports_flags == 0) {
 433                     mods = Collections.emptySet();
 434                 } else {
 435                     mods = new HashSet<>();
 436                     if ((exports_flags & ACC_SYNTHETIC) != 0)
 437                         mods.add(Exports.Modifier.SYNTHETIC);
 438                     if ((exports_flags & ACC_MANDATED) != 0)
 439                         mods.add(Exports.Modifier.MANDATED);
 440                 }
 441 
 442                 int exports_to_count = in.readUnsignedShort();
 443                 if (exports_to_count > 0) {
 444                     Set<String> targets = new HashSet<>(exports_to_count);
 445                     for (int j=0; j<exports_to_count; j++) {
 446                         int exports_to_index = in.readUnsignedShort();
 447                         String target = cpool.getModuleName(exports_to_index);
 448                         if (!targets.add(target)) {
 449                             throw invalidModuleDescriptor(pkg + " exported to "
 450                                                           + target + " more than once");
 451                         }
 452                     }
 453                     builder.exports(mods, pkg, targets);
 454                 } else {
 455                     builder.exports(mods, pkg);
 456                 }
 457             }
 458         }
 459 
 460         int opens_count = in.readUnsignedShort();
 461         if (opens_count > 0) {
 462             if (open) {
 463                 throw invalidModuleDescriptor("The opens table for an open"
 464                                               + " module must be 0 length");
 465             }
 466             for (int i=0; i<opens_count; i++) {
 467                 int opens_index = in.readUnsignedShort();
 468                 String pkg = cpool.getPackageName(opens_index);
 469 
 470                 Set<Opens.Modifier> mods;
 471                 int opens_flags = in.readUnsignedShort();
 472                 if (opens_flags == 0) {
 473                     mods = Collections.emptySet();
 474                 } else {
 475                     mods = new HashSet<>();
 476                     if ((opens_flags & ACC_SYNTHETIC) != 0)
 477                         mods.add(Opens.Modifier.SYNTHETIC);
 478                     if ((opens_flags & ACC_MANDATED) != 0)
 479                         mods.add(Opens.Modifier.MANDATED);
 480                 }
 481 
 482                 int open_to_count = in.readUnsignedShort();
 483                 if (open_to_count > 0) {
 484                     Set<String> targets = new HashSet<>(open_to_count);
 485                     for (int j=0; j<open_to_count; j++) {
 486                         int opens_to_index = in.readUnsignedShort();
 487                         String target = cpool.getModuleName(opens_to_index);
 488                         if (!targets.add(target)) {
 489                             throw invalidModuleDescriptor(pkg + " opened to "
 490                                                           + target + " more than once");
 491                         }
 492                     }
 493                     builder.opens(mods, pkg, targets);
 494                 } else {
 495                     builder.opens(mods, pkg);
 496                 }
 497             }
 498         }
 499 
 500         int uses_count = in.readUnsignedShort();
 501         if (uses_count > 0) {
 502             for (int i=0; i<uses_count; i++) {
 503                 int index = in.readUnsignedShort();
 504                 String sn = cpool.getClassName(index);
 505                 builder.uses(sn);
 506             }
 507         }
 508 
 509         int provides_count = in.readUnsignedShort();
 510         if (provides_count > 0) {
 511             for (int i=0; i<provides_count; i++) {
 512                 int index = in.readUnsignedShort();
 513                 String sn = cpool.getClassName(index);
 514                 int with_count = in.readUnsignedShort();
 515                 List<String> providers = new ArrayList<>(with_count);
 516                 for (int j=0; j<with_count; j++) {
 517                     index = in.readUnsignedShort();
 518                     String pn = cpool.getClassName(index);
 519                     if (!providers.add(pn)) {
 520                         throw invalidModuleDescriptor(sn + " provides " + pn
 521                                                       + " more than once");
 522                     }
 523                 }
 524                 builder.provides(sn, providers);
 525             }
 526         }
 527 
 528         return builder;
 529     }
 530 
 531     /**
 532      * Reads the ModulePackages attribute
 533      */
 534     private Set<String> readModulePackagesAttribute(DataInput in, ConstantPool cpool)
 535         throws IOException
 536     {
 537         int package_count = in.readUnsignedShort();
 538         Set<String> packages = new HashSet<>(package_count);
 539         for (int i=0; i<package_count; i++) {
 540             int index = in.readUnsignedShort();
 541             String pn = cpool.getPackageName(index);
 542             boolean added = packages.add(pn);
 543             if (!added) {
 544                 throw invalidModuleDescriptor("Package " + pn + " in ModulePackages"
 545                                               + "attribute more than once");
 546             }
 547         }
 548         return packages;
 549     }
 550 
 551     /**
 552      * Reads the ModuleMainClass attribute
 553      */
 554     private String readModuleMainClassAttribute(DataInput in, ConstantPool cpool)
 555         throws IOException
 556     {
 557         int index = in.readUnsignedShort();
 558         return cpool.getClassName(index);
 559     }
 560 
 561     /**
 562      * Reads the ModuleTarget attribute
 563      */
 564     private ModuleTarget readModuleTargetAttribute(DataInput in, ConstantPool cpool)
 565         throws IOException
 566     {
 567         String targetPlatform = null;
 568 
 569         int index = in.readUnsignedShort();
 570         if (index != 0)
 571             targetPlatform = cpool.getUtf8(index);
 572 
 573         return new ModuleTarget(targetPlatform);
 574     }
 575 
 576     /**
 577      * Reads the ModuleHashes attribute
 578      */
 579     private ModuleHashes readModuleHashesAttribute(DataInput in, ConstantPool cpool)
 580         throws IOException
 581     {
 582         int algorithm_index = in.readUnsignedShort();
 583         String algorithm = cpool.getUtf8(algorithm_index);
 584 
 585         int hash_count = in.readUnsignedShort();
 586         Map<String, byte[]> map = new HashMap<>(hash_count);
 587         for (int i=0; i<hash_count; i++) {
 588             int module_name_index = in.readUnsignedShort();
 589             String mn = cpool.getModuleName(module_name_index);
 590             int hash_length = in.readUnsignedShort();
 591             if (hash_length == 0) {
 592                 throw invalidModuleDescriptor("hash_length == 0");
 593             }
 594             byte[] hash = new byte[hash_length];
 595             in.readFully(hash);
 596             map.put(mn, hash);
 597         }
 598 
 599         return new ModuleHashes(algorithm, map);
 600     }
 601 
 602     /**
 603      * Reads the ModuleResolution attribute.
 604      */
 605     private ModuleResolution readModuleResolution(DataInput in,
 606                                                   ConstantPool cpool)
 607         throws IOException
 608     {
 609         int flags = in.readUnsignedShort();
 610 
 611         int reason = 0;
 612         if ((flags & WARN_DEPRECATED) != 0)
 613             reason = WARN_DEPRECATED;
 614         if ((flags & WARN_DEPRECATED_FOR_REMOVAL) != 0) {
 615             if (reason != 0)
 616                 throw invalidModuleDescriptor("Bad module resolution flags:" + flags);
 617             reason = WARN_DEPRECATED_FOR_REMOVAL;
 618         }
 619         if ((flags & WARN_INCUBATING) != 0) {
 620             if (reason != 0)
 621                 throw invalidModuleDescriptor("Bad module resolution flags:" + flags);
 622         }
 623 
 624         return new ModuleResolution(flags);
 625     }
 626 
 627     /**
 628      * Returns true if the given attribute can be present at most once
 629      * in the class file. Returns false otherwise.
 630      */
 631     private static boolean isAttributeAtMostOnce(String name) {
 632 
 633         if (name.equals(MODULE) ||
 634                 name.equals(SOURCE_FILE) ||
 635                 name.equals(SDE) ||
 636                 name.equals(MODULE_PACKAGES) ||
 637                 name.equals(MODULE_MAIN_CLASS) ||
 638                 name.equals(MODULE_TARGET) ||
 639                 name.equals(MODULE_HASHES) ||
 640                 name.equals(MODULE_RESOLUTION))
 641             return true;
 642 
 643         return false;
 644     }
 645 
 646     /**
 647      * Return true if the given attribute name is the name of a pre-defined
 648      * attribute in JVMS 4.7 that is not allowed in a module-info class.
 649      */
 650     private static boolean isAttributeDisallowed(String name) {
 651         Set<String> notAllowed = predefinedNotAllowed;
 652         if (notAllowed == null) {
 653             notAllowed = Set.of(
 654                     "ConstantValue",
 655                     "Code",
 656                     "Deprecated",
 657                     "StackMapTable",
 658                     "Exceptions",
 659                     "EnclosingMethod",
 660                     "Signature",
 661                     "LineNumberTable",
 662                     "LocalVariableTable",
 663                     "LocalVariableTypeTable",
 664                     "RuntimeVisibleParameterAnnotations",
 665                     "RuntimeInvisibleParameterAnnotations",
 666                     "RuntimeVisibleTypeAnnotations",
 667                     "RuntimeInvisibleTypeAnnotations",
 668                     "Synthetic",
 669                     "AnnotationDefault",
 670                     "BootstrapMethods",
 671                     "MethodParameters");
 672             predefinedNotAllowed = notAllowed;
 673         }
 674         return notAllowed.contains(name);
 675     }
 676 
 677     // lazily created set the pre-defined attributes that are not allowed
 678     private static volatile Set<String> predefinedNotAllowed;
 679 
 680 
 681     /**
 682      * The constant pool in a class file.
 683      */
 684     private static class ConstantPool {
 685         static final int CONSTANT_Utf8 = 1;
 686         static final int CONSTANT_Integer = 3;
 687         static final int CONSTANT_Float = 4;
 688         static final int CONSTANT_Long = 5;
 689         static final int CONSTANT_Double = 6;
 690         static final int CONSTANT_Class = 7;
 691         static final int CONSTANT_String = 8;
 692         static final int CONSTANT_Fieldref = 9;
 693         static final int CONSTANT_Methodref = 10;
 694         static final int CONSTANT_InterfaceMethodref = 11;
 695         static final int CONSTANT_NameAndType = 12;
 696         static final int CONSTANT_MethodHandle = 15;
 697         static final int CONSTANT_MethodType = 16;
 698         static final int CONSTANT_InvokeDynamic = 18;
 699         static final int CONSTANT_Module = 19;
 700         static final int CONSTANT_Package = 20;
 701 
 702         private static class Entry {
 703             protected Entry(int tag) {
 704                 this.tag = tag;
 705             }
 706             final int tag;
 707         }
 708 
 709         private static class IndexEntry extends Entry {
 710             IndexEntry(int tag, int index) {
 711                 super(tag);
 712                 this.index = index;
 713             }
 714             final int index;
 715         }
 716 
 717         private static class Index2Entry extends Entry {
 718             Index2Entry(int tag, int index1, int index2) {
 719                 super(tag);
 720                 this.index1 = index1;
 721                 this.index2 = index2;
 722             }
 723             final int index1,  index2;
 724         }
 725 
 726         private static class ValueEntry extends Entry {
 727             ValueEntry(int tag, Object value) {
 728                 super(tag);
 729                 this.value = value;
 730             }
 731             final Object value;
 732         }
 733 
 734         final Entry[] pool;
 735 
 736         ConstantPool(DataInput in) throws IOException {
 737             int count = in.readUnsignedShort();
 738             pool = new Entry[count];
 739 
 740             for (int i = 1; i < count; i++) {
 741                 int tag = in.readUnsignedByte();
 742                 switch (tag) {
 743 
 744                     case CONSTANT_Utf8:
 745                         String svalue = in.readUTF();
 746                         pool[i] = new ValueEntry(tag, svalue);
 747                         break;
 748 
 749                     case CONSTANT_Class:
 750                     case CONSTANT_Package:
 751                     case CONSTANT_Module:
 752                     case CONSTANT_String:
 753                         int index = in.readUnsignedShort();
 754                         pool[i] = new IndexEntry(tag, index);
 755                         break;
 756 
 757                     case CONSTANT_Double:
 758                         double dvalue = in.readDouble();
 759                         pool[i] = new ValueEntry(tag, dvalue);
 760                         i++;
 761                         break;
 762 
 763                     case CONSTANT_Fieldref:
 764                     case CONSTANT_InterfaceMethodref:
 765                     case CONSTANT_Methodref:
 766                     case CONSTANT_InvokeDynamic:
 767                     case CONSTANT_NameAndType:
 768                         int index1 = in.readUnsignedShort();
 769                         int index2 = in.readUnsignedShort();
 770                         pool[i] = new Index2Entry(tag, index1, index2);
 771                         break;
 772 
 773                     case CONSTANT_MethodHandle:
 774                         int refKind = in.readUnsignedByte();
 775                         index = in.readUnsignedShort();
 776                         pool[i] = new Index2Entry(tag, refKind, index);
 777                         break;
 778 
 779                     case CONSTANT_MethodType:
 780                         index = in.readUnsignedShort();
 781                         pool[i] = new IndexEntry(tag, index);
 782                         break;
 783 
 784                     case CONSTANT_Float:
 785                         float fvalue = in.readFloat();
 786                         pool[i] = new ValueEntry(tag, fvalue);
 787                         break;
 788 
 789                     case CONSTANT_Integer:
 790                         int ivalue = in.readInt();
 791                         pool[i] = new ValueEntry(tag, ivalue);
 792                         break;
 793 
 794                     case CONSTANT_Long:
 795                         long lvalue = in.readLong();
 796                         pool[i] = new ValueEntry(tag, lvalue);
 797                         i++;
 798                         break;
 799 
 800                     default:
 801                         throw invalidModuleDescriptor("Bad constant pool entry: "
 802                                                       + i);
 803                 }
 804             }
 805         }
 806 
 807         String getClassName(int index) {
 808             checkIndex(index);
 809             Entry e = pool[index];
 810             if (e.tag != CONSTANT_Class) {
 811                 throw invalidModuleDescriptor("CONSTANT_Class expected at entry: "
 812                                               + index);
 813             }
 814             String value = getUtf8(((IndexEntry) e).index);
 815             checkUnqualifiedName("CONSTANT_Class", index, value);
 816             return value.replace('/', '.');  // internal form -> binary name
 817         }
 818 
 819         String getPackageName(int index) {
 820             checkIndex(index);
 821             Entry e = pool[index];
 822             if (e.tag != CONSTANT_Package) {
 823                 throw invalidModuleDescriptor("CONSTANT_Package expected at entry: "
 824                                               + index);
 825             }
 826             String value = getUtf8(((IndexEntry) e).index);
 827             checkUnqualifiedName("CONSTANT_Package", index, value);
 828             return value.replace('/', '.');  // internal form -> binary name
 829         }
 830 
 831         String getModuleName(int index) {
 832             checkIndex(index);
 833             Entry e = pool[index];
 834             if (e.tag != CONSTANT_Module) {
 835                 throw invalidModuleDescriptor("CONSTANT_Module expected at entry: "
 836                                               + index);
 837             }
 838             String value = getUtf8(((IndexEntry) e).index);
 839             return decodeModuleName(index, value);
 840         }
 841 
 842         String getUtf8(int index) {
 843             checkIndex(index);
 844             Entry e = pool[index];
 845             if (e.tag != CONSTANT_Utf8) {
 846                 throw invalidModuleDescriptor("CONSTANT_Utf8 expected at entry: "
 847                                               + index);
 848             }
 849             return (String) (((ValueEntry) e).value);
 850         }
 851 
 852         void checkIndex(int index) {
 853             if (index < 1 || index >= pool.length)
 854                 throw invalidModuleDescriptor("Index into constant pool out of range");
 855         }
 856 
 857         void checkUnqualifiedName(String what, int index, String value) {
 858             int len = value.length();
 859             if (len == 0) {
 860                 throw invalidModuleDescriptor(what + " at entry " + index
 861                                               + " has zero length");
 862             }
 863             for (int i=0; i<len; i++) {
 864                 char c = value.charAt(i);
 865                 if (c == '.' || c == ';' || c == '[') {
 866                     throw invalidModuleDescriptor(what + " at entry " + index
 867                                                   + " has illegal character: '"
 868                                                   + c + "'");
 869                 }
 870             }
 871         }
 872 
 873         /**
 874          * "Decode" a module name that has been read from the constant pool.
 875          */
 876         String decodeModuleName(int index, String value) {
 877             int len = value.length();
 878             if (len == 0) {
 879                 throw invalidModuleDescriptor("CONSTANT_Module at entry "
 880                                               + index + " is zero length");
 881             }
 882             int i = 0;
 883             while (i < len) {
 884                 int cp = value.codePointAt(i);
 885                 if (cp == ':' || cp == '@' || cp < 0x20) {
 886                     throw invalidModuleDescriptor("CONSTANT_Module at entry "
 887                                                   + index + " has illegal character: "
 888                                                   + Character.getName(cp));
 889                 }
 890 
 891                 // blackslash is the escape character
 892                 if (cp == '\\')
 893                     return decodeModuleName(index, i, value);
 894 
 895                 i += Character.charCount(cp);
 896             }
 897             return value;
 898         }
 899 
 900         /**
 901          * "Decode" a module name that has been read from the constant pool and
 902          * partly checked for illegal characters (up to position {@code i}).
 903          */
 904         String decodeModuleName(int index, int i, String value) {
 905             StringBuilder sb = new StringBuilder();
 906 
 907             // copy the code points that have been checked
 908             int j = 0;
 909             while (j < i) {
 910                 int cp = value.codePointAt(j);
 911                 sb.appendCodePoint(cp);
 912                 j += Character.charCount(cp);
 913             }
 914 
 915             // decode from position {@code i} to end
 916             int len = value.length();
 917             while (i < len) {
 918                 int cp = value.codePointAt(i);
 919                 if (cp == ':' || cp == '@' || cp < 0x20) {
 920                     throw invalidModuleDescriptor("CONSTANT_Module at entry "
 921                                                   + index + " has illegal character: "
 922                                                   + Character.getName(cp));
 923                 }
 924 
 925                 // blackslash is the escape character
 926                 if (cp == '\\') {
 927                     j = i + Character.charCount(cp);
 928                     if (j >= len) {
 929                         throw invalidModuleDescriptor("CONSTANT_Module at entry "
 930                                                        + index + " has illegal "
 931                                                        + "escape sequence");
 932                     }
 933                     int next = value.codePointAt(j);
 934                     if (next != '\\' && next != ':' && next != '@') {
 935                         throw invalidModuleDescriptor("CONSTANT_Module at entry "
 936                                                       + index + " has illegal "
 937                                                       + "escape sequence");
 938                     }
 939                     sb.appendCodePoint(next);
 940                     i += Character.charCount(next);
 941                 } else {
 942                     sb.appendCodePoint(cp);
 943                 }
 944 
 945                 i += Character.charCount(cp);
 946             }
 947             return sb.toString();
 948         }
 949     }
 950 
 951     /**
 952      * A DataInput implementation that reads from a ByteBuffer.
 953      */
 954     private static class DataInputWrapper implements DataInput {
 955         private final ByteBuffer bb;
 956 
 957         DataInputWrapper(ByteBuffer bb) {
 958             this.bb = bb;
 959         }
 960 
 961         @Override
 962         public void readFully(byte b[]) throws IOException {
 963             readFully(b, 0, b.length);
 964         }
 965 
 966         @Override
 967         public void readFully(byte b[], int off, int len) throws IOException {
 968             try {
 969                 bb.get(b, off, len);
 970             } catch (BufferUnderflowException e) {
 971                 throw new EOFException(e.getMessage());
 972             }
 973         }
 974 
 975         @Override
 976         public int skipBytes(int n) {
 977             int skip = Math.min(n, bb.remaining());
 978             bb.position(bb.position() + skip);
 979             return skip;
 980         }
 981 
 982         @Override
 983         public boolean readBoolean() throws IOException {
 984             try {
 985                 int ch = bb.get();
 986                 return (ch != 0);
 987             } catch (BufferUnderflowException e) {
 988                 throw new EOFException(e.getMessage());
 989             }
 990         }
 991 
 992         @Override
 993         public byte readByte() throws IOException {
 994             try {
 995                 return bb.get();
 996             } catch (BufferUnderflowException e) {
 997                 throw new EOFException(e.getMessage());
 998             }
 999         }
1000 
1001         @Override
1002         public int readUnsignedByte() throws IOException {
1003             try {
1004                 return ((int) bb.get()) & 0xff;
1005             } catch (BufferUnderflowException e) {
1006                 throw new EOFException(e.getMessage());
1007             }
1008         }
1009 
1010         @Override
1011         public short readShort() throws IOException {
1012             try {
1013                 return bb.getShort();
1014             } catch (BufferUnderflowException e) {
1015                 throw new EOFException(e.getMessage());
1016             }
1017         }
1018 
1019         @Override
1020         public int readUnsignedShort() throws IOException {
1021             try {
1022                 return ((int) bb.getShort()) & 0xffff;
1023             } catch (BufferUnderflowException e) {
1024                 throw new EOFException(e.getMessage());
1025             }
1026         }
1027 
1028         @Override
1029         public char readChar() throws IOException {
1030             try {
1031                 return bb.getChar();
1032             } catch (BufferUnderflowException e) {
1033                 throw new EOFException(e.getMessage());
1034             }
1035         }
1036 
1037         @Override
1038         public int readInt() throws IOException {
1039             try {
1040                 return bb.getInt();
1041             } catch (BufferUnderflowException e) {
1042                 throw new EOFException(e.getMessage());
1043             }
1044         }
1045 
1046         @Override
1047         public long readLong() throws IOException {
1048             try {
1049                 return bb.getLong();
1050             } catch (BufferUnderflowException e) {
1051                 throw new EOFException(e.getMessage());
1052             }
1053         }
1054 
1055         @Override
1056         public float readFloat() throws IOException {
1057             try {
1058                 return bb.getFloat();
1059             } catch (BufferUnderflowException e) {
1060                 throw new EOFException(e.getMessage());
1061             }
1062         }
1063 
1064         @Override
1065         public double readDouble() throws IOException {
1066             try {
1067                 return bb.getDouble();
1068             } catch (BufferUnderflowException e) {
1069                 throw new EOFException(e.getMessage());
1070             }
1071         }
1072 
1073         @Override
1074         public String readLine() {
1075             throw new RuntimeException("not implemented");
1076         }
1077 
1078         @Override
1079         public String readUTF() throws IOException {
1080             // ### Need to measure the performance and feasibility of using
1081             // the UTF-8 decoder instead.
1082             return DataInputStream.readUTF(this);
1083         }
1084     }
1085 
1086     /**
1087      * Returns an InvalidModuleDescriptorException with the given detail
1088      * message
1089      */
1090     private static InvalidModuleDescriptorException
1091     invalidModuleDescriptor(String msg) {
1092         return new InvalidModuleDescriptorException(msg);
1093     }
1094 
1095     /**
1096      * Returns an InvalidModuleDescriptorException with a detail message to
1097      * indicate that the class file is truncated.
1098      */
1099     private static InvalidModuleDescriptorException truncatedModuleDescriptor() {
1100         return invalidModuleDescriptor("Truncated module-info.class");
1101     }
1102 
1103 }