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