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 java.lang.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.ModuleDescriptor.Builder; 35 import java.lang.module.ModuleDescriptor.Requires; 36 import java.lang.module.ModuleDescriptor.Exports; 37 import java.lang.module.ModuleDescriptor.Opens; 38 import java.nio.ByteBuffer; 39 import java.nio.BufferUnderflowException; 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.HashMap; 43 import java.util.HashSet; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Set; 47 import java.util.function.Supplier; 48 49 import jdk.internal.module.ModuleHashes; 50 51 import static jdk.internal.module.ClassFileConstants.*; 52 53 54 /** 55 * Read module information from a {@code module-info} class file. 56 * 57 * @implNote The rationale for the hand-coded reader is startup performance 58 * and fine control over the throwing of InvalidModuleDescriptorException. 59 */ 60 61 final class ModuleInfo { 62 63 // supplies the set of packages when ModulePackages attribute not present 64 private final Supplier<Set<String>> packageFinder; 65 66 // indicates if the ModuleHashes attribute should be parsed 67 private final boolean parseHashes; 68 69 private ModuleInfo(Supplier<Set<String>> pf, boolean ph) { 70 packageFinder = pf; 71 parseHashes = ph; 72 } 73 74 private ModuleInfo(Supplier<Set<String>> pf) { 75 this(pf, true); 76 } 77 78 /** 79 * Reads a {@code module-info.class} from the given input stream. 80 * 81 * @throws InvalidModuleDescriptorException 82 * @throws IOException 83 */ 84 public static ModuleDescriptor read(InputStream in, 85 Supplier<Set<String>> pf) 86 throws IOException 87 { 88 try { 89 return new ModuleInfo(pf).doRead(new DataInputStream(in)); 90 } catch (IllegalArgumentException | IllegalStateException e) { 91 throw invalidModuleDescriptor(e.getMessage()); 92 } catch (EOFException x) { 93 throw truncatedModuleDescriptor(); 94 } 95 } 96 97 /** 98 * Reads a {@code module-info.class} from the given byte buffer. 99 * 100 * @throws InvalidModuleDescriptorException 101 * @throws UncheckedIOException 102 */ 103 public static ModuleDescriptor read(ByteBuffer bb, 104 Supplier<Set<String>> pf) 105 { 106 try { 107 return new ModuleInfo(pf).doRead(new DataInputWrapper(bb)); 108 } catch (IllegalArgumentException | IllegalStateException e) { 109 throw invalidModuleDescriptor(e.getMessage()); 110 } catch (EOFException x) { 111 throw truncatedModuleDescriptor(); 112 } catch (IOException ioe) { 113 throw new UncheckedIOException(ioe); 114 } 115 } 116 117 /** 118 * Reads a {@code module-info.class} from the given byte buffer 119 * but ignore the {@code ModuleHashes} attribute. 120 * 121 * @throws InvalidModuleDescriptorException 122 * @throws UncheckedIOException 123 */ 124 static ModuleDescriptor readIgnoringHashes(ByteBuffer bb, 125 Supplier<Set<String>> pf) 126 { 127 try { 128 return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb)); 129 } catch (IllegalArgumentException | IllegalStateException e) { 130 throw invalidModuleDescriptor(e.getMessage()); 131 } catch (EOFException x) { 132 throw truncatedModuleDescriptor(); 133 } catch (IOException ioe) { 134 throw new UncheckedIOException(ioe); 135 } 136 } 137 138 /** 139 * Reads the input as a module-info class file. 140 * 141 * @throws IOException 142 * @throws InvalidModuleDescriptorException 143 * @throws IllegalArgumentException if thrown by the ModuleDescriptor.Builder 144 * because an identifier is not a legal Java identifier, duplicate 145 * exports, and many other reasons 146 */ 147 private ModuleDescriptor doRead(DataInput in) throws IOException { 148 149 int magic = in.readInt(); 150 if (magic != 0xCAFEBABE) 151 throw invalidModuleDescriptor("Bad magic number"); 152 153 int minor_version = in.readUnsignedShort(); 154 int major_version = in.readUnsignedShort(); 155 if (major_version < 53) { 156 throw invalidModuleDescriptor("Must be >= 53.0"); 157 } 158 159 ConstantPool cpool = new ConstantPool(in); 160 161 int access_flags = in.readUnsignedShort(); 162 if (access_flags != ACC_MODULE) 163 throw invalidModuleDescriptor("access_flags should be ACC_MODULE"); 164 165 int this_class = in.readUnsignedShort(); 166 if (this_class != 0) 167 throw invalidModuleDescriptor("this_class must be 0"); 168 169 int super_class = in.readUnsignedShort(); 170 if (super_class > 0) 171 throw invalidModuleDescriptor("bad #super_class"); 172 173 int interfaces_count = in.readUnsignedShort(); 174 if (interfaces_count > 0) 175 throw invalidModuleDescriptor("Bad #interfaces"); 176 177 int fields_count = in.readUnsignedShort(); 178 if (fields_count > 0) 179 throw invalidModuleDescriptor("Bad #fields"); 180 181 int methods_count = in.readUnsignedShort(); 182 if (methods_count > 0) 183 throw invalidModuleDescriptor("Bad #methods"); 184 185 int attributes_count = in.readUnsignedShort(); 186 187 // the names of the attributes found in the class file 188 Set<String> attributes = new HashSet<>(); 189 190 Builder builder = null; 191 Set<String> packages = null; 192 String version = null; 193 String mainClass = null; 194 String[] osValues = null; 195 ModuleHashes hashes = null; 196 197 for (int i = 0; i < attributes_count ; i++) { 198 int name_index = in.readUnsignedShort(); 199 String attribute_name = cpool.getUtf8(name_index); 200 int length = in.readInt(); 201 202 boolean added = attributes.add(attribute_name); 203 if (!added && isAttributeAtMostOnce(attribute_name)) { 204 throw invalidModuleDescriptor("More than one " 205 + attribute_name + " attribute"); 206 } 207 208 switch (attribute_name) { 209 210 case MODULE : 211 builder = readModuleAttribute(in, cpool); 212 break; 213 214 case MODULE_PACKAGES : 215 packages = readModulePackagesAttribute(in, cpool); 216 break; 217 218 case MODULE_VERSION : 219 version = readModuleVersionAttribute(in, cpool); 220 break; 221 222 case MODULE_MAIN_CLASS : 223 mainClass = readModuleMainClassAttribute(in, cpool); 224 break; 225 226 case MODULE_TARGET : 227 osValues = readModuleTargetAttribute(in, cpool); 228 break; 229 230 case MODULE_HASHES : 231 if (parseHashes) { 232 hashes = readModuleHashesAttribute(in, cpool); 233 } else { 234 in.skipBytes(length); 235 } 236 break; 237 238 default: 239 if (isAttributeDisallowed(attribute_name)) { 240 throw invalidModuleDescriptor(attribute_name 241 + " attribute not allowed"); 242 } else { 243 in.skipBytes(length); 244 } 245 246 } 247 } 248 249 // the Module attribute is required 250 if (builder == null) { 251 throw invalidModuleDescriptor(MODULE + " attribute not found"); 252 } 253 254 // If the ModulePackages attribute is not present then the packageFinder 255 // is used to find the set of packages 256 boolean usedPackageFinder = false; 257 if (packages == null && packageFinder != null) { 258 try { 259 packages = new HashSet<>(packageFinder.get()); 260 } catch (UncheckedIOException x) { 261 throw x.getCause(); 262 } 263 usedPackageFinder = true; 264 } 265 if (packages != null) { 266 for (String pn : builder.exportedAndOpenPackages()) { 267 if (!packages.contains(pn)) { 268 String tail; 269 if (usedPackageFinder) { 270 tail = " not found by package finder"; 271 } else { 272 tail = " missing from ModulePackages attribute"; 273 } 274 throw invalidModuleDescriptor("Package " + pn + tail); 275 } 276 packages.remove(pn); 277 } 278 builder.contains(packages); 279 } 280 281 if (version != null) 282 builder.version(version); 283 if (mainClass != null) 284 builder.mainClass(mainClass); 285 if (osValues != null) { 286 if (osValues[0] != null) builder.osName(osValues[0]); 287 if (osValues[1] != null) builder.osArch(osValues[1]); 288 if (osValues[2] != null) builder.osVersion(osValues[2]); 289 } 290 if (hashes != null) 291 builder.hashes(hashes); 292 293 return builder.build(); 294 } 295 296 /** 297 * Reads the Module attribute, returning the ModuleDescriptor.Builder to 298 * build the corresponding ModuleDescriptor. 299 */ 300 private Builder readModuleAttribute(DataInput in, ConstantPool cpool) 301 throws IOException 302 { 303 // module_name 304 int module_name_index = in.readUnsignedShort(); 305 String mn = cpool.getUtf8AsBinaryName(module_name_index); 306 307 Builder builder = new ModuleDescriptor.Builder(mn, /*strict*/ false); 308 309 int module_flags = in.readUnsignedShort(); 310 boolean open = ((module_flags & ACC_OPEN) != 0); 311 if (open) 312 builder.open(true); 313 if ((module_flags & ACC_SYNTHETIC) != 0) 314 builder.synthetic(true); 315 316 int requires_count = in.readUnsignedShort(); 317 boolean requiresJavaBase = false; 318 for (int i=0; i<requires_count; i++) { 319 int index = in.readUnsignedShort(); 320 int flags = in.readUnsignedShort(); 321 String dn = cpool.getUtf8AsBinaryName(index); 322 Set<Requires.Modifier> mods; 323 if (flags == 0) { 324 mods = Collections.emptySet(); 325 } else { 326 mods = new HashSet<>(); 327 if ((flags & ACC_TRANSITIVE) != 0) 328 mods.add(Requires.Modifier.TRANSITIVE); 329 if ((flags & ACC_STATIC_PHASE) != 0) 330 mods.add(Requires.Modifier.STATIC); 331 if ((flags & ACC_SYNTHETIC) != 0) 332 mods.add(Requires.Modifier.SYNTHETIC); 333 if ((flags & ACC_MANDATED) != 0) 334 mods.add(Requires.Modifier.MANDATED); 335 } 336 builder.requires(mods, dn); 337 if (dn.equals("java.base")) 338 requiresJavaBase = true; 339 } 340 if (mn.equals("java.base")) { 341 if (requires_count > 0) { 342 throw invalidModuleDescriptor("The requires table for java.base" 343 + " must be 0 length"); 344 } 345 } else if (!requiresJavaBase) { 346 throw invalidModuleDescriptor("The requires table must have" 347 + " an entry for java.base"); 348 } 349 350 int exports_count = in.readUnsignedShort(); 351 if (exports_count > 0) { 352 for (int i=0; i<exports_count; i++) { 353 int index = in.readUnsignedShort(); 354 String pkg = cpool.getUtf8AsBinaryName(index); 355 356 Set<Exports.Modifier> mods; 357 int flags = in.readUnsignedShort(); 358 if (flags == 0) { 359 mods = Collections.emptySet(); 360 } else { 361 mods = new HashSet<>(); 362 if ((flags & ACC_SYNTHETIC) != 0) 363 mods.add(Exports.Modifier.SYNTHETIC); 364 if ((flags & ACC_MANDATED) != 0) 365 mods.add(Exports.Modifier.MANDATED); 366 } 367 368 int exports_to_count = in.readUnsignedShort(); 369 if (exports_to_count > 0) { 370 Set<String> targets = new HashSet<>(exports_to_count); 371 for (int j=0; j<exports_to_count; j++) { 372 int exports_to_index = in.readUnsignedShort(); 373 targets.add(cpool.getUtf8AsBinaryName(exports_to_index)); 374 } 375 builder.exports(mods, pkg, targets); 376 } else { 377 builder.exports(mods, pkg); 378 } 379 } 380 } 381 382 int opens_count = in.readUnsignedShort(); 383 if (opens_count > 0) { 384 if (open) { 385 throw invalidModuleDescriptor("The opens table for an open" 386 + " module must be 0 length"); 387 } 388 for (int i=0; i<opens_count; i++) { 389 int index = in.readUnsignedShort(); 390 String pkg = cpool.getUtf8AsBinaryName(index); 391 392 Set<Opens.Modifier> mods; 393 int flags = in.readUnsignedShort(); 394 if (flags == 0) { 395 mods = Collections.emptySet(); 396 } else { 397 mods = new HashSet<>(); 398 if ((flags & ACC_SYNTHETIC) != 0) 399 mods.add(Opens.Modifier.SYNTHETIC); 400 if ((flags & ACC_MANDATED) != 0) 401 mods.add(Opens.Modifier.MANDATED); 402 } 403 404 int open_to_count = in.readUnsignedShort(); 405 if (open_to_count > 0) { 406 Set<String> targets = new HashSet<>(open_to_count); 407 for (int j=0; j<open_to_count; j++) { 408 int opens_to_index = in.readUnsignedShort(); 409 targets.add(cpool.getUtf8AsBinaryName(opens_to_index)); 410 } 411 builder.opens(mods, pkg, targets); 412 } else { 413 builder.opens(mods, pkg); 414 } 415 } 416 } 417 418 int uses_count = in.readUnsignedShort(); 419 if (uses_count > 0) { 420 for (int i=0; i<uses_count; i++) { 421 int index = in.readUnsignedShort(); 422 String sn = cpool.getClassNameAsBinaryName(index); 423 builder.uses(sn); 424 } 425 } 426 427 int provides_count = in.readUnsignedShort(); 428 if (provides_count > 0) { 429 for (int i=0; i<provides_count; i++) { 430 int index = in.readUnsignedShort(); 431 String sn = cpool.getClassNameAsBinaryName(index); 432 int with_count = in.readUnsignedShort(); 433 List<String> providers = new ArrayList<>(with_count); 434 for (int j=0; j<with_count; j++) { 435 index = in.readUnsignedShort(); 436 String pn = cpool.getClassNameAsBinaryName(index); 437 providers.add(pn); 438 } 439 builder.provides(sn, providers); 440 } 441 } 442 443 return builder; 444 } 445 446 /** 447 * Reads the ModulePackages attribute 448 */ 449 private Set<String> readModulePackagesAttribute(DataInput in, ConstantPool cpool) 450 throws IOException 451 { 452 int package_count = in.readUnsignedShort(); 453 Set<String> packages = new HashSet<>(package_count); 454 for (int i=0; i<package_count; i++) { 455 int index = in.readUnsignedShort(); 456 String pn = cpool.getUtf8AsBinaryName(index); 457 packages.add(pn); 458 } 459 return packages; 460 } 461 462 /** 463 * Reads the ModuleVersion attribute 464 */ 465 private String readModuleVersionAttribute(DataInput in, ConstantPool cpool) 466 throws IOException 467 { 468 int index = in.readUnsignedShort(); 469 return cpool.getUtf8(index); 470 } 471 472 /** 473 * Reads the ModuleMainClass attribute 474 */ 475 private String readModuleMainClassAttribute(DataInput in, ConstantPool cpool) 476 throws IOException 477 { 478 int index = in.readUnsignedShort(); 479 return cpool.getClassNameAsBinaryName(index); 480 } 481 482 /** 483 * Reads the ModuleTarget attribute 484 */ 485 private String[] readModuleTargetAttribute(DataInput in, ConstantPool cpool) 486 throws IOException 487 { 488 String[] values = new String[3]; 489 490 int name_index = in.readUnsignedShort(); 491 if (name_index != 0) 492 values[0] = cpool.getUtf8(name_index); 493 494 int arch_index = in.readUnsignedShort(); 495 if (arch_index != 0) 496 values[1] = cpool.getUtf8(arch_index); 497 498 int version_index = in.readUnsignedShort(); 499 if (version_index != 0) 500 values[2] = cpool.getUtf8(version_index); 501 502 return values; 503 } 504 505 506 /** 507 * Reads the ModuleHashes attribute 508 */ 509 private ModuleHashes readModuleHashesAttribute(DataInput in, ConstantPool cpool) 510 throws IOException 511 { 512 int algorithm_index = in.readUnsignedShort(); 513 String algorithm = cpool.getUtf8(algorithm_index); 514 515 int hash_count = in.readUnsignedShort(); 516 Map<String, byte[]> map = new HashMap<>(hash_count); 517 for (int i=0; i<hash_count; i++) { 518 int module_name_index = in.readUnsignedShort(); 519 String mn = cpool.getUtf8AsBinaryName(module_name_index); 520 int hash_length = in.readUnsignedShort(); 521 if (hash_length == 0) { 522 throw invalidModuleDescriptor("hash_length == 0"); 523 } 524 byte[] hash = new byte[hash_length]; 525 in.readFully(hash); 526 map.put(mn, hash); 527 } 528 529 return new ModuleHashes(algorithm, map); 530 } 531 532 533 /** 534 * Returns true if the given attribute can be present at most once 535 * in the class file. Returns false otherwise. 536 */ 537 private static boolean isAttributeAtMostOnce(String name) { 538 539 if (name.equals(MODULE) || 540 name.equals(SOURCE_FILE) || 541 name.equals(SDE) || 542 name.equals(MODULE_PACKAGES) || 543 name.equals(MODULE_VERSION) || 544 name.equals(MODULE_MAIN_CLASS) || 545 name.equals(MODULE_TARGET) || 546 name.equals(MODULE_HASHES)) 547 return true; 548 549 return false; 550 } 551 552 /** 553 * Return true if the given attribute name is the name of a pre-defined 554 * attribute that is not allowed in the class file. 555 * 556 * Except for Module, InnerClasses, SourceFile, SourceDebugExtension, and 557 * Deprecated, none of the pre-defined attributes in JVMS 4.7 may appear. 558 */ 559 private static boolean isAttributeDisallowed(String name) { 560 Set<String> notAllowed = predefinedNotAllowed; 561 if (notAllowed == null) { 562 notAllowed = Set.of( 563 "ConstantValue", 564 "Code", 565 "StackMapTable", 566 "Exceptions", 587 588 589 /** 590 * The constant pool in a class file. 591 */ 592 private static class ConstantPool { 593 static final int CONSTANT_Utf8 = 1; 594 static final int CONSTANT_Integer = 3; 595 static final int CONSTANT_Float = 4; 596 static final int CONSTANT_Long = 5; 597 static final int CONSTANT_Double = 6; 598 static final int CONSTANT_Class = 7; 599 static final int CONSTANT_String = 8; 600 static final int CONSTANT_Fieldref = 9; 601 static final int CONSTANT_Methodref = 10; 602 static final int CONSTANT_InterfaceMethodref = 11; 603 static final int CONSTANT_NameAndType = 12; 604 static final int CONSTANT_MethodHandle = 15; 605 static final int CONSTANT_MethodType = 16; 606 static final int CONSTANT_InvokeDynamic = 18; 607 608 private static class Entry { 609 protected Entry(int tag) { 610 this.tag = tag; 611 } 612 final int tag; 613 } 614 615 private static class IndexEntry extends Entry { 616 IndexEntry(int tag, int index) { 617 super(tag); 618 this.index = index; 619 } 620 final int index; 621 } 622 623 private static class Index2Entry extends Entry { 624 Index2Entry(int tag, int index1, int index2) { 625 super(tag); 626 this.index1 = index1; 636 } 637 final Object value; 638 } 639 640 final Entry[] pool; 641 642 ConstantPool(DataInput in) throws IOException { 643 int count = in.readUnsignedShort(); 644 pool = new Entry[count]; 645 646 for (int i = 1; i < count; i++) { 647 int tag = in.readUnsignedByte(); 648 switch (tag) { 649 650 case CONSTANT_Utf8: 651 String svalue = in.readUTF(); 652 pool[i] = new ValueEntry(tag, svalue); 653 break; 654 655 case CONSTANT_Class: 656 case CONSTANT_String: 657 int index = in.readUnsignedShort(); 658 pool[i] = new IndexEntry(tag, index); 659 break; 660 661 case CONSTANT_Double: 662 double dvalue = in.readDouble(); 663 pool[i] = new ValueEntry(tag, dvalue); 664 i++; 665 break; 666 667 case CONSTANT_Fieldref: 668 case CONSTANT_InterfaceMethodref: 669 case CONSTANT_Methodref: 670 case CONSTANT_InvokeDynamic: 671 case CONSTANT_NameAndType: 672 int index1 = in.readUnsignedShort(); 673 int index2 = in.readUnsignedShort(); 674 pool[i] = new Index2Entry(tag, index1, index2); 675 break; 698 case CONSTANT_Long: 699 long lvalue = in.readLong(); 700 pool[i] = new ValueEntry(tag, lvalue); 701 i++; 702 break; 703 704 default: 705 throw invalidModuleDescriptor("Bad constant pool entry: " 706 + i); 707 } 708 } 709 } 710 711 String getClassName(int index) { 712 checkIndex(index); 713 Entry e = pool[index]; 714 if (e.tag != CONSTANT_Class) { 715 throw invalidModuleDescriptor("CONSTANT_Class expected at entry: " 716 + index); 717 } 718 return getUtf8(((IndexEntry) e).index); 719 } 720 721 String getClassNameAsBinaryName(int index) { 722 String value = getClassName(index); 723 return value.replace('/', '.'); // internal form -> binary name 724 } 725 726 String getUtf8(int index) { 727 checkIndex(index); 728 Entry e = pool[index]; 729 if (e.tag != CONSTANT_Utf8) { 730 throw invalidModuleDescriptor("CONSTANT_Utf8 expected at entry: " 731 + index); 732 } 733 return (String) (((ValueEntry) e).value); 734 } 735 736 String getUtf8AsBinaryName(int index) { 737 String value = getUtf8(index); 738 return value.replace('/', '.'); // internal -> binary name 739 } 740 741 void checkIndex(int index) { 742 if (index < 1 || index >= pool.length) 743 throw invalidModuleDescriptor("Index into constant pool out of range"); 744 } 745 } 746 747 /** 748 * A DataInput implementation that reads from a ByteBuffer. 749 */ 750 private static class DataInputWrapper implements DataInput { 751 private final ByteBuffer bb; 752 753 DataInputWrapper(ByteBuffer bb) { 754 this.bb = bb; 755 } 756 757 @Override 758 public void readFully(byte b[]) throws IOException { 759 readFully(b, 0, b.length); 760 } 761 762 @Override 763 public void readFully(byte b[], int off, int len) throws IOException { 764 try { | 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", 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; 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; 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 { |