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             Version v = descriptor.version().orElse(null);
 296             if (v == null) {
 297                 attr.putShort(0);
 298             } else {
 299                 int module_version_index = cw.newUTF8(v.toString());
 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                 v = r.compiledVersion().orElse(null);
 324                 if (v == null) {
 325                     requires_version_index = 0;
 326                 } else {
 327                     requires_version_index = cw.newUTF8(v.toString());
 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 OS name
 553      *   u2 os_name_index;
 554      *   // index to CONSTANT_utf8_info structure with the OS arch
 555      *   u2 os_arch_index
 556      *   // index to CONSTANT_utf8_info structure with the OS version
 557      *   u2 os_version_index;
 558      * }
 559      *
 560      * } </pre>
 561      */
 562     public static class ModuleTargetAttribute extends Attribute {
 563         private final String osName;
 564         private final String osArch;
 565         private final String osVersion;
 566 
 567         public ModuleTargetAttribute(String osName, String osArch, String osVersion) {
 568             super(MODULE_TARGET);
 569             this.osName = osName;
 570             this.osArch = osArch;
 571             this.osVersion = osVersion;
 572         }
 573 
 574         public ModuleTargetAttribute() {
 575             this(null, null, null);
 576         }
 577 
 578         @Override
 579         protected Attribute read(ClassReader cr,
 580                                  int off,
 581                                  int len,
 582                                  char[] buf,
 583                                  int codeOff,
 584                                  Label[] labels)
 585         {
 586 
 587             String osName = null;
 588             String osArch = null;
 589             String osVersion = null;
 590 
 591             int name_index = cr.readUnsignedShort(off);
 592             if (name_index != 0)
 593                 osName = cr.readUTF8(off, buf);
 594             off += 2;
 595 
 596             int arch_index = cr.readUnsignedShort(off);
 597             if (arch_index != 0)
 598                 osArch = cr.readUTF8(off, buf);
 599             off += 2;
 600 
 601             int version_index = cr.readUnsignedShort(off);
 602             if (version_index != 0)
 603                 osVersion = cr.readUTF8(off, buf);
 604             off += 2;
 605 
 606             return new ModuleTargetAttribute(osName, osArch, osVersion);
 607         }
 608 
 609         @Override
 610         protected ByteVector write(ClassWriter cw,
 611                                    byte[] code,
 612                                    int len,
 613                                    int maxStack,
 614                                    int maxLocals)
 615         {
 616             ByteVector attr = new ByteVector();
 617 
 618             int name_index = 0;
 619             if (osName != null && osName.length() > 0)
 620                 name_index = cw.newUTF8(osName);
 621             attr.putShort(name_index);
 622 
 623             int arch_index = 0;
 624             if (osArch != null && osArch.length() > 0)
 625                 arch_index = cw.newUTF8(osArch);
 626             attr.putShort(arch_index);
 627 
 628             int version_index = 0;
 629             if (osVersion != null && osVersion.length() > 0)
 630                 version_index = cw.newUTF8(osVersion);
 631             attr.putShort(version_index);
 632 
 633             return attr;
 634         }
 635     }
 636 
 637     /**
 638      * ModuleHashes attribute.
 639      *
 640      * <pre> {@code
 641      *
 642      * ModuleHashes_attribute {
 643      *   // index to CONSTANT_utf8_info structure in constant pool representing
 644      *   // the string "ModuleHashes"
 645      *   u2 attribute_name_index;
 646      *   u4 attribute_length;
 647      *
 648      *   // index to CONSTANT_utf8_info structure with algorithm name
 649      *   u2 algorithm_index;
 650      *
 651      *   // the number of entries in the hashes table
 652      *   u2 hashes_count;
 653      *   {   u2 module_name_index (index to CONSTANT_Module_info structure)
 654      *       u2 hash_length;
 655      *       u1 hash[hash_length];
 656      *   } hashes[hashes_count];
 657      *
 658      * } </pre>
 659      */
 660     static class ModuleHashesAttribute extends Attribute {
 661         private final ModuleHashes hashes;
 662 
 663         ModuleHashesAttribute(ModuleHashes hashes) {
 664             super(MODULE_HASHES);
 665             this.hashes = hashes;
 666         }
 667 
 668         ModuleHashesAttribute() {
 669             this(null);
 670         }
 671 
 672         @Override
 673         protected Attribute read(ClassReader cr,
 674                                  int off,
 675                                  int len,
 676                                  char[] buf,
 677                                  int codeOff,
 678                                  Label[] labels)
 679         {
 680             String algorithm = cr.readUTF8(off, buf);
 681             off += 2;
 682 
 683             int hashes_count = cr.readUnsignedShort(off);
 684             off += 2;
 685 
 686             Map<String, byte[]> map = new HashMap<>();
 687             for (int i=0; i<hashes_count; i++) {
 688                 String mn = cr.readModule(off, buf);
 689                 off += 2;
 690 
 691                 int hash_length = cr.readUnsignedShort(off);
 692                 off += 2;
 693                 byte[] hash = new byte[hash_length];
 694                 for (int j=0; j<hash_length; j++) {
 695                     hash[j] = (byte) (0xff & cr.readByte(off+j));
 696                 }
 697                 off += hash_length;
 698 
 699                 map.put(mn, hash);
 700             }
 701 
 702             ModuleHashes hashes = new ModuleHashes(algorithm, map);
 703 
 704             return new ModuleHashesAttribute(hashes);
 705         }
 706 
 707         @Override
 708         protected ByteVector write(ClassWriter cw,
 709                                    byte[] code,
 710                                    int len,
 711                                    int maxStack,
 712                                    int maxLocals)
 713         {
 714             ByteVector attr = new ByteVector();
 715 
 716             int index = cw.newUTF8(hashes.algorithm());
 717             attr.putShort(index);
 718 
 719             Set<String> names = hashes.names();
 720             attr.putShort(names.size());
 721 
 722             for (String mn : names) {
 723                 byte[] hash = hashes.hashFor(mn);
 724                 assert hash != null;
 725                 attr.putShort(cw.newModule(mn));
 726 
 727                 attr.putShort(hash.length);
 728                 for (byte b: hash) {
 729                     attr.putByte(b);
 730                 }
 731             }
 732 
 733             return attr;
 734         }
 735     }
 736 
 737     /**
 738      *  ModuleResolution_attribute {
 739      *    u2 attribute_name_index;    // "ModuleResolution"
 740      *    u4 attribute_length;        // 2
 741      *    u2 resolution_flags;
 742      *
 743      *  The value of the resolution_flags item is a mask of flags used to denote
 744      *  properties of module resolution. The flags are as follows:
 745      *
 746      *   // Optional
 747      *   0x0001 (DO_NOT_RESOLVE_BY_DEFAULT)
 748      *
 749      *   // At most one of:
 750      *   0x0002 (WARN_DEPRECATED)
 751      *   0x0004 (WARN_DEPRECATED_FOR_REMOVAL)
 752      *   0x0008 (WARN_INCUBATING)
 753      */
 754     static class ModuleResolutionAttribute extends Attribute {
 755         private final int value;
 756 
 757         ModuleResolutionAttribute() {
 758             super(MODULE_RESOLUTION);
 759             value = 0;
 760         }
 761 
 762         ModuleResolutionAttribute(int value) {
 763             super(MODULE_RESOLUTION);
 764             this.value = value;
 765         }
 766 
 767         @Override
 768         protected Attribute read(ClassReader cr,
 769                                  int off,
 770                                  int len,
 771                                  char[] buf,
 772                                  int codeOff,
 773                                  Label[] labels)
 774         {
 775             int flags = cr.readUnsignedShort(off);
 776             return new ModuleResolutionAttribute(flags);
 777         }
 778 
 779         @Override
 780         protected ByteVector write(ClassWriter cw,
 781                                    byte[] code,
 782                                    int len,
 783                                    int maxStack,
 784                                    int maxLocals)
 785         {
 786             ByteVector attr = new ByteVector();
 787             attr.putShort(value);
 788             return attr;
 789         }
 790     }
 791 }