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