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 package jdk.tools.jlink.internal.plugins;
  26 
  27 import java.io.ByteArrayInputStream;
  28 import java.io.ByteArrayOutputStream;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.lang.module.ModuleDescriptor;
  32 import java.lang.module.ModuleDescriptor.Exports;
  33 import java.lang.module.ModuleDescriptor.Opens;
  34 import java.lang.module.ModuleDescriptor.Provides;
  35 import java.lang.module.ModuleDescriptor.Requires;
  36 import java.lang.module.ModuleDescriptor.Version;
  37 import java.util.ArrayList;
  38 import java.util.Collection;
  39 import java.util.EnumSet;
  40 import java.util.HashMap;
  41 import java.util.HashSet;
  42 import java.util.List;
  43 import java.util.Map;
  44 import java.util.Set;
  45 import java.util.TreeSet;
  46 import java.util.function.IntSupplier;
  47 
  48 import jdk.internal.module.Checks;
  49 import jdk.internal.module.ClassFileAttributes;
  50 import jdk.internal.module.ClassFileConstants;
  51 import jdk.internal.module.ModuleHashes;
  52 import jdk.internal.module.ModuleInfo.Attributes;
  53 import jdk.internal.module.ModuleInfoExtender;
  54 import jdk.internal.module.ModuleResolution;
  55 import jdk.internal.module.SystemModules;
  56 import jdk.internal.org.objectweb.asm.Attribute;
  57 import jdk.internal.org.objectweb.asm.ClassReader;
  58 import jdk.internal.org.objectweb.asm.ClassVisitor;
  59 import jdk.internal.org.objectweb.asm.ClassWriter;
  60 import jdk.internal.org.objectweb.asm.MethodVisitor;
  61 import jdk.internal.org.objectweb.asm.Opcodes;
  62 
  63 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  64 
  65 import jdk.tools.jlink.internal.ModuleSorter;
  66 import jdk.tools.jlink.plugin.PluginException;
  67 import jdk.tools.jlink.plugin.ResourcePool;
  68 import jdk.tools.jlink.plugin.Plugin;
  69 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
  70 import jdk.tools.jlink.plugin.ResourcePoolEntry;
  71 
  72 /**
  73  * Jlink plugin to reconstitute module descriptors for system modules.
  74  * It will extend module-info.class with ModulePackages attribute,
  75  * if not present. It also determines the number of packages of
  76  * the boot layer at link time.
  77  *
  78  * This plugin will override jdk.internal.module.SystemModules class
  79  *
  80  * @see jdk.internal.module.SystemModuleFinder
  81  * @see SystemModules
  82  */
  83 public final class SystemModulesPlugin implements Plugin {
  84     private static final String NAME = "system-modules";
  85     private static final String DESCRIPTION =
  86         PluginsResourceBundle.getDescription(NAME);
  87 
  88     private boolean enabled;
  89     private boolean retainModuleTarget;
  90     public SystemModulesPlugin() {
  91         this.enabled = true;
  92         this.retainModuleTarget = false;
  93     }
  94 
  95     @Override
  96     public String getName() {
  97         return NAME;
  98     }
  99 
 100     @Override
 101     public String getDescription() {
 102         return DESCRIPTION;
 103     }
 104 
 105     @Override
 106     public Set<State> getState() {
 107         return enabled ? EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL)
 108                        : EnumSet.of(State.DISABLED);
 109     }
 110 
 111     @Override
 112     public boolean hasArguments() {
 113         return true;
 114     }
 115 
 116     @Override
 117     public String getArgumentsDescription() {
 118         return PluginsResourceBundle.getArgument(NAME);
 119     }
 120 
 121     @Override
 122     public void configure(Map<String, String> config) {
 123         String arg = config.get(NAME);
 124         if (arg != null) {
 125             if (arg.equals("retainModuleTarget")) {
 126                 retainModuleTarget = true;
 127             } else {
 128                 throw new IllegalArgumentException(NAME + ": " + arg);
 129             }
 130         }
 131     }
 132 
 133     @Override
 134     public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
 135         if (!enabled) {
 136             throw new PluginException(NAME + " was set");
 137         }
 138 
 139         SystemModulesClassGenerator generator =
 140             new SystemModulesClassGenerator(retainModuleTarget);
 141 
 142         // generate the byte code to create ModuleDescriptors
 143         // such that the modules linked in the image would skip parsing
 144         // of module-info.class and also skip name check
 145 
 146         // Sort modules in the topological order so that java.base is always first.
 147         new ModuleSorter(in.moduleView()).sorted().forEach(module -> {
 148             ResourcePoolEntry data = module.findEntry("module-info.class").orElseThrow(
 149                 // automatic module not supported yet
 150                 () ->  new PluginException("module-info.class not found for " +
 151                     module.name() + " module")
 152             );
 153 
 154             assert module.name().equals(data.moduleName());
 155             try {
 156                 // validate the module and add to system modules
 157                 data = generator.buildModuleInfo(data, module.packages());
 158 
 159                 // add resource pool entry
 160                 out.add(data);
 161             } catch (IOException e) {
 162                 throw new PluginException(e);
 163             }
 164         });
 165 
 166         // Generate the new class
 167         ClassWriter cwriter = generator.getClassWriter();
 168         in.entries().forEach(data -> {
 169             if (data.path().endsWith("module-info.class"))
 170                 return;
 171             if (generator.isOverriddenClass(data.path())) {
 172                 byte[] bytes = cwriter.toByteArray();
 173                 ResourcePoolEntry ndata = data.copyWithContent(bytes);
 174                 out.add(ndata);
 175             } else {
 176                 out.add(data);
 177             }
 178         });
 179 
 180         return out.build();
 181     }
 182 
 183     static class ModuleInfo {
 184         private final ByteArrayInputStream bain;
 185         private final Attributes attrs;
 186         private final Set<String> packages;
 187         private final boolean dropModuleTarget;
 188         private final boolean addModulePackages;
 189         private ModuleDescriptor descriptor;  // may be different that the original one
 190 
 191         ModuleInfo(byte[] bytes, Set<String> packages, boolean dropModuleTarget)
 192             throws IOException
 193         {
 194             this.bain = new ByteArrayInputStream(bytes);
 195             this.packages = packages;
 196             this.attrs = jdk.internal.module.ModuleInfo.read(bain, null);
 197             // If ModulePackages attribute is present, the packages from this
 198             // module descriptor returns the packages in that attribute.
 199             // If it's not present, ModuleDescriptor::packages only contains
 200             // the exported and open packages from module-info.class
 201             this.descriptor = attrs.descriptor();
 202             if (descriptor.isAutomatic()) {
 203                 throw new InternalError("linking automatic module is not supported");
 204             }
 205 
 206             // add ModulePackages attribute if this module contains some packages
 207             // and ModulePackages is not present
 208             this.addModulePackages = packages.size() > 0 && !hasModulePackages();
 209             // drop target attribute only if any OS property is present
 210             if (dropModuleTarget) {
 211                 this.dropModuleTarget =
 212                     descriptor.osName().isPresent() ||
 213                     descriptor.osArch().isPresent() ||
 214                     descriptor.osVersion().isPresent();
 215             } else {
 216                 this.dropModuleTarget = false;
 217             }
 218         }
 219 
 220         String moduleName() {
 221             return attrs.descriptor().name();
 222         }
 223 
 224         ModuleDescriptor descriptor() {
 225             return descriptor;
 226         }
 227 
 228 
 229         Set<String> packages() {
 230             return packages;
 231         }
 232 
 233         ModuleHashes recordedHashes() {
 234             return attrs.recordedHashes();
 235         }
 236 
 237         ModuleResolution moduleResolution() {
 238             return attrs.moduleResolution();
 239         }
 240 
 241         /**
 242          * Validates names in ModuleDescriptor
 243          */
 244         void validateNames() {
 245             Checks.requireModuleName(descriptor.name());
 246             for (Requires req : descriptor.requires()) {
 247                 Checks.requireModuleName(req.name());
 248             }
 249             for (Exports e : descriptor.exports()) {
 250                 Checks.requirePackageName(e.source());
 251                 if (e.isQualified())
 252                     e.targets().forEach(Checks::requireModuleName);
 253             }
 254             for (Opens opens : descriptor.opens()) {
 255                 Checks.requirePackageName(opens.source());
 256                 if (opens.isQualified())
 257                     opens.targets().forEach(Checks::requireModuleName);
 258             }
 259             for (Provides provides : descriptor.provides()) {
 260                 Checks.requireServiceTypeName(provides.service());
 261                 provides.providers().forEach(Checks::requireServiceProviderName);
 262             }
 263             for (String service : descriptor.uses()) {
 264                 Checks.requireServiceTypeName(service);
 265             }
 266             for (String pn : descriptor.packages()) {
 267                 Checks.requirePackageName(pn);
 268             }
 269             for (String pn : packages) {
 270                 Checks.requirePackageName(pn);
 271             }
 272         }
 273 
 274 
 275         /**
 276          * Validates if exported and open packages are present
 277          */
 278         void validatePackages() {
 279             Set<String> nonExistPackages = new TreeSet<>();
 280             descriptor.exports().stream()
 281                 .map(Exports::source)
 282                 .filter(pn -> !packages.contains(pn))
 283                 .forEach(nonExistPackages::add);
 284 
 285             descriptor.opens().stream()
 286                 .map(Opens::source)
 287                 .filter(pn -> !packages.contains(pn))
 288                 .forEach(nonExistPackages::add);
 289 
 290             if (!nonExistPackages.isEmpty()) {
 291                 throw new PluginException("Packages that are exported or open in "
 292                     + descriptor.name() + " are not present: " + nonExistPackages);
 293             }
 294         }
 295 
 296         boolean hasModulePackages() throws IOException {
 297             Set<String> attrTypes = new HashSet<>();
 298             ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) {
 299                 @Override
 300                 public void visitAttribute(Attribute attr) {
 301                     attrTypes.add(attr.type);
 302                 }
 303             };
 304 
 305             // prototype of attributes that should be parsed
 306             Attribute[] attrs = new Attribute[] {
 307                 new ClassFileAttributes.ModulePackagesAttribute()
 308             };
 309 
 310             try (InputStream in = getInputStream()) {
 311                 // parse module-info.class
 312                 ClassReader cr = new ClassReader(in);
 313                 cr.accept(cv, attrs, 0);
 314                 return attrTypes.contains(ClassFileConstants.MODULE_PACKAGES);
 315             }
 316         }
 317 
 318         /**
 319          * Returns true if module-info.class should be written
 320          * 1. add ModulePackages attribute if not present; or
 321          * 2. drop ModuleTarget attribute except java.base
 322          */
 323         boolean shouldRewrite() {
 324             return addModulePackages || dropModuleTarget;
 325         }
 326 
 327         /**
 328          * Returns the bytes for the module-info.class with ModulePackages
 329          * attribute added and/or with ModuleTarget attribute dropped.
 330          */
 331         byte[] getBytes() throws IOException {
 332             try (InputStream in = getInputStream()) {
 333                 if (shouldRewrite()) {
 334                     ModuleInfoRewriter rewriter = new ModuleInfoRewriter(in);
 335                     if (addModulePackages) {
 336                         rewriter.addModulePackages(packages);
 337                     }
 338                     if (dropModuleTarget) {
 339                         rewriter.dropModuleTarget();
 340                     }
 341                     // rewritten module descriptor
 342                     byte[] bytes = rewriter.getBytes();
 343                     try (ByteArrayInputStream bain = new ByteArrayInputStream(bytes)) {
 344                         this.descriptor = ModuleDescriptor.read(bain);
 345                     }
 346                     return bytes;
 347                 } else {
 348                     return in.readAllBytes();
 349                 }
 350             }
 351         }
 352 
 353         /*
 354          * Returns the input stream of the module-info.class
 355          */
 356         InputStream getInputStream() {
 357             bain.reset();
 358             return bain;
 359         }
 360 
 361         class ModuleInfoRewriter extends ByteArrayOutputStream {
 362             final ModuleInfoExtender extender;
 363             ModuleInfoRewriter(InputStream in) {
 364                 this.extender = ModuleInfoExtender.newExtender(in);
 365             }
 366 
 367             void addModulePackages(Set<String> packages) {
 368                 // Add ModulePackages attribute
 369                 if (packages.size() > 0) {
 370                     extender.packages(packages);
 371                 }
 372             }
 373 
 374             void dropModuleTarget() {
 375                 extender.targetPlatform("", "", "");
 376             }
 377 
 378             byte[] getBytes() throws IOException {
 379                 extender.write(this);
 380                 return buf;
 381             }
 382         }
 383     }
 384 
 385     /**
 386      * ClassWriter of a new jdk.internal.module.SystemModules class
 387      * to reconstitute ModuleDescriptor of the system modules.
 388      */
 389     static class SystemModulesClassGenerator {
 390         private static final String CLASSNAME =
 391             "jdk/internal/module/SystemModules";
 392         private static final String MODULE_DESCRIPTOR_BUILDER =
 393             "jdk/internal/module/Builder";
 394         private static final String MODULE_DESCRIPTOR_ARRAY_SIGNATURE =
 395             "[Ljava/lang/module/ModuleDescriptor;";
 396         private static final String REQUIRES_MODIFIER_CLASSNAME =
 397             "java/lang/module/ModuleDescriptor$Requires$Modifier";
 398         private static final String EXPORTS_MODIFIER_CLASSNAME =
 399             "java/lang/module/ModuleDescriptor$Exports$Modifier";
 400         private static final String OPENS_MODIFIER_CLASSNAME =
 401             "java/lang/module/ModuleDescriptor$Opens$Modifier";
 402         private static final String MODULE_HASHES_ARRAY_SIGNATURE  =
 403             "[Ljdk/internal/module/ModuleHashes;";
 404         private static final String MODULE_RESOLUTION_CLASSNAME  =
 405             "jdk/internal/module/ModuleResolution";
 406         private static final String MODULE_RESOLUTIONS_ARRAY_SIGNATURE  =
 407             "[Ljdk/internal/module/ModuleResolution;";
 408 
 409         // static variables in SystemModules class
 410         private static final String MODULE_NAMES = "MODULE_NAMES";
 411         private static final String PACKAGE_COUNT = "PACKAGES_IN_BOOT_LAYER";
 412 
 413         private static final int MAX_LOCAL_VARS = 256;
 414 
 415         private final int BUILDER_VAR    = 0;
 416         private final int MD_VAR         = 1;  // variable for ModuleDescriptor
 417         private final int MH_VAR         = 1;  // variable for ModuleHashes
 418         private int nextLocalVar         = 2;  // index to next local variable
 419 
 420         private final ClassWriter cw;
 421         private boolean dropModuleTarget;
 422 
 423         // Method visitor for generating the SystemModules::modules() method
 424         private MethodVisitor mv;
 425 
 426         // list of all ModuleDescriptorBuilders, invoked in turn when building.
 427         private final List<ModuleInfo> moduleInfos = new ArrayList<>();
 428 
 429         // A builder to create one single Set instance for a given set of
 430         // names or modifiers to reduce the footprint
 431         // e.g. target modules of qualified exports
 432         private final DedupSetBuilder dedupSetBuilder
 433             = new DedupSetBuilder(this::getNextLocalVar);
 434 
 435         public SystemModulesClassGenerator(boolean retainModuleTarget) {
 436             this.cw = new ClassWriter(ClassWriter.COMPUTE_MAXS +
 437                                       ClassWriter.COMPUTE_FRAMES);
 438             this.dropModuleTarget = !retainModuleTarget;
 439         }
 440 
 441         private int getNextLocalVar() {
 442             return nextLocalVar++;
 443         }
 444 
 445         /*
 446          * static initializer initializing the static fields
 447          *
 448          * static Map<String, ModuleDescriptor> map = new HashMap<>();
 449          */
 450         private void clinit(int numModules, int numPackages,
 451                             boolean hasSplitPackages) {
 452             cw.visit(Opcodes.V1_8, ACC_PUBLIC+ACC_FINAL+ACC_SUPER, CLASSNAME,
 453                      null, "java/lang/Object", null);
 454 
 455             // public static String[] MODULE_NAMES = new String[] {....};
 456             cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULE_NAMES,
 457                     "[Ljava/lang/String;", null, null)
 458                     .visitEnd();
 459 
 460             // public static int PACKAGES_IN_BOOT_LAYER;
 461             cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, PACKAGE_COUNT,
 462                     "I", null, numPackages)
 463                     .visitEnd();
 464 
 465             MethodVisitor clinit =
 466                 cw.visitMethod(ACC_STATIC, "<clinit>", "()V",
 467                                null, null);
 468             clinit.visitCode();
 469 
 470             // create the MODULE_NAMES array
 471             pushInt(clinit, numModules);
 472             clinit.visitTypeInsn(ANEWARRAY, "java/lang/String");
 473 
 474             int index = 0;
 475             for (ModuleInfo minfo : moduleInfos) {
 476                 clinit.visitInsn(DUP);                  // arrayref
 477                 pushInt(clinit, index++);
 478                 clinit.visitLdcInsn(minfo.moduleName()); // value
 479                 clinit.visitInsn(AASTORE);
 480             }
 481 
 482             clinit.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULE_NAMES,
 483                     "[Ljava/lang/String;");
 484 
 485             clinit.visitInsn(RETURN);
 486             clinit.visitMaxs(0, 0);
 487             clinit.visitEnd();
 488 
 489             // public static boolean hasSplitPackages();
 490             MethodVisitor split =
 491                 cw.visitMethod(ACC_PUBLIC+ACC_STATIC, "hasSplitPackages",
 492                                "()Z", null, null);
 493             split.visitCode();
 494             split.visitInsn(hasSplitPackages ? ICONST_1 : ICONST_0);
 495             split.visitInsn(IRETURN);
 496             split.visitMaxs(0, 0);
 497             split.visitEnd();
 498 
 499         }
 500 
 501         /*
 502          * Adds the given ModuleDescriptor to the system module list.
 503          * It performs link-time validation and prepares mapping from various
 504          * Sets to SetBuilders to emit an optimized number of sets during build.
 505          */
 506         public ResourcePoolEntry buildModuleInfo(ResourcePoolEntry entry,
 507                                                  Set<String> packages)
 508             throws IOException
 509         {
 510             if (moduleInfos.isEmpty() && !entry.moduleName().equals("java.base")) {
 511                 throw new InternalError("java.base must be the first module to process");
 512             }
 513 
 514             ModuleInfo moduleInfo;
 515             if (entry.moduleName().equals("java.base")) {
 516                 moduleInfo = new ModuleInfo(entry.contentBytes(), packages, false);
 517                 ModuleDescriptor md = moduleInfo.descriptor;
 518                 // drop Moduletarget attribute only if java.base has all OS properties
 519                 // otherwise, retain it
 520                 if (dropModuleTarget &&
 521                         md.osName().isPresent() && md.osArch().isPresent() &&
 522                         md.osVersion().isPresent()) {
 523                     dropModuleTarget = true;
 524                 } else {
 525                     dropModuleTarget = false;
 526                 }
 527             } else {
 528                 moduleInfo = new ModuleInfo(entry.contentBytes(), packages, dropModuleTarget);
 529             }
 530 
 531             // link-time validation
 532             moduleInfo.validateNames();
 533             // check if any exported or open package is not present
 534             moduleInfo.validatePackages();
 535 
 536             // module-info.class may be overridden for optimization
 537             // 1. update ModuleTarget attribute to drop osName, osArch, osVersion
 538             // 2. add/update ModulePackages attribute
 539             if (moduleInfo.shouldRewrite()) {
 540                 entry = entry.copyWithContent(moduleInfo.getBytes());
 541             }
 542             moduleInfos.add(moduleInfo);
 543             dedups(moduleInfo.descriptor());
 544             return entry;
 545         }
 546 
 547         private void dedups(ModuleDescriptor md) {
 548             // exports
 549             for (Exports e : md.exports()) {
 550                 dedupSetBuilder.stringSet(e.targets());
 551                 dedupSetBuilder.exportsModifiers(e.modifiers());
 552             }
 553 
 554             // opens
 555             for (Opens opens : md.opens()) {
 556                 dedupSetBuilder.stringSet(opens.targets());
 557                 dedupSetBuilder.opensModifiers(opens.modifiers());
 558             }
 559 
 560             // requires
 561             for (Requires r : md.requires()) {
 562                 dedupSetBuilder.requiresModifiers(r.modifiers());
 563             }
 564 
 565             // uses
 566             dedupSetBuilder.stringSet(md.uses());
 567         }
 568 
 569         /*
 570          * Generate bytecode for SystemModules
 571          */
 572         public ClassWriter getClassWriter() {
 573             int numModules = moduleInfos.size();
 574             Set<String> allPackages = new HashSet<>();
 575             int packageCount = 0;
 576             for (ModuleInfo minfo : moduleInfos) {
 577                 allPackages.addAll(minfo.packages);
 578                 packageCount += minfo.packages.size();
 579             }
 580 
 581             int numPackages = allPackages.size();
 582             boolean hasSplitPackages = (numPackages < packageCount);
 583             clinit(numModules, numPackages, hasSplitPackages);
 584 
 585             // generate SystemModules::descriptors
 586             genDescriptorsMethod();
 587             // generate SystemModules::hashes
 588             genHashesMethod();
 589             // generate SystemModules::moduleResolutions
 590             genModuleResolutionsMethod();
 591 
 592             return cw;
 593         }
 594 
 595         /*
 596          * Generate bytecode for SystemModules::descriptors method
 597          */
 598         private void genDescriptorsMethod() {
 599             this.mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
 600                                      "descriptors",
 601                                      "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
 602                                      "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
 603                                      null);
 604             mv.visitCode();
 605             pushInt(mv, moduleInfos.size());
 606             mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor");
 607             mv.visitVarInsn(ASTORE, MD_VAR);
 608 
 609             for (int index = 0; index < moduleInfos.size(); index++) {
 610                 ModuleInfo minfo = moduleInfos.get(index);
 611                 new ModuleDescriptorBuilder(minfo.descriptor(),
 612                                             minfo.packages(),
 613                                             index).build();
 614             }
 615             mv.visitVarInsn(ALOAD, MD_VAR);
 616             mv.visitInsn(ARETURN);
 617             mv.visitMaxs(0, 0);
 618             mv.visitEnd();
 619 
 620         }
 621 
 622         /*
 623          * Generate bytecode for SystemModules::hashes method
 624          */
 625         private void genHashesMethod() {
 626             MethodVisitor hmv =
 627                 cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
 628                                "hashes",
 629                                "()" + MODULE_HASHES_ARRAY_SIGNATURE,
 630                                "()" + MODULE_HASHES_ARRAY_SIGNATURE,
 631                                null);
 632             hmv.visitCode();
 633             pushInt(hmv, moduleInfos.size());
 634             hmv.visitTypeInsn(ANEWARRAY, "jdk/internal/module/ModuleHashes");
 635             hmv.visitVarInsn(ASTORE, MH_VAR);
 636 
 637             for (int index = 0; index < moduleInfos.size(); index++) {
 638                 ModuleInfo minfo = moduleInfos.get(index);
 639                 if (minfo.recordedHashes() != null) {
 640                     new ModuleHashesBuilder(minfo.recordedHashes(),
 641                                             index,
 642                                             hmv).build();
 643                 }
 644             }
 645 
 646             hmv.visitVarInsn(ALOAD, MH_VAR);
 647             hmv.visitInsn(ARETURN);
 648             hmv.visitMaxs(0, 0);
 649             hmv.visitEnd();
 650 
 651         }
 652 
 653         /*
 654          * Generate bytecode for SystemModules::methodResoultions method
 655          */
 656         private void genModuleResolutionsMethod() {
 657             MethodVisitor mresmv =
 658                 cw.visitMethod(ACC_PUBLIC+ACC_STATIC,
 659                                "moduleResolutions",
 660                                "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
 661                                "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
 662                                null);
 663             mresmv.visitCode();
 664             pushInt(mresmv, moduleInfos.size());
 665             mresmv.visitTypeInsn(ANEWARRAY, MODULE_RESOLUTION_CLASSNAME);
 666             mresmv.visitVarInsn(ASTORE, 0);
 667 
 668             for (int index=0; index < moduleInfos.size(); index++) {
 669                 ModuleInfo minfo = moduleInfos.get(index);
 670                 if (minfo.moduleResolution() != null) {
 671                     mresmv.visitVarInsn(ALOAD, 0);
 672                     pushInt(mresmv, index);
 673                     mresmv.visitTypeInsn(NEW, MODULE_RESOLUTION_CLASSNAME);
 674                     mresmv.visitInsn(DUP);
 675                     mresmv.visitLdcInsn(minfo.moduleResolution().value());
 676                     mresmv.visitMethodInsn(INVOKESPECIAL,
 677                                            MODULE_RESOLUTION_CLASSNAME,
 678                                            "<init>",
 679                                            "(I)V", false);
 680                     mresmv.visitInsn(AASTORE);
 681                 }
 682             }
 683             mresmv.visitVarInsn(ALOAD, 0);
 684             mresmv.visitInsn(ARETURN);
 685             mresmv.visitMaxs(0, 0);
 686             mresmv.visitEnd();
 687         }
 688 
 689         public boolean isOverriddenClass(String path) {
 690             return path.equals("/java.base/" + CLASSNAME + ".class");
 691         }
 692 
 693         void pushInt(MethodVisitor mv, int num) {
 694             if (num <= 5) {
 695                 mv.visitInsn(ICONST_0 + num);
 696             } else if (num < Byte.MAX_VALUE) {
 697                 mv.visitIntInsn(BIPUSH, num);
 698             } else if (num < Short.MAX_VALUE) {
 699                 mv.visitIntInsn(SIPUSH, num);
 700             } else {
 701                 throw new IllegalArgumentException("exceed limit: " + num);
 702             }
 703         }
 704 
 705         class ModuleDescriptorBuilder {
 706             static final String BUILDER_TYPE = "Ljdk/internal/module/Builder;";
 707             static final String EXPORTS_TYPE =
 708                 "Ljava/lang/module/ModuleDescriptor$Exports;";
 709             static final String OPENS_TYPE =
 710                 "Ljava/lang/module/ModuleDescriptor$Opens;";
 711             static final String PROVIDES_TYPE =
 712                 "Ljava/lang/module/ModuleDescriptor$Provides;";
 713             static final String REQUIRES_TYPE =
 714                 "Ljava/lang/module/ModuleDescriptor$Requires;";
 715 
 716             // method signature for static Builder::newExports, newOpens,
 717             // newProvides, newRequires methods
 718             static final String EXPORTS_MODIFIER_SET_STRING_SET_SIG =
 719                 "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)"
 720                     + EXPORTS_TYPE;
 721             static final String EXPORTS_MODIFIER_SET_STRING_SIG =
 722                 "(Ljava/util/Set;Ljava/lang/String;)" + EXPORTS_TYPE;
 723             static final String OPENS_MODIFIER_SET_STRING_SET_SIG =
 724                 "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)"
 725                     + OPENS_TYPE;
 726             static final String OPENS_MODIFIER_SET_STRING_SIG =
 727                 "(Ljava/util/Set;Ljava/lang/String;)" + OPENS_TYPE;
 728             static final String PROVIDES_STRING_LIST_SIG =
 729                 "(Ljava/lang/String;Ljava/util/List;)" + PROVIDES_TYPE;
 730             static final String REQUIRES_SET_STRING_SIG =
 731                 "(Ljava/util/Set;Ljava/lang/String;)" + REQUIRES_TYPE;
 732             static final String REQUIRES_SET_STRING_STRING_SIG =
 733                 "(Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;)" + REQUIRES_TYPE;
 734 
 735             // method signature for Builder instance methods that
 736             // return this Builder instance
 737             static final String EXPORTS_ARRAY_SIG =
 738                 "([" + EXPORTS_TYPE + ")" + BUILDER_TYPE;
 739             static final String OPENS_ARRAY_SIG =
 740                 "([" + OPENS_TYPE + ")" + BUILDER_TYPE;
 741             static final String PROVIDES_ARRAY_SIG =
 742                 "([" + PROVIDES_TYPE + ")" + BUILDER_TYPE;
 743             static final String REQUIRES_ARRAY_SIG =
 744                 "([" + REQUIRES_TYPE + ")" + BUILDER_TYPE;
 745             static final String SET_SIG = "(Ljava/util/Set;)" + BUILDER_TYPE;
 746             static final String STRING_SIG = "(Ljava/lang/String;)" + BUILDER_TYPE;
 747             static final String BOOLEAN_SIG = "(Z)" + BUILDER_TYPE;
 748 
 749             final ModuleDescriptor md;
 750             final Set<String> packages;
 751             final int index;
 752             ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages, int index) {
 753                 if (md.isAutomatic()) {
 754                     throw new InternalError("linking automatic module is not supported");
 755                 }
 756                 this.md = md;
 757                 this.packages = packages;
 758                 this.index = index;
 759             }
 760 
 761             void build() {
 762                 // new jdk.internal.module.Builder
 763                 newBuilder();
 764 
 765                 // requires
 766                 requires(md.requires());
 767 
 768                 // exports
 769                 exports(md.exports());
 770 
 771                 // opens
 772                 opens(md.opens());
 773 
 774                 // uses
 775                 uses(md.uses());
 776 
 777                 // provides
 778                 provides(md.provides());
 779 
 780                 // all packages
 781                 packages(packages);
 782 
 783                 // version
 784                 md.version().ifPresent(this::version);
 785 
 786                 // main class
 787                 md.mainClass().ifPresent(this::mainClass);
 788 
 789                 // os name, arch, version
 790                 targetPlatform(md.osName().orElse(null),
 791                                md.osArch().orElse(null),
 792                                md.osVersion().orElse(null));
 793 
 794                 putModuleDescriptor();
 795             }
 796 
 797             void newBuilder() {
 798                 mv.visitTypeInsn(NEW, MODULE_DESCRIPTOR_BUILDER);
 799                 mv.visitInsn(DUP);
 800                 mv.visitLdcInsn(md.name());
 801                 mv.visitMethodInsn(INVOKESPECIAL, MODULE_DESCRIPTOR_BUILDER,
 802                     "<init>", "(Ljava/lang/String;)V", false);
 803                 mv.visitVarInsn(ASTORE, BUILDER_VAR);
 804                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
 805 
 806                 if (md.isOpen()) {
 807                     setModuleBit("open", true);
 808                 }
 809                 if (md.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)) {
 810                     setModuleBit("synthetic", true);
 811                 }
 812                 if (md.modifiers().contains(ModuleDescriptor.Modifier.MANDATED)) {
 813                     setModuleBit("mandated", true);
 814                 }
 815             }
 816 
 817             /*
 818              * Invoke Builder.<methodName>(boolean value)
 819              */
 820             void setModuleBit(String methodName, boolean value) {
 821                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
 822                 if (value) {
 823                     mv.visitInsn(ICONST_1);
 824                 } else {
 825                     mv.visitInsn(ICONST_0);
 826                 }
 827                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
 828                     methodName, BOOLEAN_SIG, false);
 829                 mv.visitInsn(POP);
 830             }
 831 
 832             /*
 833              * Put ModuleDescriptor into the modules array
 834              */
 835             void putModuleDescriptor() {
 836                 mv.visitVarInsn(ALOAD, MD_VAR);
 837                 pushInt(mv, index);
 838                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
 839                 mv.visitLdcInsn(md.hashCode());
 840                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
 841                     "build", "(I)Ljava/lang/module/ModuleDescriptor;",
 842                     false);
 843                 mv.visitInsn(AASTORE);
 844             }
 845 
 846             /*
 847              * Call Builder::newRequires to create Requires instances and
 848              * then pass it to the builder by calling:
 849              *      Builder.requires(Requires[])
 850              *
 851              */
 852             void requires(Set<Requires> requires) {
 853                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
 854                 pushInt(mv, requires.size());
 855                 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Requires");
 856                 int arrayIndex = 0;
 857                 for (Requires require : requires) {
 858                     String compiledVersion = null;
 859                     if (require.compiledVersion().isPresent()) {
 860                         compiledVersion = require.compiledVersion().get().toString();
 861                     }
 862 
 863                     mv.visitInsn(DUP);               // arrayref
 864                     pushInt(mv, arrayIndex++);
 865                     newRequires(require.modifiers(), require.name(), compiledVersion);
 866                     mv.visitInsn(AASTORE);
 867                 }
 868                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
 869                     "requires", REQUIRES_ARRAY_SIG, false);
 870             }
 871 
 872             /*
 873              * Invoke Builder.newRequires(Set<Modifier> mods, String mn, String compiledVersion)
 874              *
 875              * Set<Modifier> mods = ...
 876              * Builder.newRequires(mods, mn, compiledVersion);
 877              */
 878             void newRequires(Set<Requires.Modifier> mods, String name, String compiledVersion) {
 879                 int varIndex = dedupSetBuilder.indexOfRequiresModifiers(mods);
 880                 mv.visitVarInsn(ALOAD, varIndex);
 881                 mv.visitLdcInsn(name);
 882                 if (compiledVersion != null) {
 883                     mv.visitLdcInsn(compiledVersion);
 884                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
 885                         "newRequires", REQUIRES_SET_STRING_STRING_SIG, false);
 886                 } else {
 887                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
 888                         "newRequires", REQUIRES_SET_STRING_SIG, false);
 889                 }
 890             }
 891 
 892             /*
 893              * Call Builder::newExports to create Exports instances and
 894              * then pass it to the builder by calling:
 895              *      Builder.exports(Exports[])
 896              *
 897              */
 898             void exports(Set<Exports> exports) {
 899                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
 900                 pushInt(mv, exports.size());
 901                 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Exports");
 902                 int arrayIndex = 0;
 903                 for (Exports export : exports) {
 904                     mv.visitInsn(DUP);    // arrayref
 905                     pushInt(mv, arrayIndex++);
 906                     newExports(export.modifiers(), export.source(), export.targets());
 907                     mv.visitInsn(AASTORE);
 908                 }
 909                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
 910                     "exports", EXPORTS_ARRAY_SIG, false);
 911             }
 912 
 913             /*
 914              * Invoke
 915              *     Builder.newExports(Set<Exports.Modifier> ms, String pn,
 916              *                        Set<String> targets)
 917              * or
 918              *     Builder.newExports(Set<Exports.Modifier> ms, String pn)
 919              *
 920              * Set<String> targets = new HashSet<>();
 921              * targets.add(t);
 922              * :
 923              * :
 924              *
 925              * Set<Modifier> mods = ...
 926              * Builder.newExports(mods, pn, targets);
 927              */
 928             void newExports(Set<Exports.Modifier> ms, String pn, Set<String> targets) {
 929                 int modifiersSetIndex = dedupSetBuilder.indexOfExportsModifiers(ms);
 930                 if (!targets.isEmpty()) {
 931                     int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets);
 932                     mv.visitVarInsn(ALOAD, modifiersSetIndex);
 933                     mv.visitLdcInsn(pn);
 934                     mv.visitVarInsn(ALOAD, stringSetIndex);
 935                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
 936                         "newExports", EXPORTS_MODIFIER_SET_STRING_SET_SIG, false);
 937                 } else {
 938                     mv.visitVarInsn(ALOAD, modifiersSetIndex);
 939                     mv.visitLdcInsn(pn);
 940                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
 941                         "newExports", EXPORTS_MODIFIER_SET_STRING_SIG, false);
 942                 }
 943             }
 944 
 945 
 946             /**
 947              * Call Builder::newOpens to create Opens instances and
 948              * then pass it to the builder by calling:
 949              * Builder.opens(Opens[])
 950              */
 951             void opens(Set<Opens> opens) {
 952                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
 953                 pushInt(mv, opens.size());
 954                 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Opens");
 955                 int arrayIndex = 0;
 956                 for (Opens open : opens) {
 957                     mv.visitInsn(DUP);    // arrayref
 958                     pushInt(mv, arrayIndex++);
 959                     newOpens(open.modifiers(), open.source(), open.targets());
 960                     mv.visitInsn(AASTORE);
 961                 }
 962                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
 963                     "opens", OPENS_ARRAY_SIG, false);
 964             }
 965 
 966             /*
 967              * Invoke
 968              *     Builder.newOpens(Set<Opens.Modifier> ms, String pn,
 969              *                        Set<String> targets)
 970              * or
 971              *     Builder.newOpens(Set<Opens.Modifier> ms, String pn)
 972              *
 973              * Set<String> targets = new HashSet<>();
 974              * targets.add(t);
 975              * :
 976              * :
 977              *
 978              * Set<Modifier> mods = ...
 979              * Builder.newOpens(mods, pn, targets);
 980              */
 981             void newOpens(Set<Opens.Modifier> ms, String pn, Set<String> targets) {
 982                 int modifiersSetIndex = dedupSetBuilder.indexOfOpensModifiers(ms);
 983                 if (!targets.isEmpty()) {
 984                     int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets);
 985                     mv.visitVarInsn(ALOAD, modifiersSetIndex);
 986                     mv.visitLdcInsn(pn);
 987                     mv.visitVarInsn(ALOAD, stringSetIndex);
 988                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
 989                         "newOpens", OPENS_MODIFIER_SET_STRING_SET_SIG, false);
 990                 } else {
 991                     mv.visitVarInsn(ALOAD, modifiersSetIndex);
 992                     mv.visitLdcInsn(pn);
 993                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
 994                         "newOpens", OPENS_MODIFIER_SET_STRING_SIG, false);
 995                 }
 996             }
 997 
 998             /*
 999              * Invoke Builder.uses(Set<String> uses)
1000              */
1001             void uses(Set<String> uses) {
1002                 int varIndex = dedupSetBuilder.indexOfStringSet(uses);
1003                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1004                 mv.visitVarInsn(ALOAD, varIndex);
1005                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1006                     "uses", SET_SIG, false);
1007                 mv.visitInsn(POP);
1008             }
1009 
1010             /*
1011             * Call Builder::newProvides to create Provides instances and
1012             * then pass it to the builder by calling:
1013             *      Builder.provides(Provides[] provides)
1014             *
1015             */
1016             void provides(Collection<Provides> provides) {
1017                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1018                 pushInt(mv, provides.size());
1019                 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Provides");
1020                 int arrayIndex = 0;
1021                 for (Provides provide : provides) {
1022                     mv.visitInsn(DUP);    // arrayref
1023                     pushInt(mv, arrayIndex++);
1024                     newProvides(provide.service(), provide.providers());
1025                     mv.visitInsn(AASTORE);
1026                 }
1027                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1028                     "provides", PROVIDES_ARRAY_SIG, false);
1029             }
1030 
1031             /*
1032              * Invoke Builder.newProvides(String service, Set<String> providers)
1033              *
1034              * Set<String> providers = new HashSet<>();
1035              * providers.add(impl);
1036              * :
1037              * :
1038              * Builder.newProvides(service, providers);
1039              */
1040             void newProvides(String service, List<String> providers) {
1041                 mv.visitLdcInsn(service);
1042                 pushInt(mv, providers.size());
1043                 mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
1044                 int arrayIndex = 0;
1045                 for (String provider : providers) {
1046                     mv.visitInsn(DUP);    // arrayref
1047                     pushInt(mv, arrayIndex++);
1048                     mv.visitLdcInsn(provider);
1049                     mv.visitInsn(AASTORE);
1050                 }
1051                 mv.visitMethodInsn(INVOKESTATIC, "java/util/List",
1052                     "of", "([Ljava/lang/Object;)Ljava/util/List;", true);
1053                 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
1054                     "newProvides", PROVIDES_STRING_LIST_SIG, false);
1055             }
1056 
1057             /*
1058              * Invoke Builder.packages(String pn)
1059              */
1060             void packages(Set<String> packages) {
1061                 int varIndex = dedupSetBuilder.newStringSet(packages);
1062                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1063                 mv.visitVarInsn(ALOAD, varIndex);
1064                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1065                     "packages", SET_SIG, false);
1066                 mv.visitInsn(POP);
1067             }
1068 
1069             /*
1070              * Invoke Builder.mainClass(String cn)
1071              */
1072             void mainClass(String cn) {
1073                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1074                 mv.visitLdcInsn(cn);
1075                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1076                     "mainClass", STRING_SIG, false);
1077                 mv.visitInsn(POP);
1078             }
1079 
1080             /*
1081              * Invoke Builder.version(Version v);
1082              */
1083             void version(Version v) {
1084                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1085                 mv.visitLdcInsn(v.toString());
1086                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1087                     "version", STRING_SIG, false);
1088                 mv.visitInsn(POP);
1089             }
1090 
1091             /*
1092              * Invoke Builder.osName(String name)
1093              *        Builder.osArch(String arch)
1094              *        Builder.osVersion(String version)
1095              */
1096             void targetPlatform(String osName, String osArch, String osVersion) {
1097                 if (osName != null) {
1098                     invokeBuilderMethod("osName", osName);
1099                 }
1100 
1101                 if (osArch != null) {
1102                     invokeBuilderMethod("osArch", osArch);
1103                 }
1104 
1105                 if (osVersion != null) {
1106                     invokeBuilderMethod("osVersion", osVersion);
1107                 }
1108             }
1109 
1110             void invokeBuilderMethod(String methodName, String value) {
1111                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1112                 mv.visitLdcInsn(value);
1113                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1114                     methodName, STRING_SIG, false);
1115                 mv.visitInsn(POP);
1116             }
1117         }
1118 
1119         class ModuleHashesBuilder {
1120             private static final String MODULE_HASHES_BUILDER =
1121                 "jdk/internal/module/ModuleHashes$Builder";
1122             private static final String MODULE_HASHES_BUILDER_TYPE =
1123                 "L" + MODULE_HASHES_BUILDER + ";";
1124             static final String STRING_BYTE_ARRAY_SIG =
1125                 "(Ljava/lang/String;[B)" + MODULE_HASHES_BUILDER_TYPE;
1126 
1127             final ModuleHashes recordedHashes;
1128             final MethodVisitor hmv;
1129             final int index;
1130 
1131             ModuleHashesBuilder(ModuleHashes hashes, int index, MethodVisitor hmv) {
1132                 this.recordedHashes = hashes;
1133                 this.hmv = hmv;
1134                 this.index = index;
1135             }
1136 
1137             /**
1138              * Build ModuleHashes
1139              */
1140             void build() {
1141                 if (recordedHashes == null)
1142                     return;
1143 
1144                 // new jdk.internal.module.ModuleHashes.Builder
1145                 newModuleHashesBuilder();
1146 
1147                 // Invoke ModuleHashes.Builder::hashForModule
1148                 recordedHashes
1149                     .names()
1150                     .forEach(mn -> hashForModule(mn, recordedHashes.hashFor(mn)));
1151 
1152                 // Put ModuleHashes into the hashes array
1153                 pushModuleHashes();
1154             }
1155 
1156 
1157             /*
1158              * Create ModuleHashes.Builder instance
1159              */
1160             void newModuleHashesBuilder() {
1161                 hmv.visitTypeInsn(NEW, MODULE_HASHES_BUILDER);
1162                 hmv.visitInsn(DUP);
1163                 hmv.visitLdcInsn(recordedHashes.algorithm());
1164                 pushInt(hmv, ((4 * recordedHashes.names().size()) / 3) + 1);
1165                 hmv.visitMethodInsn(INVOKESPECIAL, MODULE_HASHES_BUILDER,
1166                     "<init>", "(Ljava/lang/String;I)V", false);
1167                 hmv.visitVarInsn(ASTORE, BUILDER_VAR);
1168                 hmv.visitVarInsn(ALOAD, BUILDER_VAR);
1169             }
1170 
1171 
1172             /*
1173              * Invoke ModuleHashes.Builder::build and put the returned
1174              * ModuleHashes to the hashes array
1175              */
1176             void pushModuleHashes() {
1177                 hmv.visitVarInsn(ALOAD, MH_VAR);
1178                 pushInt(hmv, index);
1179                 hmv.visitVarInsn(ALOAD, BUILDER_VAR);
1180                 hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER,
1181                     "build", "()Ljdk/internal/module/ModuleHashes;",
1182                     false);
1183                 hmv.visitInsn(AASTORE);
1184             }
1185 
1186             /*
1187              * Invoke ModuleHashes.Builder.hashForModule(String name, byte[] hash);
1188              */
1189             void hashForModule(String name, byte[] hash) {
1190                 hmv.visitVarInsn(ALOAD, BUILDER_VAR);
1191                 hmv.visitLdcInsn(name);
1192 
1193                 pushInt(hmv, hash.length);
1194                 hmv.visitIntInsn(NEWARRAY, T_BYTE);
1195                 for (int i = 0; i < hash.length; i++) {
1196                     hmv.visitInsn(DUP);              // arrayref
1197                     pushInt(hmv, i);
1198                     hmv.visitIntInsn(BIPUSH, hash[i]);
1199                     hmv.visitInsn(BASTORE);
1200                 }
1201 
1202                 hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER,
1203                     "hashForModule", STRING_BYTE_ARRAY_SIG, false);
1204                 hmv.visitInsn(POP);
1205             }
1206         }
1207 
1208         /*
1209          * Wraps set creation, ensuring identical sets are properly deduplicated.
1210          */
1211         class DedupSetBuilder {
1212             // map Set<String> to a specialized builder to allow them to be
1213             // deduplicated as they are requested
1214             final Map<Set<String>, SetBuilder<String>> stringSets = new HashMap<>();
1215 
1216             // map Set<Requires.Modifier> to a specialized builder to allow them to be
1217             // deduplicated as they are requested
1218             final Map<Set<Requires.Modifier>, EnumSetBuilder<Requires.Modifier>>
1219                 requiresModifiersSets = new HashMap<>();
1220 
1221             // map Set<Exports.Modifier> to a specialized builder to allow them to be
1222             // deduplicated as they are requested
1223             final Map<Set<Exports.Modifier>, EnumSetBuilder<Exports.Modifier>>
1224                 exportsModifiersSets = new HashMap<>();
1225 
1226             // map Set<Opens.Modifier> to a specialized builder to allow them to be
1227             // deduplicated as they are requested
1228             final Map<Set<Opens.Modifier>, EnumSetBuilder<Opens.Modifier>>
1229                 opensModifiersSets = new HashMap<>();
1230 
1231             private final int stringSetVar;
1232             private final int enumSetVar;
1233             private final IntSupplier localVarSupplier;
1234 
1235             DedupSetBuilder(IntSupplier localVarSupplier) {
1236                 this.stringSetVar = localVarSupplier.getAsInt();
1237                 this.enumSetVar = localVarSupplier.getAsInt();
1238                 this.localVarSupplier = localVarSupplier;
1239             }
1240 
1241             /*
1242              * Add the given set of strings to this builder.
1243              */
1244             void stringSet(Set<String> strings) {
1245                 stringSets.computeIfAbsent(strings,
1246                     s -> new SetBuilder<>(s, stringSetVar, localVarSupplier)
1247                 ).increment();
1248             }
1249 
1250             /*
1251              * Add the given set of Exports.Modifiers
1252              */
1253             void exportsModifiers(Set<Exports.Modifier> mods) {
1254                 exportsModifiersSets.computeIfAbsent(mods, s ->
1255                                 new EnumSetBuilder<>(s, EXPORTS_MODIFIER_CLASSNAME,
1256                                         enumSetVar, localVarSupplier)
1257                 ).increment();
1258             }
1259 
1260             /*
1261              * Add the given set of Opens.Modifiers
1262              */
1263             void opensModifiers(Set<Opens.Modifier> mods) {
1264                 opensModifiersSets.computeIfAbsent(mods, s ->
1265                                 new EnumSetBuilder<>(s, OPENS_MODIFIER_CLASSNAME,
1266                                         enumSetVar, localVarSupplier)
1267                 ).increment();
1268             }
1269 
1270             /*
1271              * Add the given set of Requires.Modifiers
1272              */
1273             void requiresModifiers(Set<Requires.Modifier> mods) {
1274                 requiresModifiersSets.computeIfAbsent(mods, s ->
1275                     new EnumSetBuilder<>(s, REQUIRES_MODIFIER_CLASSNAME,
1276                                          enumSetVar, localVarSupplier)
1277                 ).increment();
1278             }
1279 
1280             /*
1281              * Retrieve the index to the given set of Strings. Emit code to
1282              * generate it when SetBuilder::build is called.
1283              */
1284             int indexOfStringSet(Set<String> names) {
1285                 return stringSets.get(names).build();
1286             }
1287 
1288             /*
1289              * Retrieve the index to the given set of Exports.Modifier.
1290              * Emit code to generate it when EnumSetBuilder::build is called.
1291              */
1292             int indexOfExportsModifiers(Set<Exports.Modifier> mods) {
1293                 return exportsModifiersSets.get(mods).build();
1294             }
1295 
1296             /**
1297              * Retrieve the index to the given set of Opens.Modifier.
1298              * Emit code to generate it when EnumSetBuilder::build is called.
1299              */
1300             int indexOfOpensModifiers(Set<Opens.Modifier> mods) {
1301                 return opensModifiersSets.get(mods).build();
1302             }
1303 
1304 
1305             /*
1306              * Retrieve the index to the given set of Requires.Modifier.
1307              * Emit code to generate it when EnumSetBuilder::build is called.
1308              */
1309             int indexOfRequiresModifiers(Set<Requires.Modifier> mods) {
1310                 return requiresModifiersSets.get(mods).build();
1311             }
1312 
1313             /*
1314              * Build a new string set without any attempt to deduplicate it.
1315              */
1316             int newStringSet(Set<String> names) {
1317                 int index = new SetBuilder<>(names, stringSetVar, localVarSupplier).build();
1318                 assert index == stringSetVar;
1319                 return index;
1320             }
1321         }
1322 
1323         /*
1324          * SetBuilder generates bytecode to create one single instance of Set
1325          * for a given set of elements and assign to a local variable slot.
1326          * When there is only one single reference to a Set<T>,
1327          * it will reuse defaultVarIndex.  For a Set with multiple references,
1328          * it will use a new local variable retrieved from the nextLocalVar
1329          */
1330         class SetBuilder<T> {
1331             private final Set<T> elements;
1332             private final int defaultVarIndex;
1333             private final IntSupplier nextLocalVar;
1334             private int refCount;
1335             private int localVarIndex;
1336 
1337             SetBuilder(Set<T> elements,
1338                        int defaultVarIndex,
1339                        IntSupplier nextLocalVar) {
1340                 this.elements = elements;
1341                 this.defaultVarIndex = defaultVarIndex;
1342                 this.nextLocalVar = nextLocalVar;
1343             }
1344 
1345             /*
1346              * Increments the number of references to this particular set.
1347              */
1348             final void increment() {
1349                 refCount++;
1350             }
1351 
1352             /**
1353              * Generate the appropriate instructions to load an object reference
1354              * to the element onto the stack.
1355              */
1356             void visitElement(T element, MethodVisitor mv) {
1357                 mv.visitLdcInsn(element);
1358             }
1359 
1360             /*
1361              * Build bytecode for the Set represented by this builder,
1362              * or get the local variable index of a previously generated set
1363              * (in the local scope).
1364              *
1365              * @return local variable index of the generated set.
1366              */
1367             final int build() {
1368                 int index = localVarIndex;
1369                 if (localVarIndex == 0) {
1370                     // if non-empty and more than one set reference this builder,
1371                     // emit to a unique local
1372                     index = refCount <= 1 ? defaultVarIndex
1373                                           : nextLocalVar.getAsInt();
1374                     if (index < MAX_LOCAL_VARS) {
1375                         localVarIndex = index;
1376                     } else {
1377                         // overflow: disable optimization by using localVarIndex = 0
1378                         index = defaultVarIndex;
1379                     }
1380 
1381                     generateSetOf(index);
1382                 }
1383                 return index;
1384             }
1385 
1386             private void generateSetOf(int index) {
1387                 if (elements.size() <= 10) {
1388                     // call Set.of(e1, e2, ...)
1389                     StringBuilder sb = new StringBuilder("(");
1390                     for (T t : elements) {
1391                         sb.append("Ljava/lang/Object;");
1392                         visitElement(t, mv);
1393                     }
1394                     sb.append(")Ljava/util/Set;");
1395                     mv.visitMethodInsn(INVOKESTATIC, "java/util/Set",
1396                             "of", sb.toString(), true);
1397                 } else {
1398                     // call Set.of(E... elements)
1399                     pushInt(mv, elements.size());
1400                     mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
1401                     int arrayIndex = 0;
1402                     for (T t : elements) {
1403                         mv.visitInsn(DUP);    // arrayref
1404                         pushInt(mv, arrayIndex);
1405                         visitElement(t, mv);  // value
1406                         mv.visitInsn(AASTORE);
1407                         arrayIndex++;
1408                     }
1409                     mv.visitMethodInsn(INVOKESTATIC, "java/util/Set",
1410                             "of", "([Ljava/lang/Object;)Ljava/util/Set;", true);
1411                 }
1412                 mv.visitVarInsn(ASTORE, index);
1413             }
1414         }
1415 
1416         /*
1417          * Generates bytecode to create one single instance of EnumSet
1418          * for a given set of modifiers and assign to a local variable slot.
1419          */
1420         class EnumSetBuilder<T> extends SetBuilder<T> {
1421 
1422             private final String className;
1423 
1424             EnumSetBuilder(Set<T> modifiers, String className,
1425                            int defaultVarIndex,
1426                            IntSupplier nextLocalVar) {
1427                 super(modifiers, defaultVarIndex, nextLocalVar);
1428                 this.className = className;
1429             }
1430 
1431             /**
1432              * Loads an Enum field.
1433              */
1434             void visitElement(T t, MethodVisitor mv) {
1435                 mv.visitFieldInsn(GETSTATIC, className, t.toString(),
1436                                   "L" + className + ";");
1437             }
1438         }
1439     }
1440 }