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