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