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