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