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.Configuration;
  32 import java.lang.module.ModuleDescriptor;
  33 import java.lang.module.ModuleDescriptor.Exports;
  34 import java.lang.module.ModuleDescriptor.Opens;
  35 import java.lang.module.ModuleDescriptor.Provides;
  36 import java.lang.module.ModuleDescriptor.Requires;
  37 import java.lang.module.ModuleDescriptor.Version;
  38 import java.lang.module.ModuleFinder;
  39 import java.lang.module.ModuleReader;
  40 import java.lang.module.ModuleReference;
  41 import java.lang.module.ResolvedModule;
  42 import java.net.URI;
  43 import java.util.ArrayList;
  44 import java.util.Collection;
  45 import java.util.Collections;
  46 import java.util.EnumSet;
  47 import java.util.HashMap;
  48 import java.util.HashSet;
  49 import java.util.LinkedHashMap;
  50 import java.util.List;
  51 import java.util.Map;
  52 import java.util.Objects;
  53 import java.util.Optional;
  54 import java.util.Set;
  55 import java.util.TreeSet;
  56 import java.util.function.IntSupplier;
  57 import java.util.function.Supplier;
  58 import java.util.stream.Collectors;
  59 
  60 import jdk.internal.module.Checks;
  61 import jdk.internal.module.DefaultRoots;
  62 import jdk.internal.module.IllegalAccessMaps;
  63 import jdk.internal.module.ModuleHashes;
  64 import jdk.internal.module.ModuleInfo.Attributes;
  65 import jdk.internal.module.ModuleInfoExtender;
  66 import jdk.internal.module.ModuleReferenceImpl;
  67 import jdk.internal.module.ModuleResolution;
  68 import jdk.internal.module.ModuleTarget;
  69 
  70 import jdk.internal.org.objectweb.asm.ClassReader;
  71 import jdk.internal.org.objectweb.asm.ClassVisitor;
  72 import jdk.internal.org.objectweb.asm.ClassWriter;
  73 import jdk.internal.org.objectweb.asm.MethodVisitor;
  74 import jdk.internal.org.objectweb.asm.ModuleVisitor;
  75 import jdk.internal.org.objectweb.asm.Opcodes;
  76 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  77 
  78 import jdk.tools.jlink.internal.ModuleSorter;
  79 import jdk.tools.jlink.plugin.Plugin;
  80 import jdk.tools.jlink.plugin.PluginException;
  81 import jdk.tools.jlink.plugin.ResourcePool;
  82 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
  83 import jdk.tools.jlink.plugin.ResourcePoolEntry;
  84 
  85 /**
  86  * Jlink plugin to reconstitute module descriptors and other attributes for system
  87  * modules. The plugin generates implementations of SystemModules to avoid parsing
  88  * module-info.class files at startup. It also generates SystemModulesMap to return
  89  * the SystemModules implementation for a specific initial module.
  90  *
  91  * As a side effect, the plugin adds the ModulePackages class file attribute to the
  92  * module-info.class files that don't have the attribute.
  93  *
  94  * @see jdk.internal.module.SystemModuleFinders
  95  * @see jdk.internal.module.SystemModules
  96  */
  97 
  98 public final class SystemModulesPlugin implements Plugin {
  99     private static final String NAME = "system-modules";
 100     private static final String DESCRIPTION =
 101             PluginsResourceBundle.getDescription(NAME);
 102     private static final String SYSTEM_MODULES_MAP_CLASS =
 103             "jdk/internal/module/SystemModulesMap";
 104     private static final String SYSTEM_MODULES_CLASS_PREFIX =
 105             "jdk/internal/module/SystemModules$";
 106     private static final String ALL_SYSTEM_MODULES_CLASS =
 107             SYSTEM_MODULES_CLASS_PREFIX + "all";
 108     private static final String DEFAULT_SYSTEM_MODULES_CLASS =
 109             SYSTEM_MODULES_CLASS_PREFIX + "default";
 110 
 111     private boolean enabled;
 112 
 113     public SystemModulesPlugin() {
 114         this.enabled = true;
 115     }
 116 
 117     @Override
 118     public String getName() {
 119         return NAME;
 120     }
 121 
 122     @Override
 123     public String getDescription() {
 124         return DESCRIPTION;
 125     }
 126 
 127     @Override
 128     public Set<State> getState() {
 129         return enabled ? EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL)
 130                        : EnumSet.of(State.DISABLED);
 131     }
 132 
 133     @Override
 134     public boolean hasArguments() {
 135         return true;
 136     }
 137 
 138     @Override
 139     public String getArgumentsDescription() {
 140         return PluginsResourceBundle.getArgument(NAME);
 141     }
 142 
 143     @Override
 144     public void configure(Map<String, String> config) {
 145         String arg = config.get(NAME);
 146         if (arg != null) {
 147             throw new IllegalArgumentException(NAME + ": " + arg);
 148         }
 149     }
 150 
 151     @Override
 152     public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
 153         if (!enabled) {
 154             throw new PluginException(NAME + " was set");
 155         }
 156 
 157         // validate, transform (if needed), and add the module-info.class files
 158         List<ModuleInfo> moduleInfos = transformModuleInfos(in, out);
 159 
 160         // generate and add the SystemModuleMap and SystemModules classes
 161         Set<String> generated = genSystemModulesClasses(moduleInfos, out);
 162 
 163         // pass through all other resources
 164         in.entries()
 165             .filter(data -> !data.path().endsWith("/module-info.class")
 166                     && !generated.contains(data.path()))
 167             .forEach(data -> out.add(data));
 168 
 169         return out.build();
 170     }
 171 
 172     /**
 173      * Validates and transforms the module-info.class files in the modules, adding
 174      * the ModulePackages class file attribute if needed.
 175      *
 176      * @return the list of ModuleInfo objects, the first element is java.base
 177      */
 178     List<ModuleInfo> transformModuleInfos(ResourcePool in, ResourcePoolBuilder out) {
 179         List<ModuleInfo> moduleInfos = new ArrayList<>();
 180 
 181         // Sort modules in the topological order so that java.base is always first.
 182         new ModuleSorter(in.moduleView()).sorted().forEach(module -> {
 183             ResourcePoolEntry data = module.findEntry("module-info.class").orElseThrow(
 184                 // automatic modules not supported
 185                 () ->  new PluginException("module-info.class not found for " +
 186                         module.name() + " module")
 187             );
 188 
 189             assert module.name().equals(data.moduleName());
 190 
 191             try {
 192                 byte[] content = data.contentBytes();
 193                 Set<String> packages = module.packages();
 194                 ModuleInfo moduleInfo = new ModuleInfo(content, packages);
 195 
 196                 // link-time validation
 197                 moduleInfo.validateNames();
 198 
 199                 // check if any exported or open package is not present
 200                 moduleInfo.validatePackages();
 201 
 202                 // module-info.class may be overridden to add ModulePackages
 203                 if (moduleInfo.shouldRewrite()) {
 204                     data = data.copyWithContent(moduleInfo.getBytes());
 205                 }
 206                 moduleInfos.add(moduleInfo);
 207 
 208                 // add resource pool entry
 209                 out.add(data);
 210             } catch (IOException e) {
 211                 throw new PluginException(e);
 212             }
 213         });
 214 
 215         return moduleInfos;
 216     }
 217 
 218     /**
 219      * Generates the SystemModules classes (at least one) and the SystemModulesMap
 220      * class to map initial modules to a SystemModules class.
 221      *
 222      * @return the resource names of the resources added to the pool
 223      */
 224     private Set<String> genSystemModulesClasses(List<ModuleInfo> moduleInfos,
 225                                                 ResourcePoolBuilder out) {
 226         int moduleCount = moduleInfos.size();
 227         ModuleFinder finder = finderOf(moduleInfos);
 228         assert finder.findAll().size() == moduleCount;
 229 
 230         // map of initial module name to SystemModules class name
 231         Map<String, String> map = new LinkedHashMap<>();
 232 
 233         // the names of resources written to the pool
 234         Set<String> generated = new HashSet<>();
 235 
 236         // generate the SystemModules implementation to reconstitute all modules
 237         Set<String> allModuleNames = moduleInfos.stream()
 238                 .map(ModuleInfo::moduleName)
 239                 .collect(Collectors.toSet());
 240         String rn = genSystemModulesClass(moduleInfos,
 241                                           resolve(finder, allModuleNames),
 242                                           ALL_SYSTEM_MODULES_CLASS,
 243                                           out);
 244         generated.add(rn);
 245 
 246         // generate, if needed, a SystemModules class to reconstitute the modules
 247         // needed for the case that the initial module is the unnamed module.
 248         String defaultSystemModulesClassName;
 249         Configuration cf = resolve(finder, DefaultRoots.compute(finder));
 250         if (cf.modules().size() == moduleCount) {
 251             // all modules are resolved so no need to generate a class
 252             defaultSystemModulesClassName = ALL_SYSTEM_MODULES_CLASS;
 253         } else {
 254             defaultSystemModulesClassName = DEFAULT_SYSTEM_MODULES_CLASS;
 255             rn = genSystemModulesClass(sublist(moduleInfos, cf),
 256                                        cf,
 257                                        defaultSystemModulesClassName,
 258                                        out);
 259             generated.add(rn);
 260         }
 261 
 262         // Generate a SystemModules class for each module with a main class
 263         int suffix = 0;
 264         for (ModuleInfo mi : moduleInfos) {
 265             if (mi.descriptor().mainClass().isPresent()) {
 266                 String moduleName = mi.moduleName();
 267                 cf = resolve(finder, Set.of(moduleName));
 268                 if (cf.modules().size() == moduleCount) {
 269                     // resolves all modules so no need to generate a class
 270                     map.put(moduleName, ALL_SYSTEM_MODULES_CLASS);
 271                 } else {
 272                     String cn = SYSTEM_MODULES_CLASS_PREFIX + (suffix++);
 273                     rn = genSystemModulesClass(sublist(moduleInfos, cf), cf, cn, out);
 274                     map.put(moduleName, cn);
 275                     generated.add(rn);
 276                 }
 277             }
 278         }
 279 
 280         // generate SystemModulesMap
 281         rn = genSystemModulesMapClass(ALL_SYSTEM_MODULES_CLASS,
 282                                       defaultSystemModulesClassName,
 283                                       map,
 284                                       out);
 285         generated.add(rn);
 286 
 287         // return the resource names of the generated classes
 288         return generated;
 289     }
 290 
 291     /**
 292      * Resolves a collection of root modules, with service binding, to create
 293      * configuration.
 294      */
 295     private Configuration resolve(ModuleFinder finder, Set<String> roots) {
 296         return Configuration.empty().resolveAndBind(finder, ModuleFinder.of(), roots);
 297     }
 298 
 299     /**
 300      * Returns the list of ModuleInfo objects that correspond to the modules in
 301      * the given configuration.
 302      */
 303     private List<ModuleInfo> sublist(List<ModuleInfo> moduleInfos, Configuration cf) {
 304         Set<String> names = cf.modules()
 305                 .stream()
 306                 .map(ResolvedModule::name)
 307                 .collect(Collectors.toSet());
 308         return moduleInfos.stream()
 309                 .filter(mi -> names.contains(mi.moduleName()))
 310                 .collect(Collectors.toList());
 311     }
 312 
 313     /**
 314      * Generate a SystemModules implementation class and add it as a resource.
 315      *
 316      * @return the name of the class resource added to the pool
 317      */
 318     private String genSystemModulesClass(List<ModuleInfo> moduleInfos,
 319                                          Configuration cf,
 320                                          String className,
 321                                          ResourcePoolBuilder out) {
 322         SystemModulesClassGenerator generator
 323             = new SystemModulesClassGenerator(className, moduleInfos);
 324         byte[] bytes = generator.getClassWriter(cf).toByteArray();
 325         String rn = "/java.base/" + className + ".class";
 326         ResourcePoolEntry e = ResourcePoolEntry.create(rn, bytes);
 327         out.add(e);
 328         return rn;
 329     }
 330 
 331     static class ModuleInfo {
 332         private final ByteArrayInputStream bais;
 333         private final Attributes attrs;
 334         private final Set<String> packages;
 335         private final boolean addModulePackages;
 336         private ModuleDescriptor descriptor;  // may be different that the original one
 337 
 338         ModuleInfo(byte[] bytes, Set<String> packages) throws IOException {
 339             this.bais = new ByteArrayInputStream(bytes);
 340             this.packages = packages;
 341             this.attrs = jdk.internal.module.ModuleInfo.read(bais, null);
 342 
 343             // If ModulePackages attribute is present, the packages from this
 344             // module descriptor returns the packages in that attribute.
 345             // If it's not present, ModuleDescriptor::packages only contains
 346             // the exported and open packages from module-info.class
 347             this.descriptor = attrs.descriptor();
 348             if (descriptor.isAutomatic()) {
 349                 throw new InternalError("linking automatic module is not supported");
 350             }
 351 
 352             // add ModulePackages attribute if this module contains some packages
 353             // and ModulePackages is not present
 354             this.addModulePackages = packages.size() > 0 && !hasModulePackages();
 355         }
 356 
 357         String moduleName() {
 358             return attrs.descriptor().name();
 359         }
 360 
 361         ModuleDescriptor descriptor() {
 362             return descriptor;
 363         }
 364 
 365         Set<String> packages() {
 366             return packages;
 367         }
 368 
 369         ModuleTarget target() {
 370             return attrs.target();
 371         }
 372 
 373         ModuleHashes recordedHashes() {
 374             return attrs.recordedHashes();
 375         }
 376 
 377         ModuleResolution moduleResolution() {
 378             return attrs.moduleResolution();
 379         }
 380 
 381         /**
 382          * Validates names in ModuleDescriptor
 383          */
 384         void validateNames() {
 385             Checks.requireModuleName(descriptor.name());
 386             for (Requires req : descriptor.requires()) {
 387                 Checks.requireModuleName(req.name());
 388             }
 389             for (Exports e : descriptor.exports()) {
 390                 Checks.requirePackageName(e.source());
 391                 if (e.isQualified())
 392                     e.targets().forEach(Checks::requireModuleName);
 393             }
 394             for (Opens opens : descriptor.opens()) {
 395                 Checks.requirePackageName(opens.source());
 396                 if (opens.isQualified())
 397                     opens.targets().forEach(Checks::requireModuleName);
 398             }
 399             for (Provides provides : descriptor.provides()) {
 400                 Checks.requireServiceTypeName(provides.service());
 401                 provides.providers().forEach(Checks::requireServiceProviderName);
 402             }
 403             for (String service : descriptor.uses()) {
 404                 Checks.requireServiceTypeName(service);
 405             }
 406             for (String pn : descriptor.packages()) {
 407                 Checks.requirePackageName(pn);
 408             }
 409             for (String pn : packages) {
 410                 Checks.requirePackageName(pn);
 411             }
 412         }
 413 
 414         /**
 415          * Validates if exported and open packages are present
 416          */
 417         void validatePackages() {
 418             Set<String> nonExistPackages = new TreeSet<>();
 419             descriptor.exports().stream()
 420                 .map(Exports::source)
 421                 .filter(pn -> !packages.contains(pn))
 422                 .forEach(nonExistPackages::add);
 423 
 424             descriptor.opens().stream()
 425                 .map(Opens::source)
 426                 .filter(pn -> !packages.contains(pn))
 427                 .forEach(nonExistPackages::add);
 428 
 429             if (!nonExistPackages.isEmpty()) {
 430                 throw new PluginException("Packages that are exported or open in "
 431                     + descriptor.name() + " are not present: " + nonExistPackages);
 432             }
 433         }
 434 
 435         boolean hasModulePackages() throws IOException {
 436             Set<String> packages = new HashSet<>();
 437             ClassVisitor cv = new ClassVisitor(Opcodes.ASM7) {
 438                 @Override
 439                 public ModuleVisitor visitModule(String name,
 440                                                  int flags,
 441                                                  String version) {
 442                     return new ModuleVisitor(Opcodes.ASM7) {
 443                         public void visitPackage(String pn) {
 444                             packages.add(pn);
 445                         }
 446                     };
 447                 }
 448             };
 449 
 450             try (InputStream in = getInputStream()) {
 451                 // parse module-info.class
 452                 ClassReader cr = new ClassReader(in);
 453                 cr.accept(cv, 0);
 454                 return packages.size() > 0;
 455             }
 456         }
 457 
 458         /**
 459          * Returns true if module-info.class should be rewritten to add the
 460          * ModulePackages attribute.
 461          */
 462         boolean shouldRewrite() {
 463             return addModulePackages;
 464         }
 465 
 466         /**
 467          * Returns the bytes for the (possibly updated) module-info.class.
 468          */
 469         byte[] getBytes() throws IOException {
 470             try (InputStream in = getInputStream()) {
 471                 if (shouldRewrite()) {
 472                     ModuleInfoRewriter rewriter = new ModuleInfoRewriter(in);
 473                     if (addModulePackages) {
 474                         rewriter.addModulePackages(packages);
 475                     }
 476                     // rewritten module descriptor
 477                     byte[] bytes = rewriter.getBytes();
 478                     try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
 479                         this.descriptor = ModuleDescriptor.read(bais);
 480                     }
 481                     return bytes;
 482                 } else {
 483                     return in.readAllBytes();
 484                 }
 485             }
 486         }
 487 
 488         /*
 489          * Returns the input stream of the module-info.class
 490          */
 491         InputStream getInputStream() {
 492             bais.reset();
 493             return bais;
 494         }
 495 
 496         class ModuleInfoRewriter extends ByteArrayOutputStream {
 497             final ModuleInfoExtender extender;
 498             ModuleInfoRewriter(InputStream in) {
 499                 this.extender = ModuleInfoExtender.newExtender(in);
 500             }
 501 
 502             void addModulePackages(Set<String> packages) {
 503                 // Add ModulePackages attribute
 504                 if (packages.size() > 0) {
 505                     extender.packages(packages);
 506                 }
 507             }
 508 
 509             byte[] getBytes() throws IOException {
 510                 extender.write(this);
 511                 return buf;
 512             }
 513         }
 514     }
 515 
 516     /**
 517      * Generates a SystemModules class to reconstitute the ModuleDescriptor
 518      * and other attributes of system modules.
 519      */
 520     static class SystemModulesClassGenerator {
 521         private static final String MODULE_DESCRIPTOR_BUILDER =
 522             "jdk/internal/module/Builder";
 523         private static final String MODULE_DESCRIPTOR_ARRAY_SIGNATURE =
 524             "[Ljava/lang/module/ModuleDescriptor;";
 525         private static final String REQUIRES_MODIFIER_CLASSNAME =
 526             "java/lang/module/ModuleDescriptor$Requires$Modifier";
 527         private static final String EXPORTS_MODIFIER_CLASSNAME =
 528             "java/lang/module/ModuleDescriptor$Exports$Modifier";
 529         private static final String OPENS_MODIFIER_CLASSNAME =
 530             "java/lang/module/ModuleDescriptor$Opens$Modifier";
 531         private static final String MODULE_TARGET_CLASSNAME  =
 532             "jdk/internal/module/ModuleTarget";
 533         private static final String MODULE_TARGET_ARRAY_SIGNATURE  =
 534             "[Ljdk/internal/module/ModuleTarget;";
 535         private static final String MODULE_HASHES_ARRAY_SIGNATURE  =
 536             "[Ljdk/internal/module/ModuleHashes;";
 537         private static final String MODULE_RESOLUTION_CLASSNAME  =
 538             "jdk/internal/module/ModuleResolution";
 539         private static final String MODULE_RESOLUTIONS_ARRAY_SIGNATURE  =
 540             "[Ljdk/internal/module/ModuleResolution;";
 541 
 542         private static final int MAX_LOCAL_VARS = 256;
 543 
 544         private final int BUILDER_VAR    = 0;
 545         private final int MD_VAR         = 1;  // variable for ModuleDescriptor
 546         private final int MT_VAR         = 1;  // variable for ModuleTarget
 547         private final int MH_VAR         = 1;  // variable for ModuleHashes
 548         private int nextLocalVar         = 2;  // index to next local variable
 549 
 550         // Method visitor for generating the SystemModules::modules() method
 551         private MethodVisitor mv;
 552 
 553         // name of class to generate
 554         private final String className;
 555 
 556         // list of all ModuleDescriptorBuilders, invoked in turn when building.
 557         private final List<ModuleInfo> moduleInfos;
 558 
 559         // A builder to create one single Set instance for a given set of
 560         // names or modifiers to reduce the footprint
 561         // e.g. target modules of qualified exports
 562         private final DedupSetBuilder dedupSetBuilder
 563             = new DedupSetBuilder(this::getNextLocalVar);
 564 
 565         public SystemModulesClassGenerator(String className,
 566                                            List<ModuleInfo> moduleInfos) {
 567             this.className = className;
 568             this.moduleInfos = moduleInfos;
 569             moduleInfos.forEach(mi -> dedups(mi.descriptor()));
 570         }
 571 
 572         private int getNextLocalVar() {
 573             return nextLocalVar++;
 574         }
 575 
 576         /*
 577          * Adds the given ModuleDescriptor to the system module list.
 578          * It performs link-time validation and prepares mapping from various
 579          * Sets to SetBuilders to emit an optimized number of sets during build.
 580          */
 581         private void dedups(ModuleDescriptor md) {
 582             // exports
 583             for (Exports e : md.exports()) {
 584                 dedupSetBuilder.stringSet(e.targets());
 585                 dedupSetBuilder.exportsModifiers(e.modifiers());
 586             }
 587 
 588             // opens
 589             for (Opens opens : md.opens()) {
 590                 dedupSetBuilder.stringSet(opens.targets());
 591                 dedupSetBuilder.opensModifiers(opens.modifiers());
 592             }
 593 
 594             // requires
 595             for (Requires r : md.requires()) {
 596                 dedupSetBuilder.requiresModifiers(r.modifiers());
 597             }
 598 
 599             // uses
 600             dedupSetBuilder.stringSet(md.uses());
 601         }
 602 
 603         /**
 604          * Generate SystemModules class
 605          */
 606         public ClassWriter getClassWriter(Configuration cf) {
 607             ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
 608                                              + ClassWriter.COMPUTE_FRAMES);
 609             cw.visit(Opcodes.V1_8,
 610                      ACC_FINAL+ACC_SUPER,
 611                      className,
 612                      null,
 613                      "java/lang/Object",
 614                      new String[] { "jdk/internal/module/SystemModules" });
 615 
 616             // generate <init>
 617             genConstructor(cw);
 618 
 619             // generate hasSplitPackages
 620             genHasSplitPackages(cw);
 621 
 622             // generate hasIncubatorModules
 623             genIncubatorModules(cw);
 624 
 625             // generate moduleDescriptors
 626             genModuleDescriptorsMethod(cw);
 627 
 628             // generate moduleTargets
 629             genModuleTargetsMethod(cw);
 630 
 631             // generate moduleHashes
 632             genModuleHashesMethod(cw);
 633 
 634             // generate moduleResolutions
 635             genModuleResolutionsMethod(cw);
 636 
 637             // generate moduleReads
 638             genModuleReads(cw, cf);
 639 
 640             // generate concealedPackagesToOpen and exportedPackagesToOpen
 641             genXXXPackagesToOpenMethods(cw);
 642 
 643             return cw;
 644         }
 645 
 646         /**
 647          * Generate byteccode for no-arg constructor
 648          */
 649         private void genConstructor(ClassWriter cw) {
 650             MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
 651             mv.visitVarInsn(ALOAD, 0);
 652             mv.visitMethodInsn(INVOKESPECIAL,
 653                                "java/lang/Object",
 654                                "<init>",
 655                                "()V",
 656                                false);
 657             mv.visitInsn(RETURN);
 658             mv.visitMaxs(0, 0);
 659             mv.visitEnd();
 660         }
 661 
 662         /**
 663          * Generate bytecode for hasSplitPackages method
 664          */
 665         private void genHasSplitPackages(ClassWriter cw) {
 666             boolean distinct = moduleInfos.stream()
 667                     .map(ModuleInfo::packages)
 668                     .flatMap(Set::stream)
 669                     .allMatch(new HashSet<>()::add);
 670             boolean hasSplitPackages = !distinct;
 671 
 672             mv = cw.visitMethod(ACC_PUBLIC,
 673                                 "hasSplitPackages",
 674                                 "()Z",
 675                                 "()Z",
 676                                 null);
 677             mv.visitCode();
 678             if (hasSplitPackages) {
 679                 mv.visitInsn(ICONST_1);
 680             } else {
 681                 mv.visitInsn(ICONST_0);
 682             }
 683             mv.visitInsn(IRETURN);
 684             mv.visitMaxs(0, 0);
 685             mv.visitEnd();
 686         }
 687 
 688         /**
 689          * Generate bytecode for hasIncubatorModules method
 690          */
 691         private void genIncubatorModules(ClassWriter cw) {
 692             boolean hasIncubatorModules = moduleInfos.stream()
 693                     .map(ModuleInfo::moduleResolution)
 694                     .filter(mres -> (mres != null && mres.hasIncubatingWarning()))
 695                     .findFirst()
 696                     .isPresent();
 697 
 698             mv = cw.visitMethod(ACC_PUBLIC,
 699                                 "hasIncubatorModules",
 700                                 "()Z",
 701                                 "()Z",
 702                                 null);
 703             mv.visitCode();
 704             if (hasIncubatorModules) {
 705                 mv.visitInsn(ICONST_1);
 706             } else {
 707                 mv.visitInsn(ICONST_0);
 708             }
 709             mv.visitInsn(IRETURN);
 710             mv.visitMaxs(0, 0);
 711             mv.visitEnd();
 712         }
 713 
 714         /**
 715          * Generate bytecode for moduleDescriptors method
 716          */
 717         private void genModuleDescriptorsMethod(ClassWriter cw) {
 718             this.mv = cw.visitMethod(ACC_PUBLIC,
 719                                      "moduleDescriptors",
 720                                      "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
 721                                      "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
 722                                      null);
 723             mv.visitCode();
 724             pushInt(mv, moduleInfos.size());
 725             mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor");
 726             mv.visitVarInsn(ASTORE, MD_VAR);
 727 
 728             for (int index = 0; index < moduleInfos.size(); index++) {
 729                 ModuleInfo minfo = moduleInfos.get(index);
 730                 new ModuleDescriptorBuilder(minfo.descriptor(),
 731                                             minfo.packages(),
 732                                             index).build();
 733             }
 734             mv.visitVarInsn(ALOAD, MD_VAR);
 735             mv.visitInsn(ARETURN);
 736             mv.visitMaxs(0, 0);
 737             mv.visitEnd();
 738         }
 739 
 740         /**
 741          * Generate bytecode for moduleTargets method
 742          */
 743         private void genModuleTargetsMethod(ClassWriter cw) {
 744             MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
 745                                               "moduleTargets",
 746                                               "()" + MODULE_TARGET_ARRAY_SIGNATURE,
 747                                               "()" + MODULE_TARGET_ARRAY_SIGNATURE,
 748                                               null);
 749             mv.visitCode();
 750             pushInt(mv, moduleInfos.size());
 751             mv.visitTypeInsn(ANEWARRAY, MODULE_TARGET_CLASSNAME);
 752             mv.visitVarInsn(ASTORE, MT_VAR);
 753 
 754 
 755             // if java.base has a ModuleTarget attribute then generate the array
 756             // with one element, all other elements will be null.
 757 
 758             ModuleInfo base = moduleInfos.get(0);
 759             if (!base.moduleName().equals("java.base"))
 760                 throw new InternalError("java.base should be first module in list");
 761             ModuleTarget target = base.target();
 762 
 763             int count;
 764             if (target != null && target.targetPlatform() != null) {
 765                 count = 1;
 766             } else {
 767                 count = moduleInfos.size();
 768             }
 769 
 770             for (int index = 0; index < count; index++) {
 771                 ModuleInfo minfo = moduleInfos.get(index);
 772                 if (minfo.target() != null) {
 773                     mv.visitVarInsn(ALOAD, MT_VAR);
 774                     pushInt(mv, index);
 775 
 776                     // new ModuleTarget(String)
 777                     mv.visitTypeInsn(NEW, MODULE_TARGET_CLASSNAME);
 778                     mv.visitInsn(DUP);
 779                     mv.visitLdcInsn(minfo.target().targetPlatform());
 780                     mv.visitMethodInsn(INVOKESPECIAL, MODULE_TARGET_CLASSNAME,
 781                                        "<init>", "(Ljava/lang/String;)V", false);
 782 
 783                     mv.visitInsn(AASTORE);
 784                 }
 785             }
 786 
 787             mv.visitVarInsn(ALOAD, MT_VAR);
 788             mv.visitInsn(ARETURN);
 789             mv.visitMaxs(0, 0);
 790             mv.visitEnd();
 791         }
 792 
 793         /**
 794          * Generate bytecode for moduleHashes method
 795          */
 796         private void genModuleHashesMethod(ClassWriter cw) {
 797             MethodVisitor hmv =
 798                 cw.visitMethod(ACC_PUBLIC,
 799                                "moduleHashes",
 800                                "()" + MODULE_HASHES_ARRAY_SIGNATURE,
 801                                "()" + MODULE_HASHES_ARRAY_SIGNATURE,
 802                                null);
 803             hmv.visitCode();
 804             pushInt(hmv, moduleInfos.size());
 805             hmv.visitTypeInsn(ANEWARRAY, "jdk/internal/module/ModuleHashes");
 806             hmv.visitVarInsn(ASTORE, MH_VAR);
 807 
 808             for (int index = 0; index < moduleInfos.size(); index++) {
 809                 ModuleInfo minfo = moduleInfos.get(index);
 810                 if (minfo.recordedHashes() != null) {
 811                     new ModuleHashesBuilder(minfo.recordedHashes(),
 812                                             index,
 813                                             hmv).build();
 814                 }
 815             }
 816 
 817             hmv.visitVarInsn(ALOAD, MH_VAR);
 818             hmv.visitInsn(ARETURN);
 819             hmv.visitMaxs(0, 0);
 820             hmv.visitEnd();
 821         }
 822 
 823         /**
 824          * Generate bytecode for moduleResolutions method
 825          */
 826         private void genModuleResolutionsMethod(ClassWriter cw) {
 827             MethodVisitor mresmv =
 828                 cw.visitMethod(ACC_PUBLIC,
 829                                "moduleResolutions",
 830                                "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
 831                                "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
 832                                null);
 833             mresmv.visitCode();
 834             pushInt(mresmv, moduleInfos.size());
 835             mresmv.visitTypeInsn(ANEWARRAY, MODULE_RESOLUTION_CLASSNAME);
 836             mresmv.visitVarInsn(ASTORE, 0);
 837 
 838             for (int index=0; index < moduleInfos.size(); index++) {
 839                 ModuleInfo minfo = moduleInfos.get(index);
 840                 if (minfo.moduleResolution() != null) {
 841                     mresmv.visitVarInsn(ALOAD, 0);
 842                     pushInt(mresmv, index);
 843                     mresmv.visitTypeInsn(NEW, MODULE_RESOLUTION_CLASSNAME);
 844                     mresmv.visitInsn(DUP);
 845                     mresmv.visitLdcInsn(minfo.moduleResolution().value());
 846                     mresmv.visitMethodInsn(INVOKESPECIAL,
 847                                            MODULE_RESOLUTION_CLASSNAME,
 848                                            "<init>",
 849                                            "(I)V", false);
 850                     mresmv.visitInsn(AASTORE);
 851                 }
 852             }
 853             mresmv.visitVarInsn(ALOAD, 0);
 854             mresmv.visitInsn(ARETURN);
 855             mresmv.visitMaxs(0, 0);
 856             mresmv.visitEnd();
 857         }
 858 
 859         /**
 860          * Generate bytecode for moduleReads method
 861          */
 862         private void genModuleReads(ClassWriter cw, Configuration cf) {
 863             // module name -> names of modules that it reads
 864             Map<String, Set<String>> map = cf.modules().stream()
 865                     .collect(Collectors.toMap(
 866                             ResolvedModule::name,
 867                             m -> m.reads().stream()
 868                                     .map(ResolvedModule::name)
 869                                     .collect(Collectors.toSet())));
 870             generate(cw, "moduleReads", map, true);
 871         }
 872 
 873         /**
 874          * Generate concealedPackagesToOpen and exportedPackagesToOpen methods.
 875          */
 876         private void genXXXPackagesToOpenMethods(ClassWriter cw) {
 877             ModuleFinder finder = finderOf(moduleInfos);
 878             IllegalAccessMaps maps = IllegalAccessMaps.generate(finder);
 879             generate(cw, "concealedPackagesToOpen", maps.concealedPackagesToOpen(), false);
 880             generate(cw, "exportedPackagesToOpen", maps.exportedPackagesToOpen(), false);
 881         }
 882 
 883         /**
 884          * Generate method to return {@code Map<String, Set<String>>}.
 885          *
 886          * If {@code dedup} is true then the values are de-duplicated.
 887          */
 888         private void generate(ClassWriter cw,
 889                               String methodName,
 890                               Map<String, Set<String>> map,
 891                               boolean dedup) {
 892             MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
 893                                               methodName,
 894                                               "()Ljava/util/Map;",
 895                                               "()Ljava/util/Map;",
 896                                               null);
 897             mv.visitCode();
 898 
 899             // map of Set -> local
 900             Map<Set<String>, Integer> locals;
 901 
 902             // generate code to create the sets that are duplicated
 903             if (dedup) {
 904                 Collection<Set<String>> values = map.values();
 905                 Set<Set<String>> duplicateSets = values.stream()
 906                         .distinct()
 907                         .filter(s -> Collections.frequency(values, s) > 1)
 908                         .collect(Collectors.toSet());
 909                 locals = new HashMap<>();
 910                 int index = 1;
 911                 for (Set<String> s : duplicateSets) {
 912                     genImmutableSet(mv, s);
 913                     mv.visitVarInsn(ASTORE, index);
 914                     locals.put(s, index);
 915                     if (++index >= MAX_LOCAL_VARS) {
 916                         break;
 917                     }
 918                 }
 919             } else {
 920                 locals = Map.of();
 921             }
 922 
 923             // new Map$Entry[size]
 924             pushInt(mv, map.size());
 925             mv.visitTypeInsn(ANEWARRAY, "java/util/Map$Entry");
 926 
 927             int index = 0;
 928             for (Map.Entry<String, Set<String>> e : map.entrySet()) {
 929                 String name = e.getKey();
 930                 Set<String> s = e.getValue();
 931 
 932                 mv.visitInsn(DUP);
 933                 pushInt(mv, index);
 934                 mv.visitLdcInsn(name);
 935 
 936                 // if de-duplicated then load the local, otherwise generate code
 937                 Integer varIndex = locals.get(s);
 938                 if (varIndex == null) {
 939                     genImmutableSet(mv, s);
 940                 } else {
 941                     mv.visitVarInsn(ALOAD, varIndex);
 942                 }
 943 
 944                 String desc = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map$Entry;";
 945                 mv.visitMethodInsn(INVOKESTATIC,
 946                                    "java/util/Map",
 947                                    "entry",
 948                                    desc,
 949                                    true);
 950                 mv.visitInsn(AASTORE);
 951                 index++;
 952             }
 953 
 954             // invoke Map.ofEntries(Map$Entry[])
 955             mv.visitMethodInsn(INVOKESTATIC, "java/util/Map", "ofEntries",
 956                     "([Ljava/util/Map$Entry;)Ljava/util/Map;", true);
 957             mv.visitInsn(ARETURN);
 958             mv.visitMaxs(0, 0);
 959             mv.visitEnd();
 960         }
 961 
 962         /**
 963          * Generate code to generate an immutable set.
 964          */
 965         private void genImmutableSet(MethodVisitor mv, Set<String> set) {
 966             int size = set.size();
 967 
 968             // use Set.of(Object[]) when there are more than 2 elements
 969             // use Set.of(Object) or Set.of(Object, Object) when fewer
 970             if (size > 2) {
 971                 pushInt(mv, size);
 972                 mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
 973                 int i = 0;
 974                 for (String element : set) {
 975                     mv.visitInsn(DUP);
 976                     pushInt(mv, i);
 977                     mv.visitLdcInsn(element);
 978                     mv.visitInsn(AASTORE);
 979                     i++;
 980                 }
 981                 mv.visitMethodInsn(INVOKESTATIC,
 982                         "java/util/Set",
 983                         "of",
 984                         "([Ljava/lang/Object;)Ljava/util/Set;",
 985                         true);
 986             } else {
 987                 StringBuilder sb = new StringBuilder("(");
 988                 for (String element : set) {
 989                     mv.visitLdcInsn(element);
 990                     sb.append("Ljava/lang/Object;");
 991                 }
 992                 sb.append(")Ljava/util/Set;");
 993                 mv.visitMethodInsn(INVOKESTATIC,
 994                         "java/util/Set",
 995                         "of",
 996                         sb.toString(),
 997                         true);
 998             }
 999         }
1000 
1001         class ModuleDescriptorBuilder {
1002             static final String BUILDER_TYPE = "Ljdk/internal/module/Builder;";
1003             static final String EXPORTS_TYPE =
1004                 "Ljava/lang/module/ModuleDescriptor$Exports;";
1005             static final String OPENS_TYPE =
1006                 "Ljava/lang/module/ModuleDescriptor$Opens;";
1007             static final String PROVIDES_TYPE =
1008                 "Ljava/lang/module/ModuleDescriptor$Provides;";
1009             static final String REQUIRES_TYPE =
1010                 "Ljava/lang/module/ModuleDescriptor$Requires;";
1011 
1012             // method signature for static Builder::newExports, newOpens,
1013             // newProvides, newRequires methods
1014             static final String EXPORTS_MODIFIER_SET_STRING_SET_SIG =
1015                 "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)"
1016                     + EXPORTS_TYPE;
1017             static final String EXPORTS_MODIFIER_SET_STRING_SIG =
1018                 "(Ljava/util/Set;Ljava/lang/String;)" + EXPORTS_TYPE;
1019             static final String OPENS_MODIFIER_SET_STRING_SET_SIG =
1020                 "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)"
1021                     + OPENS_TYPE;
1022             static final String OPENS_MODIFIER_SET_STRING_SIG =
1023                 "(Ljava/util/Set;Ljava/lang/String;)" + OPENS_TYPE;
1024             static final String PROVIDES_STRING_LIST_SIG =
1025                 "(Ljava/lang/String;Ljava/util/List;)" + PROVIDES_TYPE;
1026             static final String REQUIRES_SET_STRING_SIG =
1027                 "(Ljava/util/Set;Ljava/lang/String;)" + REQUIRES_TYPE;
1028             static final String REQUIRES_SET_STRING_STRING_SIG =
1029                 "(Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;)" + REQUIRES_TYPE;
1030 
1031             // method signature for Builder instance methods that
1032             // return this Builder instance
1033             static final String EXPORTS_ARRAY_SIG =
1034                 "([" + EXPORTS_TYPE + ")" + BUILDER_TYPE;
1035             static final String OPENS_ARRAY_SIG =
1036                 "([" + OPENS_TYPE + ")" + BUILDER_TYPE;
1037             static final String PROVIDES_ARRAY_SIG =
1038                 "([" + PROVIDES_TYPE + ")" + BUILDER_TYPE;
1039             static final String REQUIRES_ARRAY_SIG =
1040                 "([" + REQUIRES_TYPE + ")" + BUILDER_TYPE;
1041             static final String SET_SIG = "(Ljava/util/Set;)" + BUILDER_TYPE;
1042             static final String STRING_SIG = "(Ljava/lang/String;)" + BUILDER_TYPE;
1043             static final String BOOLEAN_SIG = "(Z)" + BUILDER_TYPE;
1044 
1045             final ModuleDescriptor md;
1046             final Set<String> packages;
1047             final int index;
1048 
1049             ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages, int index) {
1050                 if (md.isAutomatic()) {
1051                     throw new InternalError("linking automatic module is not supported");
1052                 }
1053                 this.md = md;
1054                 this.packages = packages;
1055                 this.index = index;
1056             }
1057 
1058             void build() {
1059                 // new jdk.internal.module.Builder
1060                 newBuilder();
1061 
1062                 // requires
1063                 requires(md.requires());
1064 
1065                 // exports
1066                 exports(md.exports());
1067 
1068                 // opens
1069                 opens(md.opens());
1070 
1071                 // uses
1072                 uses(md.uses());
1073 
1074                 // provides
1075                 provides(md.provides());
1076 
1077                 // all packages
1078                 packages(packages);
1079 
1080                 // version
1081                 md.version().ifPresent(this::version);
1082 
1083                 // main class
1084                 md.mainClass().ifPresent(this::mainClass);
1085 
1086                 putModuleDescriptor();
1087             }
1088 
1089             void newBuilder() {
1090                 mv.visitTypeInsn(NEW, MODULE_DESCRIPTOR_BUILDER);
1091                 mv.visitInsn(DUP);
1092                 mv.visitLdcInsn(md.name());
1093                 mv.visitMethodInsn(INVOKESPECIAL, MODULE_DESCRIPTOR_BUILDER,
1094                     "<init>", "(Ljava/lang/String;)V", false);
1095                 mv.visitVarInsn(ASTORE, BUILDER_VAR);
1096                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1097 
1098                 if (md.isOpen()) {
1099                     setModuleBit("open", true);
1100                 }
1101                 if (md.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)) {
1102                     setModuleBit("synthetic", true);
1103                 }
1104                 if (md.modifiers().contains(ModuleDescriptor.Modifier.MANDATED)) {
1105                     setModuleBit("mandated", true);
1106                 }
1107             }
1108 
1109             /*
1110              * Invoke Builder.<methodName>(boolean value)
1111              */
1112             void setModuleBit(String methodName, boolean value) {
1113                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1114                 if (value) {
1115                     mv.visitInsn(ICONST_1);
1116                 } else {
1117                     mv.visitInsn(ICONST_0);
1118                 }
1119                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1120                     methodName, BOOLEAN_SIG, false);
1121                 mv.visitInsn(POP);
1122             }
1123 
1124             /*
1125              * Put ModuleDescriptor into the modules array
1126              */
1127             void putModuleDescriptor() {
1128                 mv.visitVarInsn(ALOAD, MD_VAR);
1129                 pushInt(mv, index);
1130                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1131                 mv.visitLdcInsn(md.hashCode());
1132                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1133                     "build", "(I)Ljava/lang/module/ModuleDescriptor;",
1134                     false);
1135                 mv.visitInsn(AASTORE);
1136             }
1137 
1138             /*
1139              * Call Builder::newRequires to create Requires instances and
1140              * then pass it to the builder by calling:
1141              *      Builder.requires(Requires[])
1142              *
1143              */
1144             void requires(Set<Requires> requires) {
1145                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1146                 pushInt(mv, requires.size());
1147                 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Requires");
1148                 int arrayIndex = 0;
1149                 for (Requires require : requires) {
1150                     String compiledVersion = null;
1151                     if (require.compiledVersion().isPresent()) {
1152                         compiledVersion = require.compiledVersion().get().toString();
1153                     }
1154 
1155                     mv.visitInsn(DUP);               // arrayref
1156                     pushInt(mv, arrayIndex++);
1157                     newRequires(require.modifiers(), require.name(), compiledVersion);
1158                     mv.visitInsn(AASTORE);
1159                 }
1160                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1161                     "requires", REQUIRES_ARRAY_SIG, false);
1162             }
1163 
1164             /*
1165              * Invoke Builder.newRequires(Set<Modifier> mods, String mn, String compiledVersion)
1166              *
1167              * Set<Modifier> mods = ...
1168              * Builder.newRequires(mods, mn, compiledVersion);
1169              */
1170             void newRequires(Set<Requires.Modifier> mods, String name, String compiledVersion) {
1171                 int varIndex = dedupSetBuilder.indexOfRequiresModifiers(mods);
1172                 mv.visitVarInsn(ALOAD, varIndex);
1173                 mv.visitLdcInsn(name);
1174                 if (compiledVersion != null) {
1175                     mv.visitLdcInsn(compiledVersion);
1176                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
1177                         "newRequires", REQUIRES_SET_STRING_STRING_SIG, false);
1178                 } else {
1179                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
1180                         "newRequires", REQUIRES_SET_STRING_SIG, false);
1181                 }
1182             }
1183 
1184             /*
1185              * Call Builder::newExports to create Exports instances and
1186              * then pass it to the builder by calling:
1187              *      Builder.exports(Exports[])
1188              *
1189              */
1190             void exports(Set<Exports> exports) {
1191                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1192                 pushInt(mv, exports.size());
1193                 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Exports");
1194                 int arrayIndex = 0;
1195                 for (Exports export : exports) {
1196                     mv.visitInsn(DUP);    // arrayref
1197                     pushInt(mv, arrayIndex++);
1198                     newExports(export.modifiers(), export.source(), export.targets());
1199                     mv.visitInsn(AASTORE);
1200                 }
1201                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1202                     "exports", EXPORTS_ARRAY_SIG, false);
1203             }
1204 
1205             /*
1206              * Invoke
1207              *     Builder.newExports(Set<Exports.Modifier> ms, String pn,
1208              *                        Set<String> targets)
1209              * or
1210              *     Builder.newExports(Set<Exports.Modifier> ms, String pn)
1211              *
1212              * Set<String> targets = new HashSet<>();
1213              * targets.add(t);
1214              * :
1215              * :
1216              *
1217              * Set<Modifier> mods = ...
1218              * Builder.newExports(mods, pn, targets);
1219              */
1220             void newExports(Set<Exports.Modifier> ms, String pn, Set<String> targets) {
1221                 int modifiersSetIndex = dedupSetBuilder.indexOfExportsModifiers(ms);
1222                 if (!targets.isEmpty()) {
1223                     int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets);
1224                     mv.visitVarInsn(ALOAD, modifiersSetIndex);
1225                     mv.visitLdcInsn(pn);
1226                     mv.visitVarInsn(ALOAD, stringSetIndex);
1227                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
1228                         "newExports", EXPORTS_MODIFIER_SET_STRING_SET_SIG, false);
1229                 } else {
1230                     mv.visitVarInsn(ALOAD, modifiersSetIndex);
1231                     mv.visitLdcInsn(pn);
1232                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
1233                         "newExports", EXPORTS_MODIFIER_SET_STRING_SIG, false);
1234                 }
1235             }
1236 
1237 
1238             /**
1239              * Call Builder::newOpens to create Opens instances and
1240              * then pass it to the builder by calling:
1241              * Builder.opens(Opens[])
1242              */
1243             void opens(Set<Opens> opens) {
1244                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1245                 pushInt(mv, opens.size());
1246                 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Opens");
1247                 int arrayIndex = 0;
1248                 for (Opens open : opens) {
1249                     mv.visitInsn(DUP);    // arrayref
1250                     pushInt(mv, arrayIndex++);
1251                     newOpens(open.modifiers(), open.source(), open.targets());
1252                     mv.visitInsn(AASTORE);
1253                 }
1254                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1255                     "opens", OPENS_ARRAY_SIG, false);
1256             }
1257 
1258             /*
1259              * Invoke
1260              *     Builder.newOpens(Set<Opens.Modifier> ms, String pn,
1261              *                        Set<String> targets)
1262              * or
1263              *     Builder.newOpens(Set<Opens.Modifier> ms, String pn)
1264              *
1265              * Set<String> targets = new HashSet<>();
1266              * targets.add(t);
1267              * :
1268              * :
1269              *
1270              * Set<Modifier> mods = ...
1271              * Builder.newOpens(mods, pn, targets);
1272              */
1273             void newOpens(Set<Opens.Modifier> ms, String pn, Set<String> targets) {
1274                 int modifiersSetIndex = dedupSetBuilder.indexOfOpensModifiers(ms);
1275                 if (!targets.isEmpty()) {
1276                     int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets);
1277                     mv.visitVarInsn(ALOAD, modifiersSetIndex);
1278                     mv.visitLdcInsn(pn);
1279                     mv.visitVarInsn(ALOAD, stringSetIndex);
1280                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
1281                         "newOpens", OPENS_MODIFIER_SET_STRING_SET_SIG, false);
1282                 } else {
1283                     mv.visitVarInsn(ALOAD, modifiersSetIndex);
1284                     mv.visitLdcInsn(pn);
1285                     mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
1286                         "newOpens", OPENS_MODIFIER_SET_STRING_SIG, false);
1287                 }
1288             }
1289 
1290             /*
1291              * Invoke Builder.uses(Set<String> uses)
1292              */
1293             void uses(Set<String> uses) {
1294                 int varIndex = dedupSetBuilder.indexOfStringSet(uses);
1295                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1296                 mv.visitVarInsn(ALOAD, varIndex);
1297                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1298                     "uses", SET_SIG, false);
1299                 mv.visitInsn(POP);
1300             }
1301 
1302             /*
1303             * Call Builder::newProvides to create Provides instances and
1304             * then pass it to the builder by calling:
1305             *      Builder.provides(Provides[] provides)
1306             *
1307             */
1308             void provides(Collection<Provides> provides) {
1309                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1310                 pushInt(mv, provides.size());
1311                 mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Provides");
1312                 int arrayIndex = 0;
1313                 for (Provides provide : provides) {
1314                     mv.visitInsn(DUP);    // arrayref
1315                     pushInt(mv, arrayIndex++);
1316                     newProvides(provide.service(), provide.providers());
1317                     mv.visitInsn(AASTORE);
1318                 }
1319                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1320                     "provides", PROVIDES_ARRAY_SIG, false);
1321             }
1322 
1323             /*
1324              * Invoke Builder.newProvides(String service, Set<String> providers)
1325              *
1326              * Set<String> providers = new HashSet<>();
1327              * providers.add(impl);
1328              * :
1329              * :
1330              * Builder.newProvides(service, providers);
1331              */
1332             void newProvides(String service, List<String> providers) {
1333                 mv.visitLdcInsn(service);
1334                 pushInt(mv, providers.size());
1335                 mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
1336                 int arrayIndex = 0;
1337                 for (String provider : providers) {
1338                     mv.visitInsn(DUP);    // arrayref
1339                     pushInt(mv, arrayIndex++);
1340                     mv.visitLdcInsn(provider);
1341                     mv.visitInsn(AASTORE);
1342                 }
1343                 mv.visitMethodInsn(INVOKESTATIC, "java/util/List",
1344                     "of", "([Ljava/lang/Object;)Ljava/util/List;", true);
1345                 mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
1346                     "newProvides", PROVIDES_STRING_LIST_SIG, false);
1347             }
1348 
1349             /*
1350              * Invoke Builder.packages(String pn)
1351              */
1352             void packages(Set<String> packages) {
1353                 int varIndex = dedupSetBuilder.newStringSet(packages);
1354                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1355                 mv.visitVarInsn(ALOAD, varIndex);
1356                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1357                     "packages", SET_SIG, false);
1358                 mv.visitInsn(POP);
1359             }
1360 
1361             /*
1362              * Invoke Builder.mainClass(String cn)
1363              */
1364             void mainClass(String cn) {
1365                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1366                 mv.visitLdcInsn(cn);
1367                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1368                     "mainClass", STRING_SIG, false);
1369                 mv.visitInsn(POP);
1370             }
1371 
1372             /*
1373              * Invoke Builder.version(Version v);
1374              */
1375             void version(Version v) {
1376                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1377                 mv.visitLdcInsn(v.toString());
1378                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1379                     "version", STRING_SIG, false);
1380                 mv.visitInsn(POP);
1381             }
1382 
1383             void invokeBuilderMethod(String methodName, String value) {
1384                 mv.visitVarInsn(ALOAD, BUILDER_VAR);
1385                 mv.visitLdcInsn(value);
1386                 mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
1387                     methodName, STRING_SIG, false);
1388                 mv.visitInsn(POP);
1389             }
1390         }
1391 
1392         class ModuleHashesBuilder {
1393             private static final String MODULE_HASHES_BUILDER =
1394                 "jdk/internal/module/ModuleHashes$Builder";
1395             private static final String MODULE_HASHES_BUILDER_TYPE =
1396                 "L" + MODULE_HASHES_BUILDER + ";";
1397             static final String STRING_BYTE_ARRAY_SIG =
1398                 "(Ljava/lang/String;[B)" + MODULE_HASHES_BUILDER_TYPE;
1399 
1400             final ModuleHashes recordedHashes;
1401             final MethodVisitor hmv;
1402             final int index;
1403 
1404             ModuleHashesBuilder(ModuleHashes hashes, int index, MethodVisitor hmv) {
1405                 this.recordedHashes = hashes;
1406                 this.hmv = hmv;
1407                 this.index = index;
1408             }
1409 
1410             /**
1411              * Build ModuleHashes
1412              */
1413             void build() {
1414                 if (recordedHashes == null)
1415                     return;
1416 
1417                 // new jdk.internal.module.ModuleHashes.Builder
1418                 newModuleHashesBuilder();
1419 
1420                 // Invoke ModuleHashes.Builder::hashForModule
1421                 recordedHashes
1422                     .names()
1423                     .forEach(mn -> hashForModule(mn, recordedHashes.hashFor(mn)));
1424 
1425                 // Put ModuleHashes into the hashes array
1426                 pushModuleHashes();
1427             }
1428 
1429 
1430             /*
1431              * Create ModuleHashes.Builder instance
1432              */
1433             void newModuleHashesBuilder() {
1434                 hmv.visitTypeInsn(NEW, MODULE_HASHES_BUILDER);
1435                 hmv.visitInsn(DUP);
1436                 hmv.visitLdcInsn(recordedHashes.algorithm());
1437                 pushInt(hmv, ((4 * recordedHashes.names().size()) / 3) + 1);
1438                 hmv.visitMethodInsn(INVOKESPECIAL, MODULE_HASHES_BUILDER,
1439                     "<init>", "(Ljava/lang/String;I)V", false);
1440                 hmv.visitVarInsn(ASTORE, BUILDER_VAR);
1441                 hmv.visitVarInsn(ALOAD, BUILDER_VAR);
1442             }
1443 
1444 
1445             /*
1446              * Invoke ModuleHashes.Builder::build and put the returned
1447              * ModuleHashes to the hashes array
1448              */
1449             void pushModuleHashes() {
1450                 hmv.visitVarInsn(ALOAD, MH_VAR);
1451                 pushInt(hmv, index);
1452                 hmv.visitVarInsn(ALOAD, BUILDER_VAR);
1453                 hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER,
1454                     "build", "()Ljdk/internal/module/ModuleHashes;",
1455                     false);
1456                 hmv.visitInsn(AASTORE);
1457             }
1458 
1459             /*
1460              * Invoke ModuleHashes.Builder.hashForModule(String name, byte[] hash);
1461              */
1462             void hashForModule(String name, byte[] hash) {
1463                 hmv.visitVarInsn(ALOAD, BUILDER_VAR);
1464                 hmv.visitLdcInsn(name);
1465 
1466                 pushInt(hmv, hash.length);
1467                 hmv.visitIntInsn(NEWARRAY, T_BYTE);
1468                 for (int i = 0; i < hash.length; i++) {
1469                     hmv.visitInsn(DUP);              // arrayref
1470                     pushInt(hmv, i);
1471                     hmv.visitIntInsn(BIPUSH, hash[i]);
1472                     hmv.visitInsn(BASTORE);
1473                 }
1474 
1475                 hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER,
1476                     "hashForModule", STRING_BYTE_ARRAY_SIG, false);
1477                 hmv.visitInsn(POP);
1478             }
1479         }
1480 
1481         /*
1482          * Wraps set creation, ensuring identical sets are properly deduplicated.
1483          */
1484         class DedupSetBuilder {
1485             // map Set<String> to a specialized builder to allow them to be
1486             // deduplicated as they are requested
1487             final Map<Set<String>, SetBuilder<String>> stringSets = new HashMap<>();
1488 
1489             // map Set<Requires.Modifier> to a specialized builder to allow them to be
1490             // deduplicated as they are requested
1491             final Map<Set<Requires.Modifier>, EnumSetBuilder<Requires.Modifier>>
1492                 requiresModifiersSets = new HashMap<>();
1493 
1494             // map Set<Exports.Modifier> to a specialized builder to allow them to be
1495             // deduplicated as they are requested
1496             final Map<Set<Exports.Modifier>, EnumSetBuilder<Exports.Modifier>>
1497                 exportsModifiersSets = new HashMap<>();
1498 
1499             // map Set<Opens.Modifier> to a specialized builder to allow them to be
1500             // deduplicated as they are requested
1501             final Map<Set<Opens.Modifier>, EnumSetBuilder<Opens.Modifier>>
1502                 opensModifiersSets = new HashMap<>();
1503 
1504             private final int stringSetVar;
1505             private final int enumSetVar;
1506             private final IntSupplier localVarSupplier;
1507 
1508             DedupSetBuilder(IntSupplier localVarSupplier) {
1509                 this.stringSetVar = localVarSupplier.getAsInt();
1510                 this.enumSetVar = localVarSupplier.getAsInt();
1511                 this.localVarSupplier = localVarSupplier;
1512             }
1513 
1514             /*
1515              * Add the given set of strings to this builder.
1516              */
1517             void stringSet(Set<String> strings) {
1518                 stringSets.computeIfAbsent(strings,
1519                     s -> new SetBuilder<>(s, stringSetVar, localVarSupplier)
1520                 ).increment();
1521             }
1522 
1523             /*
1524              * Add the given set of Exports.Modifiers
1525              */
1526             void exportsModifiers(Set<Exports.Modifier> mods) {
1527                 exportsModifiersSets.computeIfAbsent(mods, s ->
1528                                 new EnumSetBuilder<>(s, EXPORTS_MODIFIER_CLASSNAME,
1529                                         enumSetVar, localVarSupplier)
1530                 ).increment();
1531             }
1532 
1533             /*
1534              * Add the given set of Opens.Modifiers
1535              */
1536             void opensModifiers(Set<Opens.Modifier> mods) {
1537                 opensModifiersSets.computeIfAbsent(mods, s ->
1538                                 new EnumSetBuilder<>(s, OPENS_MODIFIER_CLASSNAME,
1539                                         enumSetVar, localVarSupplier)
1540                 ).increment();
1541             }
1542 
1543             /*
1544              * Add the given set of Requires.Modifiers
1545              */
1546             void requiresModifiers(Set<Requires.Modifier> mods) {
1547                 requiresModifiersSets.computeIfAbsent(mods, s ->
1548                     new EnumSetBuilder<>(s, REQUIRES_MODIFIER_CLASSNAME,
1549                                          enumSetVar, localVarSupplier)
1550                 ).increment();
1551             }
1552 
1553             /*
1554              * Retrieve the index to the given set of Strings. Emit code to
1555              * generate it when SetBuilder::build is called.
1556              */
1557             int indexOfStringSet(Set<String> names) {
1558                 return stringSets.get(names).build();
1559             }
1560 
1561             /*
1562              * Retrieve the index to the given set of Exports.Modifier.
1563              * Emit code to generate it when EnumSetBuilder::build is called.
1564              */
1565             int indexOfExportsModifiers(Set<Exports.Modifier> mods) {
1566                 return exportsModifiersSets.get(mods).build();
1567             }
1568 
1569             /**
1570              * Retrieve the index to the given set of Opens.Modifier.
1571              * Emit code to generate it when EnumSetBuilder::build is called.
1572              */
1573             int indexOfOpensModifiers(Set<Opens.Modifier> mods) {
1574                 return opensModifiersSets.get(mods).build();
1575             }
1576 
1577 
1578             /*
1579              * Retrieve the index to the given set of Requires.Modifier.
1580              * Emit code to generate it when EnumSetBuilder::build is called.
1581              */
1582             int indexOfRequiresModifiers(Set<Requires.Modifier> mods) {
1583                 return requiresModifiersSets.get(mods).build();
1584             }
1585 
1586             /*
1587              * Build a new string set without any attempt to deduplicate it.
1588              */
1589             int newStringSet(Set<String> names) {
1590                 int index = new SetBuilder<>(names, stringSetVar, localVarSupplier).build();
1591                 assert index == stringSetVar;
1592                 return index;
1593             }
1594         }
1595 
1596         /*
1597          * SetBuilder generates bytecode to create one single instance of Set
1598          * for a given set of elements and assign to a local variable slot.
1599          * When there is only one single reference to a Set<T>,
1600          * it will reuse defaultVarIndex.  For a Set with multiple references,
1601          * it will use a new local variable retrieved from the nextLocalVar
1602          */
1603         class SetBuilder<T> {
1604             private final Set<T> elements;
1605             private final int defaultVarIndex;
1606             private final IntSupplier nextLocalVar;
1607             private int refCount;
1608             private int localVarIndex;
1609 
1610             SetBuilder(Set<T> elements,
1611                        int defaultVarIndex,
1612                        IntSupplier nextLocalVar) {
1613                 this.elements = elements;
1614                 this.defaultVarIndex = defaultVarIndex;
1615                 this.nextLocalVar = nextLocalVar;
1616             }
1617 
1618             /*
1619              * Increments the number of references to this particular set.
1620              */
1621             final void increment() {
1622                 refCount++;
1623             }
1624 
1625             /**
1626              * Generate the appropriate instructions to load an object reference
1627              * to the element onto the stack.
1628              */
1629             void visitElement(T element, MethodVisitor mv) {
1630                 mv.visitLdcInsn(element);
1631             }
1632 
1633             /*
1634              * Build bytecode for the Set represented by this builder,
1635              * or get the local variable index of a previously generated set
1636              * (in the local scope).
1637              *
1638              * @return local variable index of the generated set.
1639              */
1640             final int build() {
1641                 int index = localVarIndex;
1642                 if (localVarIndex == 0) {
1643                     // if non-empty and more than one set reference this builder,
1644                     // emit to a unique local
1645                     index = refCount <= 1 ? defaultVarIndex
1646                                           : nextLocalVar.getAsInt();
1647                     if (index < MAX_LOCAL_VARS) {
1648                         localVarIndex = index;
1649                     } else {
1650                         // overflow: disable optimization by using localVarIndex = 0
1651                         index = defaultVarIndex;
1652                     }
1653 
1654                     generateSetOf(index);
1655                 }
1656                 return index;
1657             }
1658 
1659             private void generateSetOf(int index) {
1660                 if (elements.size() <= 10) {
1661                     // call Set.of(e1, e2, ...)
1662                     StringBuilder sb = new StringBuilder("(");
1663                     for (T t : elements) {
1664                         sb.append("Ljava/lang/Object;");
1665                         visitElement(t, mv);
1666                     }
1667                     sb.append(")Ljava/util/Set;");
1668                     mv.visitMethodInsn(INVOKESTATIC, "java/util/Set",
1669                             "of", sb.toString(), true);
1670                 } else {
1671                     // call Set.of(E... elements)
1672                     pushInt(mv, elements.size());
1673                     mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
1674                     int arrayIndex = 0;
1675                     for (T t : elements) {
1676                         mv.visitInsn(DUP);    // arrayref
1677                         pushInt(mv, arrayIndex);
1678                         visitElement(t, mv);  // value
1679                         mv.visitInsn(AASTORE);
1680                         arrayIndex++;
1681                     }
1682                     mv.visitMethodInsn(INVOKESTATIC, "java/util/Set",
1683                             "of", "([Ljava/lang/Object;)Ljava/util/Set;", true);
1684                 }
1685                 mv.visitVarInsn(ASTORE, index);
1686             }
1687         }
1688 
1689         /*
1690          * Generates bytecode to create one single instance of EnumSet
1691          * for a given set of modifiers and assign to a local variable slot.
1692          */
1693         class EnumSetBuilder<T> extends SetBuilder<T> {
1694 
1695             private final String className;
1696 
1697             EnumSetBuilder(Set<T> modifiers, String className,
1698                            int defaultVarIndex,
1699                            IntSupplier nextLocalVar) {
1700                 super(modifiers, defaultVarIndex, nextLocalVar);
1701                 this.className = className;
1702             }
1703 
1704             /**
1705              * Loads an Enum field.
1706              */
1707             void visitElement(T t, MethodVisitor mv) {
1708                 mv.visitFieldInsn(GETSTATIC, className, t.toString(),
1709                                   "L" + className + ";");
1710             }
1711         }
1712     }
1713 
1714     /**
1715      * Generate SystemModulesMap and add it as a resource.
1716      *
1717      * @return the name of the class resource added to the pool
1718      */
1719     private String genSystemModulesMapClass(String allSystemModulesClassName,
1720                                             String defaultSystemModulesClassName,
1721                                             Map<String, String> map,
1722                                             ResourcePoolBuilder out) {
1723         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
1724                                          + ClassWriter.COMPUTE_FRAMES);
1725         cw.visit(Opcodes.V1_8,
1726                  ACC_FINAL+ACC_SUPER,
1727                  SYSTEM_MODULES_MAP_CLASS,
1728                  null,
1729                  "java/lang/Object",
1730                  null);
1731 
1732         // <init>
1733         MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null);
1734         mv.visitVarInsn(ALOAD, 0);
1735         mv.visitMethodInsn(INVOKESPECIAL,
1736                            "java/lang/Object",
1737                            "<init>",
1738                            "()V",
1739                            false);
1740         mv.visitInsn(RETURN);
1741         mv.visitMaxs(0, 0);
1742         mv.visitEnd();
1743 
1744         // allSystemModules()
1745         mv = cw.visitMethod(ACC_STATIC,
1746                             "allSystemModules",
1747                             "()Ljdk/internal/module/SystemModules;",
1748                             "()Ljdk/internal/module/SystemModules;",
1749                             null);
1750         mv.visitCode();
1751         mv.visitTypeInsn(NEW, allSystemModulesClassName);
1752         mv.visitInsn(DUP);
1753         mv.visitMethodInsn(INVOKESPECIAL,
1754                            allSystemModulesClassName,
1755                            "<init>",
1756                            "()V",
1757                            false);
1758         mv.visitInsn(ARETURN);
1759         mv.visitMaxs(0, 0);
1760         mv.visitEnd();
1761 
1762         // defaultSystemModules()
1763         mv = cw.visitMethod(ACC_STATIC,
1764                             "defaultSystemModules",
1765                             "()Ljdk/internal/module/SystemModules;",
1766                             "()Ljdk/internal/module/SystemModules;",
1767                             null);
1768         mv.visitCode();
1769         mv.visitTypeInsn(NEW, defaultSystemModulesClassName);
1770         mv.visitInsn(DUP);
1771         mv.visitMethodInsn(INVOKESPECIAL,
1772                            defaultSystemModulesClassName,
1773                            "<init>",
1774                            "()V",
1775                            false);
1776         mv.visitInsn(ARETURN);
1777         mv.visitMaxs(0, 0);
1778         mv.visitEnd();
1779 
1780         // moduleNames()
1781         mv = cw.visitMethod(ACC_STATIC,
1782                             "moduleNames",
1783                             "()[Ljava/lang/String;",
1784                             "()[Ljava/lang/String;",
1785                             null);
1786         mv.visitCode();
1787         pushInt(mv, map.size());
1788         mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
1789 
1790         int index = 0;
1791         for (String moduleName : map.keySet()) {
1792             mv.visitInsn(DUP);                  // arrayref
1793             pushInt(mv, index);
1794             mv.visitLdcInsn(moduleName);
1795             mv.visitInsn(AASTORE);
1796             index++;
1797         }
1798 
1799         mv.visitInsn(ARETURN);
1800         mv.visitMaxs(0, 0);
1801         mv.visitEnd();
1802 
1803         // classNames()
1804         mv = cw.visitMethod(ACC_STATIC,
1805                             "classNames",
1806                             "()[Ljava/lang/String;",
1807                             "()[Ljava/lang/String;",
1808                             null);
1809         mv.visitCode();
1810         pushInt(mv, map.size());
1811         mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
1812 
1813         index = 0;
1814         for (String className : map.values()) {
1815             mv.visitInsn(DUP);                  // arrayref
1816             pushInt(mv, index);
1817             mv.visitLdcInsn(className.replace('/', '.'));
1818             mv.visitInsn(AASTORE);
1819             index++;
1820         }
1821 
1822         mv.visitInsn(ARETURN);
1823         mv.visitMaxs(0, 0);
1824         mv.visitEnd();
1825 
1826         // write the class file to the pool as a resource
1827         String rn = "/java.base/" + SYSTEM_MODULES_MAP_CLASS + ".class";
1828         ResourcePoolEntry e = ResourcePoolEntry.create(rn, cw.toByteArray());
1829         out.add(e);
1830 
1831         return rn;
1832     }
1833 
1834     /**
1835      * Pushes an int constant
1836      */
1837     private static void pushInt(MethodVisitor mv, int value) {
1838         if (value <= 5) {
1839             mv.visitInsn(ICONST_0 + value);
1840         } else if (value < Byte.MAX_VALUE) {
1841             mv.visitIntInsn(BIPUSH, value);
1842         } else if (value < Short.MAX_VALUE) {
1843             mv.visitIntInsn(SIPUSH, value);
1844         } else {
1845             throw new IllegalArgumentException("exceed limit: " + value);
1846         }
1847     }
1848 
1849     /**
1850      * Returns a module finder that finds all modules in the given list
1851      */
1852     private static ModuleFinder finderOf(Collection<ModuleInfo> moduleInfos) {
1853         Supplier<ModuleReader> readerSupplier = () -> null;
1854         Map<String, ModuleReference> namesToReference = new HashMap<>();
1855         for (ModuleInfo mi : moduleInfos) {
1856             String name = mi.moduleName();
1857             ModuleReference mref
1858                 = new ModuleReferenceImpl(mi.descriptor(),
1859                                           URI.create("jrt:/" + name),
1860                                           readerSupplier,
1861                                           null,
1862                                           mi.target(),
1863                                           null,
1864                                           null,
1865                                           mi.moduleResolution());
1866             namesToReference.put(name, mref);
1867         }
1868 
1869         return new ModuleFinder() {
1870             @Override
1871             public Optional<ModuleReference> find(String name) {
1872                 Objects.requireNonNull(name);
1873                 return Optional.ofNullable(namesToReference.get(name));
1874             }
1875             @Override
1876             public Set<ModuleReference> findAll() {
1877                 return new HashSet<>(namesToReference.values());
1878             }
1879         };
1880     }
1881 }