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