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