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