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