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