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