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 }