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