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