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