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