JDK 11 jdk.jlink.jmod - JLink Tool

JDK 11 jdk.jlink.jmod is the JMOD file for JDK 11 JLink tool, which can be invoked by the "jlink" command.

JDK 11 JLink tool compiled class files are stored in \fyicenter\jdk-11.0.1\jmods\jdk.jlink.jmod.

JDK 11 JLink tool compiled class files are also linked and stored in the \fyicenter\jdk-11.0.1\lib\modules JImage file.

JDK 11 JLink tool source code files are stored in \fyicenter\jdk-11.0.1\lib\src.zip\jdk.jlink.

You can click and view the content of each source code file in the list below.

✍: FYIcenter

jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java

/*
 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package jdk.tools.jlink.internal.plugins;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Version;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import jdk.internal.module.Checks;
import jdk.internal.module.DefaultRoots;
import jdk.internal.module.IllegalAccessMaps;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleInfo.Attributes;
import jdk.internal.module.ModuleInfoExtender;
import jdk.internal.module.ModuleReferenceImpl;
import jdk.internal.module.ModuleResolution;
import jdk.internal.module.ModuleTarget;

import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.ModuleVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import static jdk.internal.org.objectweb.asm.Opcodes.*;

import jdk.tools.jlink.internal.ModuleSorter;
import jdk.tools.jlink.plugin.Plugin;
import jdk.tools.jlink.plugin.PluginException;
import jdk.tools.jlink.plugin.ResourcePool;
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
import jdk.tools.jlink.plugin.ResourcePoolEntry;

/**
 * Jlink plugin to reconstitute module descriptors and other attributes for system
 * modules. The plugin generates implementations of SystemModules to avoid parsing
 * module-info.class files at startup. It also generates SystemModulesMap to return
 * the SystemModules implementation for a specific initial module.
 *
 * As a side effect, the plugin adds the ModulePackages class file attribute to the
 * module-info.class files that don't have the attribute.
 *
 * @see jdk.internal.module.SystemModuleFinders
 * @see jdk.internal.module.SystemModules
 */

public final class SystemModulesPlugin implements Plugin {
    private static final String NAME = "system-modules";
    private static final String DESCRIPTION =
            PluginsResourceBundle.getDescription(NAME);
    private static final String SYSTEM_MODULES_MAP_CLASS =
            "jdk/internal/module/SystemModulesMap";
    private static final String SYSTEM_MODULES_CLASS_PREFIX =
            "jdk/internal/module/SystemModules$";
    private static final String ALL_SYSTEM_MODULES_CLASS =
            SYSTEM_MODULES_CLASS_PREFIX + "all";
    private static final String DEFAULT_SYSTEM_MODULES_CLASS =
            SYSTEM_MODULES_CLASS_PREFIX + "default";

    private boolean enabled;

    public SystemModulesPlugin() {
        this.enabled = true;
    }

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public String getDescription() {
        return DESCRIPTION;
    }

    @Override
    public Set<State> getState() {
        return enabled ? EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL)
                       : EnumSet.of(State.DISABLED);
    }

    @Override
    public boolean hasArguments() {
        return true;
    }

    @Override
    public String getArgumentsDescription() {
        return PluginsResourceBundle.getArgument(NAME);
    }

    @Override
    public void configure(Map<String, String> config) {
        String arg = config.get(NAME);
        if (arg != null) {
            throw new IllegalArgumentException(NAME + ": " + arg);
        }
    }

    @Override
    public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
        if (!enabled) {
            throw new PluginException(NAME + " was set");
        }

        // validate, transform (if needed), and add the module-info.class files
        List<ModuleInfo> moduleInfos = transformModuleInfos(in, out);

        // generate and add the SystemModuleMap and SystemModules classes
        Set<String> generated = genSystemModulesClasses(moduleInfos, out);

        // pass through all other resources
        in.entries()
            .filter(data -> !data.path().endsWith("/module-info.class")
                    && !generated.contains(data.path()))
            .forEach(data -> out.add(data));

        return out.build();
    }

    /**
     * Validates and transforms the module-info.class files in the modules, adding
     * the ModulePackages class file attribute if needed.
     *
     * @return the list of ModuleInfo objects, the first element is java.base
     */
    List<ModuleInfo> transformModuleInfos(ResourcePool in, ResourcePoolBuilder out) {
        List<ModuleInfo> moduleInfos = new ArrayList<>();

        // Sort modules in the topological order so that java.base is always first.
        new ModuleSorter(in.moduleView()).sorted().forEach(module -> {
            ResourcePoolEntry data = module.findEntry("module-info.class").orElseThrow(
                // automatic modules not supported
                () ->  new PluginException("module-info.class not found for " +
                        module.name() + " module")
            );

            assert module.name().equals(data.moduleName());

            try {
                byte[] content = data.contentBytes();
                Set<String> packages = module.packages();
                ModuleInfo moduleInfo = new ModuleInfo(content, packages);

                // link-time validation
                moduleInfo.validateNames();

                // check if any exported or open package is not present
                moduleInfo.validatePackages();

                // module-info.class may be overridden to add ModulePackages
                if (moduleInfo.shouldRewrite()) {
                    data = data.copyWithContent(moduleInfo.getBytes());
                }
                moduleInfos.add(moduleInfo);

                // add resource pool entry
                out.add(data);
            } catch (IOException e) {
                throw new PluginException(e);
            }
        });

        return moduleInfos;
    }

    /**
     * Generates the SystemModules classes (at least one) and the SystemModulesMap
     * class to map initial modules to a SystemModules class.
     *
     * @return the resource names of the resources added to the pool
     */
    private Set<String> genSystemModulesClasses(List<ModuleInfo> moduleInfos,
                                                ResourcePoolBuilder out) {
        int moduleCount = moduleInfos.size();
        ModuleFinder finder = finderOf(moduleInfos);
        assert finder.findAll().size() == moduleCount;

        // map of initial module name to SystemModules class name
        Map<String, String> map = new LinkedHashMap<>();

        // the names of resources written to the pool
        Set<String> generated = new HashSet<>();

        // generate the SystemModules implementation to reconstitute all modules
        Set<String> allModuleNames = moduleInfos.stream()
                .map(ModuleInfo::moduleName)
                .collect(Collectors.toSet());
        String rn = genSystemModulesClass(moduleInfos,
                                          resolve(finder, allModuleNames),
                                          ALL_SYSTEM_MODULES_CLASS,
                                          out);
        generated.add(rn);

        // generate, if needed, a SystemModules class to reconstitute the modules
        // needed for the case that the initial module is the unnamed module.
        String defaultSystemModulesClassName;
        Configuration cf = resolve(finder, DefaultRoots.compute(finder));
        if (cf.modules().size() == moduleCount) {
            // all modules are resolved so no need to generate a class
            defaultSystemModulesClassName = ALL_SYSTEM_MODULES_CLASS;
        } else {
            defaultSystemModulesClassName = DEFAULT_SYSTEM_MODULES_CLASS;
            rn = genSystemModulesClass(sublist(moduleInfos, cf),
                                       cf,
                                       defaultSystemModulesClassName,
                                       out);
            generated.add(rn);
        }

        // Generate a SystemModules class for each module with a main class
        int suffix = 0;
        for (ModuleInfo mi : moduleInfos) {
            if (mi.descriptor().mainClass().isPresent()) {
                String moduleName = mi.moduleName();
                cf = resolve(finder, Set.of(moduleName));
                if (cf.modules().size() == moduleCount) {
                    // resolves all modules so no need to generate a class
                    map.put(moduleName, ALL_SYSTEM_MODULES_CLASS);
                } else {
                    String cn = SYSTEM_MODULES_CLASS_PREFIX + (suffix++);
                    rn = genSystemModulesClass(sublist(moduleInfos, cf), cf, cn, out);
                    map.put(moduleName, cn);
                    generated.add(rn);
                }
            }
        }

        // generate SystemModulesMap
        rn = genSystemModulesMapClass(ALL_SYSTEM_MODULES_CLASS,
                                      defaultSystemModulesClassName,
                                      map,
                                      out);
        generated.add(rn);

        // return the resource names of the generated classes
        return generated;
    }

    /**
     * Resolves a collection of root modules, with service binding, to create
     * configuration.
     */
    private Configuration resolve(ModuleFinder finder, Set<String> roots) {
        return Configuration.empty().resolveAndBind(finder, ModuleFinder.of(), roots);
    }

    /**
     * Returns the list of ModuleInfo objects that correspond to the modules in
     * the given configuration.
     */
    private List<ModuleInfo> sublist(List<ModuleInfo> moduleInfos, Configuration cf) {
        Set<String> names = cf.modules()
                .stream()
                .map(ResolvedModule::name)
                .collect(Collectors.toSet());
        return moduleInfos.stream()
                .filter(mi -> names.contains(mi.moduleName()))
                .collect(Collectors.toList());
    }

    /**
     * Generate a SystemModules implementation class and add it as a resource.
     *
     * @return the name of the class resource added to the pool
     */
    private String genSystemModulesClass(List<ModuleInfo> moduleInfos,
                                         Configuration cf,
                                         String className,
                                         ResourcePoolBuilder out) {
        SystemModulesClassGenerator generator
            = new SystemModulesClassGenerator(className, moduleInfos);
        byte[] bytes = generator.getClassWriter(cf).toByteArray();
        String rn = "/java.base/" + className + ".class";
        ResourcePoolEntry e = ResourcePoolEntry.create(rn, bytes);
        out.add(e);
        return rn;
    }

    static class ModuleInfo {
        private final ByteArrayInputStream bais;
        private final Attributes attrs;
        private final Set<String> packages;
        private final boolean addModulePackages;
        private ModuleDescriptor descriptor;  // may be different that the original one

        ModuleInfo(byte[] bytes, Set<String> packages) throws IOException {
            this.bais = new ByteArrayInputStream(bytes);
            this.packages = packages;
            this.attrs = jdk.internal.module.ModuleInfo.read(bais, null);

            // If ModulePackages attribute is present, the packages from this
            // module descriptor returns the packages in that attribute.
            // If it's not present, ModuleDescriptor::packages only contains
            // the exported and open packages from module-info.class
            this.descriptor = attrs.descriptor();
            if (descriptor.isAutomatic()) {
                throw new InternalError("linking automatic module is not supported");
            }

            // add ModulePackages attribute if this module contains some packages
            // and ModulePackages is not present
            this.addModulePackages = packages.size() > 0 && !hasModulePackages();
        }

        String moduleName() {
            return attrs.descriptor().name();
        }

        ModuleDescriptor descriptor() {
            return descriptor;
        }

        Set<String> packages() {
            return packages;
        }

        ModuleTarget target() {
            return attrs.target();
        }

        ModuleHashes recordedHashes() {
            return attrs.recordedHashes();
        }

        ModuleResolution moduleResolution() {
            return attrs.moduleResolution();
        }

        /**
         * Validates names in ModuleDescriptor
         */
        void validateNames() {
            Checks.requireModuleName(descriptor.name());
            for (Requires req : descriptor.requires()) {
                Checks.requireModuleName(req.name());
            }
            for (Exports e : descriptor.exports()) {
                Checks.requirePackageName(e.source());
                if (e.isQualified())
                    e.targets().forEach(Checks::requireModuleName);
            }
            for (Opens opens : descriptor.opens()) {
                Checks.requirePackageName(opens.source());
                if (opens.isQualified())
                    opens.targets().forEach(Checks::requireModuleName);
            }
            for (Provides provides : descriptor.provides()) {
                Checks.requireServiceTypeName(provides.service());
                provides.providers().forEach(Checks::requireServiceProviderName);
            }
            for (String service : descriptor.uses()) {
                Checks.requireServiceTypeName(service);
            }
            for (String pn : descriptor.packages()) {
                Checks.requirePackageName(pn);
            }
            for (String pn : packages) {
                Checks.requirePackageName(pn);
            }
        }

        /**
         * Validates if exported and open packages are present
         */
        void validatePackages() {
            Set<String> nonExistPackages = new TreeSet<>();
            descriptor.exports().stream()
                .map(Exports::source)
                .filter(pn -> !packages.contains(pn))
                .forEach(nonExistPackages::add);

            descriptor.opens().stream()
                .map(Opens::source)
                .filter(pn -> !packages.contains(pn))
                .forEach(nonExistPackages::add);

            if (!nonExistPackages.isEmpty()) {
                throw new PluginException("Packages that are exported or open in "
                    + descriptor.name() + " are not present: " + nonExistPackages);
            }
        }

        boolean hasModulePackages() throws IOException {
            Set<String> packages = new HashSet<>();
            ClassVisitor cv = new ClassVisitor(Opcodes.ASM6) {
                @Override
                public ModuleVisitor visitModule(String name,
                                                 int flags,
                                                 String version) {
                    return new ModuleVisitor(Opcodes.ASM6) {
                        public void visitPackage(String pn) {
                            packages.add(pn);
                        }
                    };
                }
            };

            try (InputStream in = getInputStream()) {
                // parse module-info.class
                ClassReader cr = new ClassReader(in);
                cr.accept(cv, 0);
                return packages.size() > 0;
            }
        }

        /**
         * Returns true if module-info.class should be rewritten to add the
         * ModulePackages attribute.
         */
        boolean shouldRewrite() {
            return addModulePackages;
        }

        /**
         * Returns the bytes for the (possibly updated) module-info.class.
         */
        byte[] getBytes() throws IOException {
            try (InputStream in = getInputStream()) {
                if (shouldRewrite()) {
                    ModuleInfoRewriter rewriter = new ModuleInfoRewriter(in);
                    if (addModulePackages) {
                        rewriter.addModulePackages(packages);
                    }
                    // rewritten module descriptor
                    byte[] bytes = rewriter.getBytes();
                    try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
                        this.descriptor = ModuleDescriptor.read(bais);
                    }
                    return bytes;
                } else {
                    return in.readAllBytes();
                }
            }
        }

        /*
         * Returns the input stream of the module-info.class
         */
        InputStream getInputStream() {
            bais.reset();
            return bais;
        }

        class ModuleInfoRewriter extends ByteArrayOutputStream {
            final ModuleInfoExtender extender;
            ModuleInfoRewriter(InputStream in) {
                this.extender = ModuleInfoExtender.newExtender(in);
            }

            void addModulePackages(Set<String> packages) {
                // Add ModulePackages attribute
                if (packages.size() > 0) {
                    extender.packages(packages);
                }
            }

            byte[] getBytes() throws IOException {
                extender.write(this);
                return buf;
            }
        }
    }

    /**
     * Generates a SystemModules class to reconstitute the ModuleDescriptor
     * and other attributes of system modules.
     */
    static class SystemModulesClassGenerator {
        private static final String MODULE_DESCRIPTOR_BUILDER =
            "jdk/internal/module/Builder";
        private static final String MODULE_DESCRIPTOR_ARRAY_SIGNATURE =
            "[Ljava/lang/module/ModuleDescriptor;";
        private static final String REQUIRES_MODIFIER_CLASSNAME =
            "java/lang/module/ModuleDescriptor$Requires$Modifier";
        private static final String EXPORTS_MODIFIER_CLASSNAME =
            "java/lang/module/ModuleDescriptor$Exports$Modifier";
        private static final String OPENS_MODIFIER_CLASSNAME =
            "java/lang/module/ModuleDescriptor$Opens$Modifier";
        private static final String MODULE_TARGET_CLASSNAME  =
            "jdk/internal/module/ModuleTarget";
        private static final String MODULE_TARGET_ARRAY_SIGNATURE  =
            "[Ljdk/internal/module/ModuleTarget;";
        private static final String MODULE_HASHES_ARRAY_SIGNATURE  =
            "[Ljdk/internal/module/ModuleHashes;";
        private static final String MODULE_RESOLUTION_CLASSNAME  =
            "jdk/internal/module/ModuleResolution";
        private static final String MODULE_RESOLUTIONS_ARRAY_SIGNATURE  =
            "[Ljdk/internal/module/ModuleResolution;";

        private static final int MAX_LOCAL_VARS = 256;

        private final int BUILDER_VAR    = 0;
        private final int MD_VAR         = 1;  // variable for ModuleDescriptor
        private final int MT_VAR         = 1;  // variable for ModuleTarget
        private final int MH_VAR         = 1;  // variable for ModuleHashes
        private int nextLocalVar         = 2;  // index to next local variable

        // Method visitor for generating the SystemModules::modules() method
        private MethodVisitor mv;

        // name of class to generate
        private final String className;

        // list of all ModuleDescriptorBuilders, invoked in turn when building.
        private final List<ModuleInfo> moduleInfos;

        // A builder to create one single Set instance for a given set of
        // names or modifiers to reduce the footprint
        // e.g. target modules of qualified exports
        private final DedupSetBuilder dedupSetBuilder
            = new DedupSetBuilder(this::getNextLocalVar);

        public SystemModulesClassGenerator(String className,
                                           List<ModuleInfo> moduleInfos) {
            this.className = className;
            this.moduleInfos = moduleInfos;
            moduleInfos.forEach(mi -> dedups(mi.descriptor()));
        }

        private int getNextLocalVar() {
            return nextLocalVar++;
        }

        /*
         * Adds the given ModuleDescriptor to the system module list.
         * It performs link-time validation and prepares mapping from various
         * Sets to SetBuilders to emit an optimized number of sets during build.
         */
        private void dedups(ModuleDescriptor md) {
            // exports
            for (Exports e : md.exports()) {
                dedupSetBuilder.stringSet(e.targets());
                dedupSetBuilder.exportsModifiers(e.modifiers());
            }

            // opens
            for (Opens opens : md.opens()) {
                dedupSetBuilder.stringSet(opens.targets());
                dedupSetBuilder.opensModifiers(opens.modifiers());
            }

            // requires
            for (Requires r : md.requires()) {
                dedupSetBuilder.requiresModifiers(r.modifiers());
            }

            // uses
            dedupSetBuilder.stringSet(md.uses());
        }

        /**
         * Generate SystemModules class
         */
        public ClassWriter getClassWriter(Configuration cf) {
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
                                             + ClassWriter.COMPUTE_FRAMES);
            cw.visit(Opcodes.V1_8,
                     ACC_FINAL+ACC_SUPER,
                     className,
                     null,
                     "java/lang/Object",
                     new String[] { "jdk/internal/module/SystemModules" });

            // generate <init>
            genConstructor(cw);

            // generate hasSplitPackages
            genHasSplitPackages(cw);

            // generate hasIncubatorModules
            genIncubatorModules(cw);

            // generate moduleDescriptors
            genModuleDescriptorsMethod(cw);

            // generate moduleTargets
            genModuleTargetsMethod(cw);

            // generate moduleHashes
            genModuleHashesMethod(cw);

            // generate moduleResolutions
            genModuleResolutionsMethod(cw);

            // generate moduleReads
            genModuleReads(cw, cf);

            // generate concealedPackagesToOpen and exportedPackagesToOpen
            genXXXPackagesToOpenMethods(cw);

            return cw;
        }

        /**
         * Generate byteccode for no-arg constructor
         */
        private void genConstructor(ClassWriter cw) {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL,
                               "java/lang/Object",
                               "<init>",
                               "()V",
                               false);
            mv.visitInsn(RETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        /**
         * Generate bytecode for hasSplitPackages method
         */
        private void genHasSplitPackages(ClassWriter cw) {
            boolean distinct = moduleInfos.stream()
                    .map(ModuleInfo::packages)
                    .flatMap(Set::stream)
                    .allMatch(new HashSet<>()::add);
            boolean hasSplitPackages = !distinct;

            mv = cw.visitMethod(ACC_PUBLIC,
                                "hasSplitPackages",
                                "()Z",
                                "()Z",
                                null);
            mv.visitCode();
            if (hasSplitPackages) {
                mv.visitInsn(ICONST_1);
            } else {
                mv.visitInsn(ICONST_0);
            }
            mv.visitInsn(IRETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        /**
         * Generate bytecode for hasIncubatorModules method
         */
        private void genIncubatorModules(ClassWriter cw) {
            boolean hasIncubatorModules = moduleInfos.stream()
                    .map(ModuleInfo::moduleResolution)
                    .filter(mres -> (mres != null && mres.hasIncubatingWarning()))
                    .findFirst()
                    .isPresent();

            mv = cw.visitMethod(ACC_PUBLIC,
                                "hasIncubatorModules",
                                "()Z",
                                "()Z",
                                null);
            mv.visitCode();
            if (hasIncubatorModules) {
                mv.visitInsn(ICONST_1);
            } else {
                mv.visitInsn(ICONST_0);
            }
            mv.visitInsn(IRETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        /**
         * Generate bytecode for moduleDescriptors method
         */
        private void genModuleDescriptorsMethod(ClassWriter cw) {
            this.mv = cw.visitMethod(ACC_PUBLIC,
                                     "moduleDescriptors",
                                     "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
                                     "()" + MODULE_DESCRIPTOR_ARRAY_SIGNATURE,
                                     null);
            mv.visitCode();
            pushInt(mv, moduleInfos.size());
            mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor");
            mv.visitVarInsn(ASTORE, MD_VAR);

            for (int index = 0; index < moduleInfos.size(); index++) {
                ModuleInfo minfo = moduleInfos.get(index);
                new ModuleDescriptorBuilder(minfo.descriptor(),
                                            minfo.packages(),
                                            index).build();
            }
            mv.visitVarInsn(ALOAD, MD_VAR);
            mv.visitInsn(ARETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        /**
         * Generate bytecode for moduleTargets method
         */
        private void genModuleTargetsMethod(ClassWriter cw) {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
                                              "moduleTargets",
                                              "()" + MODULE_TARGET_ARRAY_SIGNATURE,
                                              "()" + MODULE_TARGET_ARRAY_SIGNATURE,
                                              null);
            mv.visitCode();
            pushInt(mv, moduleInfos.size());
            mv.visitTypeInsn(ANEWARRAY, MODULE_TARGET_CLASSNAME);
            mv.visitVarInsn(ASTORE, MT_VAR);


            // if java.base has a ModuleTarget attribute then generate the array
            // with one element, all other elements will be null.

            ModuleInfo base = moduleInfos.get(0);
            if (!base.moduleName().equals("java.base"))
                throw new InternalError("java.base should be first module in list");
            ModuleTarget target = base.target();

            int count;
            if (target != null && target.targetPlatform() != null) {
                count = 1;
            } else {
                count = moduleInfos.size();
            }

            for (int index = 0; index < count; index++) {
                ModuleInfo minfo = moduleInfos.get(index);
                if (minfo.target() != null) {
                    mv.visitVarInsn(ALOAD, MT_VAR);
                    pushInt(mv, index);

                    // new ModuleTarget(String)
                    mv.visitTypeInsn(NEW, MODULE_TARGET_CLASSNAME);
                    mv.visitInsn(DUP);
                    mv.visitLdcInsn(minfo.target().targetPlatform());
                    mv.visitMethodInsn(INVOKESPECIAL, MODULE_TARGET_CLASSNAME,
                                       "<init>", "(Ljava/lang/String;)V", false);

                    mv.visitInsn(AASTORE);
                }
            }

            mv.visitVarInsn(ALOAD, MT_VAR);
            mv.visitInsn(ARETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        /**
         * Generate bytecode for moduleHashes method
         */
        private void genModuleHashesMethod(ClassWriter cw) {
            MethodVisitor hmv =
                cw.visitMethod(ACC_PUBLIC,
                               "moduleHashes",
                               "()" + MODULE_HASHES_ARRAY_SIGNATURE,
                               "()" + MODULE_HASHES_ARRAY_SIGNATURE,
                               null);
            hmv.visitCode();
            pushInt(hmv, moduleInfos.size());
            hmv.visitTypeInsn(ANEWARRAY, "jdk/internal/module/ModuleHashes");
            hmv.visitVarInsn(ASTORE, MH_VAR);

            for (int index = 0; index < moduleInfos.size(); index++) {
                ModuleInfo minfo = moduleInfos.get(index);
                if (minfo.recordedHashes() != null) {
                    new ModuleHashesBuilder(minfo.recordedHashes(),
                                            index,
                                            hmv).build();
                }
            }

            hmv.visitVarInsn(ALOAD, MH_VAR);
            hmv.visitInsn(ARETURN);
            hmv.visitMaxs(0, 0);
            hmv.visitEnd();
        }

        /**
         * Generate bytecode for moduleResolutions method
         */
        private void genModuleResolutionsMethod(ClassWriter cw) {
            MethodVisitor mresmv =
                cw.visitMethod(ACC_PUBLIC,
                               "moduleResolutions",
                               "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
                               "()" + MODULE_RESOLUTIONS_ARRAY_SIGNATURE,
                               null);
            mresmv.visitCode();
            pushInt(mresmv, moduleInfos.size());
            mresmv.visitTypeInsn(ANEWARRAY, MODULE_RESOLUTION_CLASSNAME);
            mresmv.visitVarInsn(ASTORE, 0);

            for (int index=0; index < moduleInfos.size(); index++) {
                ModuleInfo minfo = moduleInfos.get(index);
                if (minfo.moduleResolution() != null) {
                    mresmv.visitVarInsn(ALOAD, 0);
                    pushInt(mresmv, index);
                    mresmv.visitTypeInsn(NEW, MODULE_RESOLUTION_CLASSNAME);
                    mresmv.visitInsn(DUP);
                    mresmv.visitLdcInsn(minfo.moduleResolution().value());
                    mresmv.visitMethodInsn(INVOKESPECIAL,
                                           MODULE_RESOLUTION_CLASSNAME,
                                           "<init>",
                                           "(I)V", false);
                    mresmv.visitInsn(AASTORE);
                }
            }
            mresmv.visitVarInsn(ALOAD, 0);
            mresmv.visitInsn(ARETURN);
            mresmv.visitMaxs(0, 0);
            mresmv.visitEnd();
        }

        /**
         * Generate bytecode for moduleReads method
         */
        private void genModuleReads(ClassWriter cw, Configuration cf) {
            // module name -> names of modules that it reads
            Map<String, Set<String>> map = cf.modules().stream()
                    .collect(Collectors.toMap(
                            ResolvedModule::name,
                            m -> m.reads().stream()
                                    .map(ResolvedModule::name)
                                    .collect(Collectors.toSet())));
            generate(cw, "moduleReads", map, true);
        }

        /**
         * Generate concealedPackagesToOpen and exportedPackagesToOpen methods.
         */
        private void genXXXPackagesToOpenMethods(ClassWriter cw) {
            ModuleFinder finder = finderOf(moduleInfos);
            IllegalAccessMaps maps = IllegalAccessMaps.generate(finder);
            generate(cw, "concealedPackagesToOpen", maps.concealedPackagesToOpen(), false);
            generate(cw, "exportedPackagesToOpen", maps.exportedPackagesToOpen(), false);
        }

        /**
         * Generate method to return {@code Map<String, Set<String>>}.
         *
         * If {@code dedup} is true then the values are de-duplicated.
         */
        private void generate(ClassWriter cw,
                              String methodName,
                              Map<String, Set<String>> map,
                              boolean dedup) {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
                                              methodName,
                                              "()Ljava/util/Map;",
                                              "()Ljava/util/Map;",
                                              null);
            mv.visitCode();

            // map of Set -> local
            Map<Set<String>, Integer> locals;

            // generate code to create the sets that are duplicated
            if (dedup) {
                Collection<Set<String>> values = map.values();
                Set<Set<String>> duplicateSets = values.stream()
                        .distinct()
                        .filter(s -> Collections.frequency(values, s) > 1)
                        .collect(Collectors.toSet());
                locals = new HashMap<>();
                int index = 1;
                for (Set<String> s : duplicateSets) {
                    genImmutableSet(mv, s);
                    mv.visitVarInsn(ASTORE, index);
                    locals.put(s, index);
                    if (++index >= MAX_LOCAL_VARS) {
                        break;
                    }
                }
            } else {
                locals = Map.of();
            }

            // new Map$Entry[size]
            pushInt(mv, map.size());
            mv.visitTypeInsn(ANEWARRAY, "java/util/Map$Entry");

            int index = 0;
            for (Map.Entry<String, Set<String>> e : map.entrySet()) {
                String name = e.getKey();
                Set<String> s = e.getValue();

                mv.visitInsn(DUP);
                pushInt(mv, index);
                mv.visitLdcInsn(name);

                // if de-duplicated then load the local, otherwise generate code
                Integer varIndex = locals.get(s);
                if (varIndex == null) {
                    genImmutableSet(mv, s);
                } else {
                    mv.visitVarInsn(ALOAD, varIndex);
                }

                String desc = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map$Entry;";
                mv.visitMethodInsn(INVOKESTATIC,
                                   "java/util/Map",
                                   "entry",
                                   desc,
                                   true);
                mv.visitInsn(AASTORE);
                index++;
            }

            // invoke Map.ofEntries(Map$Entry[])
            mv.visitMethodInsn(INVOKESTATIC, "java/util/Map", "ofEntries",
                    "([Ljava/util/Map$Entry;)Ljava/util/Map;", true);
            mv.visitInsn(ARETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        /**
         * Generate code to generate an immutable set.
         */
        private void genImmutableSet(MethodVisitor mv, Set<String> set) {
            int size = set.size();

            // use Set.of(Object[]) when there are more than 2 elements
            // use Set.of(Object) or Set.of(Object, Object) when fewer
            if (size > 2) {
                pushInt(mv, size);
                mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
                int i = 0;
                for (String element : set) {
                    mv.visitInsn(DUP);
                    pushInt(mv, i);
                    mv.visitLdcInsn(element);
                    mv.visitInsn(AASTORE);
                    i++;
                }
                mv.visitMethodInsn(INVOKESTATIC,
                        "java/util/Set",
                        "of",
                        "([Ljava/lang/Object;)Ljava/util/Set;",
                        true);
            } else {
                StringBuilder sb = new StringBuilder("(");
                for (String element : set) {
                    mv.visitLdcInsn(element);
                    sb.append("Ljava/lang/Object;");
                }
                sb.append(")Ljava/util/Set;");
                mv.visitMethodInsn(INVOKESTATIC,
                        "java/util/Set",
                        "of",
                        sb.toString(),
                        true);
            }
        }

        class ModuleDescriptorBuilder {
            static final String BUILDER_TYPE = "Ljdk/internal/module/Builder;";
            static final String EXPORTS_TYPE =
                "Ljava/lang/module/ModuleDescriptor$Exports;";
            static final String OPENS_TYPE =
                "Ljava/lang/module/ModuleDescriptor$Opens;";
            static final String PROVIDES_TYPE =
                "Ljava/lang/module/ModuleDescriptor$Provides;";
            static final String REQUIRES_TYPE =
                "Ljava/lang/module/ModuleDescriptor$Requires;";

            // method signature for static Builder::newExports, newOpens,
            // newProvides, newRequires methods
            static final String EXPORTS_MODIFIER_SET_STRING_SET_SIG =
                "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)"
                    + EXPORTS_TYPE;
            static final String EXPORTS_MODIFIER_SET_STRING_SIG =
                "(Ljava/util/Set;Ljava/lang/String;)" + EXPORTS_TYPE;
            static final String OPENS_MODIFIER_SET_STRING_SET_SIG =
                "(Ljava/util/Set;Ljava/lang/String;Ljava/util/Set;)"
                    + OPENS_TYPE;
            static final String OPENS_MODIFIER_SET_STRING_SIG =
                "(Ljava/util/Set;Ljava/lang/String;)" + OPENS_TYPE;
            static final String PROVIDES_STRING_LIST_SIG =
                "(Ljava/lang/String;Ljava/util/List;)" + PROVIDES_TYPE;
            static final String REQUIRES_SET_STRING_SIG =
                "(Ljava/util/Set;Ljava/lang/String;)" + REQUIRES_TYPE;
            static final String REQUIRES_SET_STRING_STRING_SIG =
                "(Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;)" + REQUIRES_TYPE;

            // method signature for Builder instance methods that
            // return this Builder instance
            static final String EXPORTS_ARRAY_SIG =
                "([" + EXPORTS_TYPE + ")" + BUILDER_TYPE;
            static final String OPENS_ARRAY_SIG =
                "([" + OPENS_TYPE + ")" + BUILDER_TYPE;
            static final String PROVIDES_ARRAY_SIG =
                "([" + PROVIDES_TYPE + ")" + BUILDER_TYPE;
            static final String REQUIRES_ARRAY_SIG =
                "([" + REQUIRES_TYPE + ")" + BUILDER_TYPE;
            static final String SET_SIG = "(Ljava/util/Set;)" + BUILDER_TYPE;
            static final String STRING_SIG = "(Ljava/lang/String;)" + BUILDER_TYPE;
            static final String BOOLEAN_SIG = "(Z)" + BUILDER_TYPE;

            final ModuleDescriptor md;
            final Set<String> packages;
            final int index;

            ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages, int index) {
                if (md.isAutomatic()) {
                    throw new InternalError("linking automatic module is not supported");
                }
                this.md = md;
                this.packages = packages;
                this.index = index;
            }

            void build() {
                // new jdk.internal.module.Builder
                newBuilder();

                // requires
                requires(md.requires());

                // exports
                exports(md.exports());

                // opens
                opens(md.opens());

                // uses
                uses(md.uses());

                // provides
                provides(md.provides());

                // all packages
                packages(packages);

                // version
                md.version().ifPresent(this::version);

                // main class
                md.mainClass().ifPresent(this::mainClass);

                putModuleDescriptor();
            }

            void newBuilder() {
                mv.visitTypeInsn(NEW, MODULE_DESCRIPTOR_BUILDER);
                mv.visitInsn(DUP);
                mv.visitLdcInsn(md.name());
                mv.visitMethodInsn(INVOKESPECIAL, MODULE_DESCRIPTOR_BUILDER,
                    "<init>", "(Ljava/lang/String;)V", false);
                mv.visitVarInsn(ASTORE, BUILDER_VAR);
                mv.visitVarInsn(ALOAD, BUILDER_VAR);

                if (md.isOpen()) {
                    setModuleBit("open", true);
                }
                if (md.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)) {
                    setModuleBit("synthetic", true);
                }
                if (md.modifiers().contains(ModuleDescriptor.Modifier.MANDATED)) {
                    setModuleBit("mandated", true);
                }
            }

            /*
             * Invoke Builder.<methodName>(boolean value)
             */
            void setModuleBit(String methodName, boolean value) {
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                if (value) {
                    mv.visitInsn(ICONST_1);
                } else {
                    mv.visitInsn(ICONST_0);
                }
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    methodName, BOOLEAN_SIG, false);
                mv.visitInsn(POP);
            }

            /*
             * Put ModuleDescriptor into the modules array
             */
            void putModuleDescriptor() {
                mv.visitVarInsn(ALOAD, MD_VAR);
                pushInt(mv, index);
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                mv.visitLdcInsn(md.hashCode());
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    "build", "(I)Ljava/lang/module/ModuleDescriptor;",
                    false);
                mv.visitInsn(AASTORE);
            }

            /*
             * Call Builder::newRequires to create Requires instances and
             * then pass it to the builder by calling:
             *      Builder.requires(Requires[])
             *
             */
            void requires(Set<Requires> requires) {
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                pushInt(mv, requires.size());
                mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Requires");
                int arrayIndex = 0;
                for (Requires require : requires) {
                    String compiledVersion = null;
                    if (require.compiledVersion().isPresent()) {
                        compiledVersion = require.compiledVersion().get().toString();
                    }

                    mv.visitInsn(DUP);               // arrayref
                    pushInt(mv, arrayIndex++);
                    newRequires(require.modifiers(), require.name(), compiledVersion);
                    mv.visitInsn(AASTORE);
                }
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    "requires", REQUIRES_ARRAY_SIG, false);
            }

            /*
             * Invoke Builder.newRequires(Set<Modifier> mods, String mn, String compiledVersion)
             *
             * Set<Modifier> mods = ...
             * Builder.newRequires(mods, mn, compiledVersion);
             */
            void newRequires(Set<Requires.Modifier> mods, String name, String compiledVersion) {
                int varIndex = dedupSetBuilder.indexOfRequiresModifiers(mods);
                mv.visitVarInsn(ALOAD, varIndex);
                mv.visitLdcInsn(name);
                if (compiledVersion != null) {
                    mv.visitLdcInsn(compiledVersion);
                    mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
                        "newRequires", REQUIRES_SET_STRING_STRING_SIG, false);
                } else {
                    mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
                        "newRequires", REQUIRES_SET_STRING_SIG, false);
                }
            }

            /*
             * Call Builder::newExports to create Exports instances and
             * then pass it to the builder by calling:
             *      Builder.exports(Exports[])
             *
             */
            void exports(Set<Exports> exports) {
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                pushInt(mv, exports.size());
                mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Exports");
                int arrayIndex = 0;
                for (Exports export : exports) {
                    mv.visitInsn(DUP);    // arrayref
                    pushInt(mv, arrayIndex++);
                    newExports(export.modifiers(), export.source(), export.targets());
                    mv.visitInsn(AASTORE);
                }
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    "exports", EXPORTS_ARRAY_SIG, false);
            }

            /*
             * Invoke
             *     Builder.newExports(Set<Exports.Modifier> ms, String pn,
             *                        Set<String> targets)
             * or
             *     Builder.newExports(Set<Exports.Modifier> ms, String pn)
             *
             * Set<String> targets = new HashSet<>();
             * targets.add(t);
             * :
             * :
             *
             * Set<Modifier> mods = ...
             * Builder.newExports(mods, pn, targets);
             */
            void newExports(Set<Exports.Modifier> ms, String pn, Set<String> targets) {
                int modifiersSetIndex = dedupSetBuilder.indexOfExportsModifiers(ms);
                if (!targets.isEmpty()) {
                    int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets);
                    mv.visitVarInsn(ALOAD, modifiersSetIndex);
                    mv.visitLdcInsn(pn);
                    mv.visitVarInsn(ALOAD, stringSetIndex);
                    mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
                        "newExports", EXPORTS_MODIFIER_SET_STRING_SET_SIG, false);
                } else {
                    mv.visitVarInsn(ALOAD, modifiersSetIndex);
                    mv.visitLdcInsn(pn);
                    mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
                        "newExports", EXPORTS_MODIFIER_SET_STRING_SIG, false);
                }
            }


            /**
             * Call Builder::newOpens to create Opens instances and
             * then pass it to the builder by calling:
             * Builder.opens(Opens[])
             */
            void opens(Set<Opens> opens) {
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                pushInt(mv, opens.size());
                mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Opens");
                int arrayIndex = 0;
                for (Opens open : opens) {
                    mv.visitInsn(DUP);    // arrayref
                    pushInt(mv, arrayIndex++);
                    newOpens(open.modifiers(), open.source(), open.targets());
                    mv.visitInsn(AASTORE);
                }
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    "opens", OPENS_ARRAY_SIG, false);
            }

            /*
             * Invoke
             *     Builder.newOpens(Set<Opens.Modifier> ms, String pn,
             *                        Set<String> targets)
             * or
             *     Builder.newOpens(Set<Opens.Modifier> ms, String pn)
             *
             * Set<String> targets = new HashSet<>();
             * targets.add(t);
             * :
             * :
             *
             * Set<Modifier> mods = ...
             * Builder.newOpens(mods, pn, targets);
             */
            void newOpens(Set<Opens.Modifier> ms, String pn, Set<String> targets) {
                int modifiersSetIndex = dedupSetBuilder.indexOfOpensModifiers(ms);
                if (!targets.isEmpty()) {
                    int stringSetIndex = dedupSetBuilder.indexOfStringSet(targets);
                    mv.visitVarInsn(ALOAD, modifiersSetIndex);
                    mv.visitLdcInsn(pn);
                    mv.visitVarInsn(ALOAD, stringSetIndex);
                    mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
                        "newOpens", OPENS_MODIFIER_SET_STRING_SET_SIG, false);
                } else {
                    mv.visitVarInsn(ALOAD, modifiersSetIndex);
                    mv.visitLdcInsn(pn);
                    mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
                        "newOpens", OPENS_MODIFIER_SET_STRING_SIG, false);
                }
            }

            /*
             * Invoke Builder.uses(Set<String> uses)
             */
            void uses(Set<String> uses) {
                int varIndex = dedupSetBuilder.indexOfStringSet(uses);
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                mv.visitVarInsn(ALOAD, varIndex);
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    "uses", SET_SIG, false);
                mv.visitInsn(POP);
            }

            /*
            * Call Builder::newProvides to create Provides instances and
            * then pass it to the builder by calling:
            *      Builder.provides(Provides[] provides)
            *
            */
            void provides(Collection<Provides> provides) {
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                pushInt(mv, provides.size());
                mv.visitTypeInsn(ANEWARRAY, "java/lang/module/ModuleDescriptor$Provides");
                int arrayIndex = 0;
                for (Provides provide : provides) {
                    mv.visitInsn(DUP);    // arrayref
                    pushInt(mv, arrayIndex++);
                    newProvides(provide.service(), provide.providers());
                    mv.visitInsn(AASTORE);
                }
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    "provides", PROVIDES_ARRAY_SIG, false);
            }

            /*
             * Invoke Builder.newProvides(String service, Set<String> providers)
             *
             * Set<String> providers = new HashSet<>();
             * providers.add(impl);
             * :
             * :
             * Builder.newProvides(service, providers);
             */
            void newProvides(String service, List<String> providers) {
                mv.visitLdcInsn(service);
                pushInt(mv, providers.size());
                mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
                int arrayIndex = 0;
                for (String provider : providers) {
                    mv.visitInsn(DUP);    // arrayref
                    pushInt(mv, arrayIndex++);
                    mv.visitLdcInsn(provider);
                    mv.visitInsn(AASTORE);
                }
                mv.visitMethodInsn(INVOKESTATIC, "java/util/List",
                    "of", "([Ljava/lang/Object;)Ljava/util/List;", true);
                mv.visitMethodInsn(INVOKESTATIC, MODULE_DESCRIPTOR_BUILDER,
                    "newProvides", PROVIDES_STRING_LIST_SIG, false);
            }

            /*
             * Invoke Builder.packages(String pn)
             */
            void packages(Set<String> packages) {
                int varIndex = dedupSetBuilder.newStringSet(packages);
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                mv.visitVarInsn(ALOAD, varIndex);
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    "packages", SET_SIG, false);
                mv.visitInsn(POP);
            }

            /*
             * Invoke Builder.mainClass(String cn)
             */
            void mainClass(String cn) {
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                mv.visitLdcInsn(cn);
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    "mainClass", STRING_SIG, false);
                mv.visitInsn(POP);
            }

            /*
             * Invoke Builder.version(Version v);
             */
            void version(Version v) {
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                mv.visitLdcInsn(v.toString());
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    "version", STRING_SIG, false);
                mv.visitInsn(POP);
            }

            void invokeBuilderMethod(String methodName, String value) {
                mv.visitVarInsn(ALOAD, BUILDER_VAR);
                mv.visitLdcInsn(value);
                mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
                    methodName, STRING_SIG, false);
                mv.visitInsn(POP);
            }
        }

        class ModuleHashesBuilder {
            private static final String MODULE_HASHES_BUILDER =
                "jdk/internal/module/ModuleHashes$Builder";
            private static final String MODULE_HASHES_BUILDER_TYPE =
                "L" + MODULE_HASHES_BUILDER + ";";
            static final String STRING_BYTE_ARRAY_SIG =
                "(Ljava/lang/String;[B)" + MODULE_HASHES_BUILDER_TYPE;

            final ModuleHashes recordedHashes;
            final MethodVisitor hmv;
            final int index;

            ModuleHashesBuilder(ModuleHashes hashes, int index, MethodVisitor hmv) {
                this.recordedHashes = hashes;
                this.hmv = hmv;
                this.index = index;
            }

            /**
             * Build ModuleHashes
             */
            void build() {
                if (recordedHashes == null)
                    return;

                // new jdk.internal.module.ModuleHashes.Builder
                newModuleHashesBuilder();

                // Invoke ModuleHashes.Builder::hashForModule
                recordedHashes
                    .names()
                    .forEach(mn -> hashForModule(mn, recordedHashes.hashFor(mn)));

                // Put ModuleHashes into the hashes array
                pushModuleHashes();
            }


            /*
             * Create ModuleHashes.Builder instance
             */
            void newModuleHashesBuilder() {
                hmv.visitTypeInsn(NEW, MODULE_HASHES_BUILDER);
                hmv.visitInsn(DUP);
                hmv.visitLdcInsn(recordedHashes.algorithm());
                pushInt(hmv, ((4 * recordedHashes.names().size()) / 3) + 1);
                hmv.visitMethodInsn(INVOKESPECIAL, MODULE_HASHES_BUILDER,
                    "<init>", "(Ljava/lang/String;I)V", false);
                hmv.visitVarInsn(ASTORE, BUILDER_VAR);
                hmv.visitVarInsn(ALOAD, BUILDER_VAR);
            }


            /*
             * Invoke ModuleHashes.Builder::build and put the returned
             * ModuleHashes to the hashes array
             */
            void pushModuleHashes() {
                hmv.visitVarInsn(ALOAD, MH_VAR);
                pushInt(hmv, index);
                hmv.visitVarInsn(ALOAD, BUILDER_VAR);
                hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER,
                    "build", "()Ljdk/internal/module/ModuleHashes;",
                    false);
                hmv.visitInsn(AASTORE);
            }

            /*
             * Invoke ModuleHashes.Builder.hashForModule(String name, byte[] hash);
             */
            void hashForModule(String name, byte[] hash) {
                hmv.visitVarInsn(ALOAD, BUILDER_VAR);
                hmv.visitLdcInsn(name);

                pushInt(hmv, hash.length);
                hmv.visitIntInsn(NEWARRAY, T_BYTE);
                for (int i = 0; i < hash.length; i++) {
                    hmv.visitInsn(DUP);              // arrayref
                    pushInt(hmv, i);
                    hmv.visitIntInsn(BIPUSH, hash[i]);
                    hmv.visitInsn(BASTORE);
                }

                hmv.visitMethodInsn(INVOKEVIRTUAL, MODULE_HASHES_BUILDER,
                    "hashForModule", STRING_BYTE_ARRAY_SIG, false);
                hmv.visitInsn(POP);
            }
        }

        /*
         * Wraps set creation, ensuring identical sets are properly deduplicated.
         */
        class DedupSetBuilder {
            // map Set<String> to a specialized builder to allow them to be
            // deduplicated as they are requested
            final Map<Set<String>, SetBuilder<String>> stringSets = new HashMap<>();

            // map Set<Requires.Modifier> to a specialized builder to allow them to be
            // deduplicated as they are requested
            final Map<Set<Requires.Modifier>, EnumSetBuilder<Requires.Modifier>>
                requiresModifiersSets = new HashMap<>();

            // map Set<Exports.Modifier> to a specialized builder to allow them to be
            // deduplicated as they are requested
            final Map<Set<Exports.Modifier>, EnumSetBuilder<Exports.Modifier>>
                exportsModifiersSets = new HashMap<>();

            // map Set<Opens.Modifier> to a specialized builder to allow them to be
            // deduplicated as they are requested
            final Map<Set<Opens.Modifier>, EnumSetBuilder<Opens.Modifier>>
                opensModifiersSets = new HashMap<>();

            private final int stringSetVar;
            private final int enumSetVar;
            private final IntSupplier localVarSupplier;

            DedupSetBuilder(IntSupplier localVarSupplier) {
                this.stringSetVar = localVarSupplier.getAsInt();
                this.enumSetVar = localVarSupplier.getAsInt();
                this.localVarSupplier = localVarSupplier;
            }

            /*
             * Add the given set of strings to this builder.
             */
            void stringSet(Set<String> strings) {
                stringSets.computeIfAbsent(strings,
                    s -> new SetBuilder<>(s, stringSetVar, localVarSupplier)
                ).increment();
            }

            /*
             * Add the given set of Exports.Modifiers
             */
            void exportsModifiers(Set<Exports.Modifier> mods) {
                exportsModifiersSets.computeIfAbsent(mods, s ->
                                new EnumSetBuilder<>(s, EXPORTS_MODIFIER_CLASSNAME,
                                        enumSetVar, localVarSupplier)
                ).increment();
            }

            /*
             * Add the given set of Opens.Modifiers
             */
            void opensModifiers(Set<Opens.Modifier> mods) {
                opensModifiersSets.computeIfAbsent(mods, s ->
                                new EnumSetBuilder<>(s, OPENS_MODIFIER_CLASSNAME,
                                        enumSetVar, localVarSupplier)
                ).increment();
            }

            /*
             * Add the given set of Requires.Modifiers
             */
            void requiresModifiers(Set<Requires.Modifier> mods) {
                requiresModifiersSets.computeIfAbsent(mods, s ->
                    new EnumSetBuilder<>(s, REQUIRES_MODIFIER_CLASSNAME,
                                         enumSetVar, localVarSupplier)
                ).increment();
            }

            /*
             * Retrieve the index to the given set of Strings. Emit code to
             * generate it when SetBuilder::build is called.
             */
            int indexOfStringSet(Set<String> names) {
                return stringSets.get(names).build();
            }

            /*
             * Retrieve the index to the given set of Exports.Modifier.
             * Emit code to generate it when EnumSetBuilder::build is called.
             */
            int indexOfExportsModifiers(Set<Exports.Modifier> mods) {
                return exportsModifiersSets.get(mods).build();
            }

            /**
             * Retrieve the index to the given set of Opens.Modifier.
             * Emit code to generate it when EnumSetBuilder::build is called.
             */
            int indexOfOpensModifiers(Set<Opens.Modifier> mods) {
                return opensModifiersSets.get(mods).build();
            }


            /*
             * Retrieve the index to the given set of Requires.Modifier.
             * Emit code to generate it when EnumSetBuilder::build is called.
             */
            int indexOfRequiresModifiers(Set<Requires.Modifier> mods) {
                return requiresModifiersSets.get(mods).build();
            }

            /*
             * Build a new string set without any attempt to deduplicate it.
             */
            int newStringSet(Set<String> names) {
                int index = new SetBuilder<>(names, stringSetVar, localVarSupplier).build();
                assert index == stringSetVar;
                return index;
            }
        }

        /*
         * SetBuilder generates bytecode to create one single instance of Set
         * for a given set of elements and assign to a local variable slot.
         * When there is only one single reference to a Set<T>,
         * it will reuse defaultVarIndex.  For a Set with multiple references,
         * it will use a new local variable retrieved from the nextLocalVar
         */
        class SetBuilder<T> {
            private final Set<T> elements;
            private final int defaultVarIndex;
            private final IntSupplier nextLocalVar;
            private int refCount;
            private int localVarIndex;

            SetBuilder(Set<T> elements,
                       int defaultVarIndex,
                       IntSupplier nextLocalVar) {
                this.elements = elements;
                this.defaultVarIndex = defaultVarIndex;
                this.nextLocalVar = nextLocalVar;
            }

            /*
             * Increments the number of references to this particular set.
             */
            final void increment() {
                refCount++;
            }

            /**
             * Generate the appropriate instructions to load an object reference
             * to the element onto the stack.
             */
            void visitElement(T element, MethodVisitor mv) {
                mv.visitLdcInsn(element);
            }

            /*
             * Build bytecode for the Set represented by this builder,
             * or get the local variable index of a previously generated set
             * (in the local scope).
             *
             * @return local variable index of the generated set.
             */
            final int build() {
                int index = localVarIndex;
                if (localVarIndex == 0) {
                    // if non-empty and more than one set reference this builder,
                    // emit to a unique local
                    index = refCount <= 1 ? defaultVarIndex
                                          : nextLocalVar.getAsInt();
                    if (index < MAX_LOCAL_VARS) {
                        localVarIndex = index;
                    } else {
                        // overflow: disable optimization by using localVarIndex = 0
                        index = defaultVarIndex;
                    }

                    generateSetOf(index);
                }
                return index;
            }

            private void generateSetOf(int index) {
                if (elements.size() <= 10) {
                    // call Set.of(e1, e2, ...)
                    StringBuilder sb = new StringBuilder("(");
                    for (T t : elements) {
                        sb.append("Ljava/lang/Object;");
                        visitElement(t, mv);
                    }
                    sb.append(")Ljava/util/Set;");
                    mv.visitMethodInsn(INVOKESTATIC, "java/util/Set",
                            "of", sb.toString(), true);
                } else {
                    // call Set.of(E... elements)
                    pushInt(mv, elements.size());
                    mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
                    int arrayIndex = 0;
                    for (T t : elements) {
                        mv.visitInsn(DUP);    // arrayref
                        pushInt(mv, arrayIndex);
                        visitElement(t, mv);  // value
                        mv.visitInsn(AASTORE);
                        arrayIndex++;
                    }
                    mv.visitMethodInsn(INVOKESTATIC, "java/util/Set",
                            "of", "([Ljava/lang/Object;)Ljava/util/Set;", true);
                }
                mv.visitVarInsn(ASTORE, index);
            }
        }

        /*
         * Generates bytecode to create one single instance of EnumSet
         * for a given set of modifiers and assign to a local variable slot.
         */
        class EnumSetBuilder<T> extends SetBuilder<T> {

            private final String className;

            EnumSetBuilder(Set<T> modifiers, String className,
                           int defaultVarIndex,
                           IntSupplier nextLocalVar) {
                super(modifiers, defaultVarIndex, nextLocalVar);
                this.className = className;
            }

            /**
             * Loads an Enum field.
             */
            void visitElement(T t, MethodVisitor mv) {
                mv.visitFieldInsn(GETSTATIC, className, t.toString(),
                                  "L" + className + ";");
            }
        }
    }

    /**
     * Generate SystemModulesMap and add it as a resource.
     *
     * @return the name of the class resource added to the pool
     */
    private String genSystemModulesMapClass(String allSystemModulesClassName,
                                            String defaultSystemModulesClassName,
                                            Map<String, String> map,
                                            ResourcePoolBuilder out) {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
                                         + ClassWriter.COMPUTE_FRAMES);
        cw.visit(Opcodes.V1_8,
                 ACC_FINAL+ACC_SUPER,
                 SYSTEM_MODULES_MAP_CLASS,
                 null,
                 "java/lang/Object",
                 null);

        // <init>
        MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL,
                           "java/lang/Object",
                           "<init>",
                           "()V",
                           false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();

        // allSystemModules()
        mv = cw.visitMethod(ACC_STATIC,
                            "allSystemModules",
                            "()Ljdk/internal/module/SystemModules;",
                            "()Ljdk/internal/module/SystemModules;",
                            null);
        mv.visitCode();
        mv.visitTypeInsn(NEW, allSystemModulesClassName);
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL,
                           allSystemModulesClassName,
                           "<init>",
                           "()V",
                           false);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();

        // defaultSystemModules()
        mv = cw.visitMethod(ACC_STATIC,
                            "defaultSystemModules",
                            "()Ljdk/internal/module/SystemModules;",
                            "()Ljdk/internal/module/SystemModules;",
                            null);
        mv.visitCode();
        mv.visitTypeInsn(NEW, defaultSystemModulesClassName);
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL,
                           defaultSystemModulesClassName,
                           "<init>",
                           "()V",
                           false);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();

        // moduleNames()
        mv = cw.visitMethod(ACC_STATIC,
                            "moduleNames",
                            "()[Ljava/lang/String;",
                            "()[Ljava/lang/String;",
                            null);
        mv.visitCode();
        pushInt(mv, map.size());
        mv.visitTypeInsn(ANEWARRAY, "java/lang/String");

        int index = 0;
        for (String moduleName : map.keySet()) {
            mv.visitInsn(DUP);                  // arrayref
            pushInt(mv, index);
            mv.visitLdcInsn(moduleName);
            mv.visitInsn(AASTORE);
            index++;
        }

        mv.visitInsn(ARETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();

        // classNames()
        mv = cw.visitMethod(ACC_STATIC,
                            "classNames",
                            "()[Ljava/lang/String;",
                            "()[Ljava/lang/String;",
                            null);
        mv.visitCode();
        pushInt(mv, map.size());
        mv.visitTypeInsn(ANEWARRAY, "java/lang/String");

        index = 0;
        for (String className : map.values()) {
            mv.visitInsn(DUP);                  // arrayref
            pushInt(mv, index);
            mv.visitLdcInsn(className.replace('/', '.'));
            mv.visitInsn(AASTORE);
            index++;
        }

        mv.visitInsn(ARETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();

        // write the class file to the pool as a resource
        String rn = "/java.base/" + SYSTEM_MODULES_MAP_CLASS + ".class";
        ResourcePoolEntry e = ResourcePoolEntry.create(rn, cw.toByteArray());
        out.add(e);

        return rn;
    }

    /**
     * Pushes an int constant
     */
    private static void pushInt(MethodVisitor mv, int value) {
        if (value <= 5) {
            mv.visitInsn(ICONST_0 + value);
        } else if (value < Byte.MAX_VALUE) {
            mv.visitIntInsn(BIPUSH, value);
        } else if (value < Short.MAX_VALUE) {
            mv.visitIntInsn(SIPUSH, value);
        } else {
            throw new IllegalArgumentException("exceed limit: " + value);
        }
    }

    /**
     * Returns a module finder that finds all modules in the given list
     */
    private static ModuleFinder finderOf(Collection<ModuleInfo> moduleInfos) {
        Supplier<ModuleReader> readerSupplier = () -> null;
        Map<String, ModuleReference> namesToReference = new HashMap<>();
        for (ModuleInfo mi : moduleInfos) {
            String name = mi.moduleName();
            ModuleReference mref
                = new ModuleReferenceImpl(mi.descriptor(),
                                          URI.create("jrt:/" + name),
                                          readerSupplier,
                                          null,
                                          mi.target(),
                                          null,
                                          null,
                                          mi.moduleResolution());
            namesToReference.put(name, mref);
        }

        return new ModuleFinder() {
            @Override
            public Optional<ModuleReference> find(String name) {
                Objects.requireNonNull(name);
                return Optional.ofNullable(namesToReference.get(name));
            }
            @Override
            public Set<ModuleReference> findAll() {
                return new HashSet<>(namesToReference.values());
            }
        };
    }
}

jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java

 

Or download all of them as a single archive file:

File name: jdk.jlink-11.0.1-src.zip
File size: 155677 bytes
Release date: 2018-11-04
Download 

 

JDK 11 jdk.jshell.jmod - JShell Tool

JDK 11 jdk.jfr.jmod - JFR Module

Download and Use JDK 11

⇑⇑ FAQ for JDK (Java Development Kit)

2020-06-30, 25259👍, 0💬