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