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