1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.internal.module;
  27 
  28 import java.lang.module.ModuleDescriptor;
  29 import java.lang.module.ModuleDescriptor.Builder;
  30 import java.lang.module.ModuleDescriptor.Requires;
  31 import java.lang.module.ModuleDescriptor.Exports;
  32 import java.lang.module.ModuleDescriptor.Opens;
  33 import java.lang.module.ModuleDescriptor.Provides;
  34 import java.lang.module.ModuleDescriptor.Version;
  35 import java.util.ArrayList;
  36 import java.util.Collections;
  37 import java.util.HashMap;
  38 import java.util.HashSet;
  39 import java.util.List;
  40 import java.util.Map;
  41 import java.util.Set;
  42 
  43 import jdk.internal.misc.JavaLangModuleAccess;
  44 import jdk.internal.misc.SharedSecrets;
  45 import jdk.internal.org.objectweb.asm.Attribute;
  46 import jdk.internal.org.objectweb.asm.ByteVector;
  47 import jdk.internal.org.objectweb.asm.ClassReader;
  48 import jdk.internal.org.objectweb.asm.ClassWriter;
  49 import jdk.internal.org.objectweb.asm.Label;
  50 import static jdk.internal.module.ClassFileConstants.*;
  51 
  52 
  53 /**
  54  * Provides ASM implementations of {@code Attribute} to read and write the
  55  * class file attributes in a module-info class file.
  56  */
  57 
  58 public final class ClassFileAttributes {
  59 
  60     private ClassFileAttributes() { }
  61 
  62     /**
  63      * Module_attribute {
  64      *   // See lang-vm.html for details.
  65      * }
  66      */
  67     public static class ModuleAttribute extends Attribute {
  68         private static final JavaLangModuleAccess JLMA
  69             = SharedSecrets.getJavaLangModuleAccess();
  70 
  71         private ModuleDescriptor descriptor;
  72         private Version replacementVersion;
  73 
  74         public ModuleAttribute(ModuleDescriptor descriptor) {
  75             super(MODULE);
  76             this.descriptor = descriptor;
  77         }
  78 
  79         public ModuleAttribute(Version v) {
  80             super(MODULE);
  81             this.replacementVersion = v;
  82         }
  83 
  84         public ModuleAttribute() {
  85             super(MODULE);
  86         }
  87 
  88         @Override
  89         protected Attribute read(ClassReader cr,
  90                                  int off,
  91                                  int len,
  92                                  char[] buf,
  93                                  int codeOff,
  94                                  Label[] labels)
  95         {
  96             // module_name (CONSTANT_Module_info)
  97             String mn = cr.readModule(off, buf);
  98             off += 2;
  99 
 100             // module_flags
 101             int module_flags = cr.readUnsignedShort(off);
 102             off += 2;
 103 
 104             Set<ModuleDescriptor.Modifier> modifiers = new HashSet<>();
 105             if ((module_flags & ACC_OPEN) != 0)
 106                 modifiers.add(ModuleDescriptor.Modifier.OPEN);
 107             if ((module_flags & ACC_SYNTHETIC) != 0)
 108                 modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC);
 109             if ((module_flags & ACC_MANDATED) != 0)
 110                 modifiers.add(ModuleDescriptor.Modifier.MANDATED);
 111 
 112             Builder builder = JLMA.newModuleBuilder(mn, false, modifiers);
 113 
 114             // module_version
 115             String module_version = cr.readUTF8(off, buf);
 116             off += 2;
 117             if (replacementVersion != null) {
 118                 builder.version(replacementVersion);
 119             } else if (module_version != null) {
 120                 builder.version(module_version);
 121             }
 122 
 123             // requires_count and requires[requires_count]
 124             int requires_count = cr.readUnsignedShort(off);
 125             off += 2;
 126             for (int i=0; i<requires_count; i++) {
 127                 // CONSTANT_Module_info
 128                 String dn = cr.readModule(off, buf);
 129                 off += 2;
 130 
 131                 // requires_flags
 132                 int requires_flags = cr.readUnsignedShort(off);
 133                 off += 2;
 134                 Set<Requires.Modifier> mods;
 135                 if (requires_flags == 0) {
 136                     mods = Collections.emptySet();
 137                 } else {
 138                     mods = new HashSet<>();
 139                     if ((requires_flags & ACC_TRANSITIVE) != 0)
 140                         mods.add(Requires.Modifier.TRANSITIVE);
 141                     if ((requires_flags & ACC_STATIC_PHASE) != 0)
 142                         mods.add(Requires.Modifier.STATIC);
 143                     if ((requires_flags & ACC_SYNTHETIC) != 0)
 144                         mods.add(Requires.Modifier.SYNTHETIC);
 145                     if ((requires_flags & ACC_MANDATED) != 0)
 146                         mods.add(Requires.Modifier.MANDATED);
 147                 }
 148 
 149                 // requires_version
 150                 String requires_version = cr.readUTF8(off, buf);
 151                 off += 2;
 152                 if (requires_version == null) {
 153                     builder.requires(mods, dn);
 154                 } else {
 155                     JLMA.requires(builder, mods, dn, requires_version);
 156                 }
 157             }
 158 
 159             // exports_count and exports[exports_count]
 160             int exports_count = cr.readUnsignedShort(off);
 161             off += 2;
 162             if (exports_count > 0) {
 163                 for (int i=0; i<exports_count; i++) {
 164                     // CONSTANT_Package_info
 165                     String pkg = cr.readPackage(off, buf).replace('/', '.');
 166                     off += 2;
 167 
 168                     int exports_flags = cr.readUnsignedShort(off);
 169                     off += 2;
 170                     Set<Exports.Modifier> mods;
 171                     if (exports_flags == 0) {
 172                         mods = Collections.emptySet();
 173                     } else {
 174                         mods = new HashSet<>();
 175                         if ((exports_flags & ACC_SYNTHETIC) != 0)
 176                             mods.add(Exports.Modifier.SYNTHETIC);
 177                         if ((exports_flags & ACC_MANDATED) != 0)
 178                             mods.add(Exports.Modifier.MANDATED);
 179                     }
 180 
 181                     int exports_to_count = cr.readUnsignedShort(off);
 182                     off += 2;
 183                     if (exports_to_count > 0) {
 184                         Set<String> targets = new HashSet<>();
 185                         for (int j=0; j<exports_to_count; j++) {
 186                             String t = cr.readModule(off, buf);
 187                             off += 2;
 188                             targets.add(t);
 189                         }
 190                         builder.exports(mods, pkg, targets);
 191                     } else {
 192                         builder.exports(mods, pkg);
 193                     }
 194                 }
 195             }
 196 
 197             // opens_count and opens[opens_count]
 198             int open_count = cr.readUnsignedShort(off);
 199             off += 2;
 200             if (open_count > 0) {
 201                 for (int i=0; i<open_count; i++) {
 202                     // CONSTANT_Package_info
 203                     String pkg = cr.readPackage(off, buf).replace('/', '.');
 204                     off += 2;
 205 
 206                     int opens_flags = cr.readUnsignedShort(off);
 207                     off += 2;
 208                     Set<Opens.Modifier> mods;
 209                     if (opens_flags == 0) {
 210                         mods = Collections.emptySet();
 211                     } else {
 212                         mods = new HashSet<>();
 213                         if ((opens_flags & ACC_SYNTHETIC) != 0)
 214                             mods.add(Opens.Modifier.SYNTHETIC);
 215                         if ((opens_flags & ACC_MANDATED) != 0)
 216                             mods.add(Opens.Modifier.MANDATED);
 217                     }
 218 
 219                     int opens_to_count = cr.readUnsignedShort(off);
 220                     off += 2;
 221                     if (opens_to_count > 0) {
 222                         Set<String> targets = new HashSet<>();
 223                         for (int j=0; j<opens_to_count; j++) {
 224                             String t = cr.readModule(off, buf);
 225                             off += 2;
 226                             targets.add(t);
 227                         }
 228                         builder.opens(mods, pkg, targets);
 229                     } else {
 230                         builder.opens(mods, pkg);
 231                     }
 232                 }
 233             }
 234 
 235             // uses_count and uses_index[uses_count]
 236             int uses_count = cr.readUnsignedShort(off);
 237             off += 2;
 238             if (uses_count > 0) {
 239                 for (int i=0; i<uses_count; i++) {
 240                     String sn = cr.readClass(off, buf).replace('/', '.');
 241                     builder.uses(sn);
 242                     off += 2;
 243                 }
 244             }
 245 
 246             // provides_count and provides[provides_count]
 247             int provides_count = cr.readUnsignedShort(off);
 248             off += 2;
 249             if (provides_count > 0) {
 250                 for (int i=0; i<provides_count; i++) {
 251                     String service = cr.readClass(off, buf).replace('/', '.');
 252                     off += 2;
 253                     int with_count = cr.readUnsignedShort(off);
 254                     off += 2;
 255                     List<String> providers = new ArrayList<>();
 256                     for (int j=0; j<with_count; j++) {
 257                         String cn = cr.readClass(off, buf).replace('/', '.');
 258                         off += 2;
 259                         providers.add(cn);
 260                     }
 261                     builder.provides(service, providers);
 262                 }
 263             }
 264 
 265             return new ModuleAttribute(builder.build());
 266         }
 267 
 268         @Override
 269         protected ByteVector write(ClassWriter cw,
 270                                    byte[] code,
 271                                    int len,
 272                                    int maxStack,
 273                                    int maxLocals)
 274         {
 275             assert descriptor != null;
 276             ByteVector attr = new ByteVector();
 277 
 278             // module_name
 279             String mn = descriptor.name();
 280             int module_name_index = cw.newModule(mn);
 281             attr.putShort(module_name_index);
 282 
 283             // module_flags
 284             Set<ModuleDescriptor.Modifier> modifiers = descriptor.modifiers();
 285             int module_flags = 0;
 286             if (modifiers.contains(ModuleDescriptor.Modifier.OPEN))
 287                 module_flags |= ACC_OPEN;
 288             if (modifiers.contains(ModuleDescriptor.Modifier.SYNTHETIC))
 289                 module_flags |= ACC_SYNTHETIC;
 290             if (modifiers.contains(ModuleDescriptor.Modifier.MANDATED))
 291                 module_flags |= ACC_MANDATED;
 292             attr.putShort(module_flags);
 293 
 294             // module_version
 295             String vs = descriptor.rawVersion().orElse(null);
 296             if (vs == null) {
 297                 attr.putShort(0);
 298             } else {
 299                 int module_version_index = cw.newUTF8(vs);
 300                 attr.putShort(module_version_index);
 301             }
 302 
 303             // requires_count
 304             attr.putShort(descriptor.requires().size());
 305 
 306             // requires[requires_count]
 307             for (Requires r : descriptor.requires()) {
 308                 int requires_index = cw.newModule(r.name());
 309                 attr.putShort(requires_index);
 310 
 311                 int requires_flags = 0;
 312                 if (r.modifiers().contains(Requires.Modifier.TRANSITIVE))
 313                     requires_flags |= ACC_TRANSITIVE;
 314                 if (r.modifiers().contains(Requires.Modifier.STATIC))
 315                     requires_flags |= ACC_STATIC_PHASE;
 316                 if (r.modifiers().contains(Requires.Modifier.SYNTHETIC))
 317                     requires_flags |= ACC_SYNTHETIC;
 318                 if (r.modifiers().contains(Requires.Modifier.MANDATED))
 319                     requires_flags |= ACC_MANDATED;
 320                 attr.putShort(requires_flags);
 321 
 322                 int requires_version_index;
 323                 vs = r.rawCompiledVersion().orElse(null);
 324                 if (vs == null) {
 325                     requires_version_index = 0;
 326                 } else {
 327                     requires_version_index = cw.newUTF8(vs);
 328                 }
 329                 attr.putShort(requires_version_index);
 330             }
 331 
 332             // exports_count and exports[exports_count];
 333             attr.putShort(descriptor.exports().size());
 334             for (Exports e : descriptor.exports()) {
 335                 String pkg = e.source().replace('.', '/');
 336                 attr.putShort(cw.newPackage(pkg));
 337 
 338                 int exports_flags = 0;
 339                 if (e.modifiers().contains(Exports.Modifier.SYNTHETIC))
 340                     exports_flags |= ACC_SYNTHETIC;
 341                 if (e.modifiers().contains(Exports.Modifier.MANDATED))
 342                     exports_flags |= ACC_MANDATED;
 343                 attr.putShort(exports_flags);
 344 
 345                 if (e.isQualified()) {
 346                     Set<String> ts = e.targets();
 347                     attr.putShort(ts.size());
 348                     ts.forEach(target -> attr.putShort(cw.newModule(target)));
 349                 } else {
 350                     attr.putShort(0);
 351                 }
 352             }
 353 
 354             // opens_counts and opens[opens_counts]
 355             attr.putShort(descriptor.opens().size());
 356             for (Opens obj : descriptor.opens()) {
 357                 String pkg = obj.source().replace('.', '/');
 358                 attr.putShort(cw.newPackage(pkg));
 359 
 360                 int opens_flags = 0;
 361                 if (obj.modifiers().contains(Opens.Modifier.SYNTHETIC))
 362                     opens_flags |= ACC_SYNTHETIC;
 363                 if (obj.modifiers().contains(Opens.Modifier.MANDATED))
 364                     opens_flags |= ACC_MANDATED;
 365                 attr.putShort(opens_flags);
 366 
 367                 if (obj.isQualified()) {
 368                     Set<String> ts = obj.targets();
 369                     attr.putShort(ts.size());
 370                     ts.forEach(target -> attr.putShort(cw.newModule(target)));
 371                 } else {
 372                     attr.putShort(0);
 373                 }
 374             }
 375 
 376             // uses_count and uses_index[uses_count]
 377             if (descriptor.uses().isEmpty()) {
 378                 attr.putShort(0);
 379             } else {
 380                 attr.putShort(descriptor.uses().size());
 381                 for (String s : descriptor.uses()) {
 382                     String service = s.replace('.', '/');
 383                     int index = cw.newClass(service);
 384                     attr.putShort(index);
 385                 }
 386             }
 387 
 388             // provides_count and provides[provides_count]
 389             if (descriptor.provides().isEmpty()) {
 390                 attr.putShort(0);
 391             } else {
 392                 attr.putShort(descriptor.provides().size());
 393                 for (Provides p : descriptor.provides()) {
 394                     String service = p.service().replace('.', '/');
 395                     attr.putShort(cw.newClass(service));
 396                     int with_count = p.providers().size();
 397                     attr.putShort(with_count);
 398                     for (String provider : p.providers()) {
 399                         attr.putShort(cw.newClass(provider.replace('.', '/')));
 400                     }
 401                 }
 402             }
 403 
 404             return attr;
 405         }
 406     }
 407 
 408     /**
 409      * ModulePackages attribute.
 410      *
 411      * <pre> {@code
 412      *
 413      * ModulePackages_attribute {
 414      *   // index to CONSTANT_utf8_info structure in constant pool representing
 415      *   // the string "ModulePackages"
 416      *   u2 attribute_name_index;
 417      *   u4 attribute_length;
 418      *
 419      *   // the number of entries in the packages table
 420      *   u2 packages_count;
 421      *   { // index to CONSTANT_Package_info structure with the package name
 422      *     u2 package_index
 423      *   } packages[package_count];
 424      *
 425      * }</pre>
 426      */
 427     public static class ModulePackagesAttribute extends Attribute {
 428         private final Set<String> packages;
 429 
 430         public ModulePackagesAttribute(Set<String> packages) {
 431             super(MODULE_PACKAGES);
 432             this.packages = packages;
 433         }
 434 
 435         public ModulePackagesAttribute() {
 436             this(null);
 437         }
 438 
 439         @Override
 440         protected Attribute read(ClassReader cr,
 441                                  int off,
 442                                  int len,
 443                                  char[] buf,
 444                                  int codeOff,
 445                                  Label[] labels)
 446         {
 447             // package count
 448             int package_count = cr.readUnsignedShort(off);
 449             off += 2;
 450 
 451             // packages
 452             Set<String> packages = new HashSet<>();
 453             for (int i=0; i<package_count; i++) {
 454                 String pkg = cr.readPackage(off, buf).replace('/', '.');
 455                 packages.add(pkg);
 456                 off += 2;
 457             }
 458 
 459             return new ModulePackagesAttribute(packages);
 460         }
 461 
 462         @Override
 463         protected ByteVector write(ClassWriter cw,
 464                                    byte[] code,
 465                                    int len,
 466                                    int maxStack,
 467                                    int maxLocals)
 468         {
 469             assert packages != null;
 470 
 471             ByteVector attr = new ByteVector();
 472 
 473             // package_count
 474             attr.putShort(packages.size());
 475 
 476             // packages
 477             packages.stream()
 478                 .map(p -> p.replace('.', '/'))
 479                 .forEach(p -> attr.putShort(cw.newPackage(p)));
 480 
 481             return attr;
 482         }
 483 
 484     }
 485 
 486     /**
 487      * ModuleMainClass attribute.
 488      *
 489      * <pre> {@code
 490      *
 491      * MainClass_attribute {
 492      *   // index to CONSTANT_utf8_info structure in constant pool representing
 493      *   // the string "ModuleMainClass"
 494      *   u2 attribute_name_index;
 495      *   u4 attribute_length;
 496      *
 497      *   // index to CONSTANT_Class_info structure with the main class name
 498      *   u2 main_class_index;
 499      * }
 500      *
 501      * } </pre>
 502      */
 503     public static class ModuleMainClassAttribute extends Attribute {
 504         private final String mainClass;
 505 
 506         public ModuleMainClassAttribute(String mainClass) {
 507             super(MODULE_MAIN_CLASS);
 508             this.mainClass = mainClass;
 509         }
 510 
 511         public ModuleMainClassAttribute() {
 512             this(null);
 513         }
 514 
 515         @Override
 516         protected Attribute read(ClassReader cr,
 517                                  int off,
 518                                  int len,
 519                                  char[] buf,
 520                                  int codeOff,
 521                                  Label[] labels)
 522         {
 523             String value = cr.readClass(off, buf).replace('/', '.');
 524             return new ModuleMainClassAttribute(value);
 525         }
 526 
 527         @Override
 528         protected ByteVector write(ClassWriter cw,
 529                                    byte[] code,
 530                                    int len,
 531                                    int maxStack,
 532                                    int maxLocals)
 533         {
 534             ByteVector attr = new ByteVector();
 535             int index = cw.newClass(mainClass.replace('.', '/'));
 536             attr.putShort(index);
 537             return attr;
 538         }
 539     }
 540 
 541     /**
 542      * ModuleTarget attribute.
 543      *
 544      * <pre> {@code
 545      *
 546      * TargetPlatform_attribute {
 547      *   // index to CONSTANT_utf8_info structure in constant pool representing
 548      *   // the string "ModuleTarget"
 549      *   u2 attribute_name_index;
 550      *   u4 attribute_length;
 551      *
 552      *   // index to CONSTANT_utf8_info structure with the target platform
 553      *   u2 target_platform_index;
 554      * }
 555      *
 556      * } </pre>
 557      */
 558     public static class ModuleTargetAttribute extends Attribute {
 559         private final String targetPlatform;
 560 
 561         public ModuleTargetAttribute(String targetPlatform) {
 562             super(MODULE_TARGET);
 563             this.targetPlatform = targetPlatform;
 564         }
 565 
 566         public ModuleTargetAttribute() {
 567             this(null);
 568         }
 569 
 570         public String targetPlatform() {
 571             return targetPlatform;
 572         }
 573 
 574         @Override
 575         protected Attribute read(ClassReader cr,
 576                                  int off,
 577                                  int len,
 578                                  char[] buf,
 579                                  int codeOff,
 580                                  Label[] labels)
 581         {
 582 
 583             String targetPlatform = null;
 584 
 585             int target_platform_index = cr.readUnsignedShort(off);
 586             if (target_platform_index != 0)
 587                 targetPlatform = cr.readUTF8(off, buf);
 588             off += 2;
 589 
 590             return new ModuleTargetAttribute(targetPlatform);
 591         }
 592 
 593         @Override
 594         protected ByteVector write(ClassWriter cw,
 595                                    byte[] code,
 596                                    int len,
 597                                    int maxStack,
 598                                    int maxLocals)
 599         {
 600             ByteVector attr = new ByteVector();
 601 
 602             int target_platform_index = 0;
 603             if (targetPlatform != null && targetPlatform.length() > 0)
 604                 target_platform_index = cw.newUTF8(targetPlatform);
 605             attr.putShort(target_platform_index);
 606 
 607             return attr;
 608         }
 609     }
 610 
 611     /**
 612      * ModuleHashes attribute.
 613      *
 614      * <pre> {@code
 615      *
 616      * ModuleHashes_attribute {
 617      *   // index to CONSTANT_utf8_info structure in constant pool representing
 618      *   // the string "ModuleHashes"
 619      *   u2 attribute_name_index;
 620      *   u4 attribute_length;
 621      *
 622      *   // index to CONSTANT_utf8_info structure with algorithm name
 623      *   u2 algorithm_index;
 624      *
 625      *   // the number of entries in the hashes table
 626      *   u2 hashes_count;
 627      *   {   u2 module_name_index (index to CONSTANT_Module_info structure)
 628      *       u2 hash_length;
 629      *       u1 hash[hash_length];
 630      *   } hashes[hashes_count];
 631      *
 632      * } </pre>
 633      */
 634     static class ModuleHashesAttribute extends Attribute {
 635         private final ModuleHashes hashes;
 636 
 637         ModuleHashesAttribute(ModuleHashes hashes) {
 638             super(MODULE_HASHES);
 639             this.hashes = hashes;
 640         }
 641 
 642         ModuleHashesAttribute() {
 643             this(null);
 644         }
 645 
 646         @Override
 647         protected Attribute read(ClassReader cr,
 648                                  int off,
 649                                  int len,
 650                                  char[] buf,
 651                                  int codeOff,
 652                                  Label[] labels)
 653         {
 654             String algorithm = cr.readUTF8(off, buf);
 655             off += 2;
 656 
 657             int hashes_count = cr.readUnsignedShort(off);
 658             off += 2;
 659 
 660             Map<String, byte[]> map = new HashMap<>();
 661             for (int i=0; i<hashes_count; i++) {
 662                 String mn = cr.readModule(off, buf);
 663                 off += 2;
 664 
 665                 int hash_length = cr.readUnsignedShort(off);
 666                 off += 2;
 667                 byte[] hash = new byte[hash_length];
 668                 for (int j=0; j<hash_length; j++) {
 669                     hash[j] = (byte) (0xff & cr.readByte(off+j));
 670                 }
 671                 off += hash_length;
 672 
 673                 map.put(mn, hash);
 674             }
 675 
 676             ModuleHashes hashes = new ModuleHashes(algorithm, map);
 677 
 678             return new ModuleHashesAttribute(hashes);
 679         }
 680 
 681         @Override
 682         protected ByteVector write(ClassWriter cw,
 683                                    byte[] code,
 684                                    int len,
 685                                    int maxStack,
 686                                    int maxLocals)
 687         {
 688             ByteVector attr = new ByteVector();
 689 
 690             int index = cw.newUTF8(hashes.algorithm());
 691             attr.putShort(index);
 692 
 693             Set<String> names = hashes.names();
 694             attr.putShort(names.size());
 695 
 696             for (String mn : names) {
 697                 byte[] hash = hashes.hashFor(mn);
 698                 assert hash != null;
 699                 attr.putShort(cw.newModule(mn));
 700 
 701                 attr.putShort(hash.length);
 702                 for (byte b: hash) {
 703                     attr.putByte(b);
 704                 }
 705             }
 706 
 707             return attr;
 708         }
 709     }
 710 
 711     /**
 712      *  ModuleResolution_attribute {
 713      *    u2 attribute_name_index;    // "ModuleResolution"
 714      *    u4 attribute_length;        // 2
 715      *    u2 resolution_flags;
 716      *
 717      *  The value of the resolution_flags item is a mask of flags used to denote
 718      *  properties of module resolution. The flags are as follows:
 719      *
 720      *   // Optional
 721      *   0x0001 (DO_NOT_RESOLVE_BY_DEFAULT)
 722      *
 723      *   // At most one of:
 724      *   0x0002 (WARN_DEPRECATED)
 725      *   0x0004 (WARN_DEPRECATED_FOR_REMOVAL)
 726      *   0x0008 (WARN_INCUBATING)
 727      */
 728     static class ModuleResolutionAttribute extends Attribute {
 729         private final int value;
 730 
 731         ModuleResolutionAttribute() {
 732             super(MODULE_RESOLUTION);
 733             value = 0;
 734         }
 735 
 736         ModuleResolutionAttribute(int value) {
 737             super(MODULE_RESOLUTION);
 738             this.value = value;
 739         }
 740 
 741         @Override
 742         protected Attribute read(ClassReader cr,
 743                                  int off,
 744                                  int len,
 745                                  char[] buf,
 746                                  int codeOff,
 747                                  Label[] labels)
 748         {
 749             int flags = cr.readUnsignedShort(off);
 750             return new ModuleResolutionAttribute(flags);
 751         }
 752 
 753         @Override
 754         protected ByteVector write(ClassWriter cw,
 755                                    byte[] code,
 756                                    int len,
 757                                    int maxStack,
 758                                    int maxLocals)
 759         {
 760             ByteVector attr = new ByteVector();
 761             attr.putShort(value);
 762             return attr;
 763         }
 764     }
 765 }