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