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