code: // ASM: a very small and fast Java bytecode manipulation framework
  // Copyright (c) 2000-2011 INRIA, France Telecom
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions
  // are met:
  // 1. Redistributions of source code must retain the above copyright
  //    notice, this list of conditions and the following disclaimer.
  // 2. Redistributions in binary form must reproduce the above copyright
  //    notice, this list of conditions and the following disclaimer in the
  //    documentation and/or other materials provided with the distribution.
  // 3. Neither the name of the copyright holders nor the names of its
  //    contributors may be used to endorse or promote products derived from
  //    this software without specific prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  // THE POSSIBILITY OF SUCH DAMAGE.
  package clojure.asm;
  
  /**
   * A {@link ModuleVisitor} that generates the corresponding Module, ModulePackages and
   * ModuleMainClass attributes, as defined in the Java Virtual Machine Specification (JVMS).
   *
   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.25">JVMS
   *     4.7.25</a>
   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.26">JVMS
   *     4.7.26</a>
   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.27">JVMS
   *     4.7.27</a>
   * @author Remi Forax
   * @author Eric Bruneton
   */
  final class ModuleWriter extends ModuleVisitor {
  
    /** Where the constants used in this AnnotationWriter must be stored. */
    private final SymbolTable symbolTable;
  
    /** The module_name_index field of the JVMS Module attribute. */
    private final int moduleNameIndex;
  
    /** The module_flags field of the JVMS Module attribute. */
    private final int moduleFlags;
  
    /** The module_version_index field of the JVMS Module attribute. */
    private final int moduleVersionIndex;
  
    /** The requires_count field of the JVMS Module attribute. */
    private int requiresCount;
  
    /** The binary content of the 'requires' array of the JVMS Module attribute. */
    private final ByteVector requires;
  
    /** The exports_count field of the JVMS Module attribute. */
    private int exportsCount;
  
    /** The binary content of the 'exports' array of the JVMS Module attribute. */
    private final ByteVector exports;
  
    /** The opens_count field of the JVMS Module attribute. */
    private int opensCount;
  
    /** The binary content of the 'opens' array of the JVMS Module attribute. */
    private final ByteVector opens;
  
    /** The uses_count field of the JVMS Module attribute. */
    private int usesCount;
  
    /** The binary content of the 'uses_index' array of the JVMS Module attribute. */
    private final ByteVector usesIndex;
  
    /** The provides_count field of the JVMS Module attribute. */
    private int providesCount;
  
    /** The binary content of the 'provides' array of the JVMS Module attribute. */
    private final ByteVector provides;
  
    /** The provides_count field of the JVMS ModulePackages attribute. */
    private int packageCount;
  
    /** The binary content of the 'package_index' array of the JVMS ModulePackages attribute. */
    private final ByteVector packageIndex;
  
    /** The main_class_index field of the JVMS ModuleMainClass attribute, or 0. */
    private int mainClassIndex;
  
    ModuleWriter(final SymbolTable symbolTable, final int name, final int access, final int version) {
      super(Opcodes.ASM6);
      this.symbolTable = symbolTable;
      this.moduleNameIndex = name;
      this.moduleFlags = access;
      this.moduleVersionIndex = version;
      this.requires = new ByteVector();
      this.exports = new ByteVector();
      this.opens = new ByteVector();
      this.usesIndex = new ByteVector();
      this.provides = new ByteVector();
      this.packageIndex = new ByteVector();
    }
  
    @Override
    public void visitMainClass(final String mainClass) {
      this.mainClassIndex = symbolTable.addConstantClass(mainClass).index;
    }
  
    @Override
    public void visitPackage(final String packaze) {
      packageIndex.putShort(symbolTable.addConstantPackage(packaze).index);
      packageCount++;
    }
  
    @Override
    public void visitRequire(final String module, final int access, final String version) {
      requires
          .putShort(symbolTable.addConstantModule(module).index)
          .putShort(access)
          .putShort(version == null ? 0 : symbolTable.addConstantUtf8(version));
      requiresCount++;
    }
  
    @Override
    public void visitExport(final String packaze, final int access, final String... modules) {
      exports.putShort(symbolTable.addConstantPackage(packaze).index).putShort(access);
      if (modules == null) {
        exports.putShort(0);
      } else {
        exports.putShort(modules.length);
        for (String module : modules) {
          exports.putShort(symbolTable.addConstantModule(module).index);
        }
      }
      exportsCount++;
    }
  
    @Override
    public void visitOpen(final String packaze, final int access, final String... modules) {
      opens.putShort(symbolTable.addConstantPackage(packaze).index).putShort(access);
      if (modules == null) {
        opens.putShort(0);
      } else {
        opens.putShort(modules.length);
        for (String module : modules) {
          opens.putShort(symbolTable.addConstantModule(module).index);
        }
      }
      opensCount++;
    }
  
    @Override
    public void visitUse(final String service) {
      usesIndex.putShort(symbolTable.addConstantClass(service).index);
      usesCount++;
    }
  
    @Override
    public void visitProvide(final String service, final String... providers) {
      provides.putShort(symbolTable.addConstantClass(service).index);
      provides.putShort(providers.length);
      for (String provider : providers) {
        provides.putShort(symbolTable.addConstantClass(provider).index);
      }
      providesCount++;
    }
  
    @Override
    public void visitEnd() {
      // Nothing to do.
    }
  
    /**
     * Returns the number of Module, ModulePackages and ModuleMainClass attributes generated by this
     * ModuleWriter.
     *
     * @return the number of Module, ModulePackages and ModuleMainClass attributes (between 1 and 3).
     */
    int getAttributeCount() {
      return 1 + (packageCount > 0 ? 1 : 0) + (mainClassIndex > 0 ? 1 : 0);
    }
  
    /**
     * Returns the size of the Module, ModulePackages and ModuleMainClass attributes generated by this
     * ModuleWriter. Also add the names of these attributes in the constant pool.
     *
     * @return the size in bytes of the Module, ModulePackages and ModuleMainClass attributes.
     */
    int computeAttributesSize() {
      symbolTable.addConstantUtf8(Constants.MODULE);
      // 6 attribute header bytes, 6 bytes for name, flags and version, and 5 * 2 bytes for counts.
      int size =
          22 + requires.length + exports.length + opens.length + usesIndex.length + provides.length;
      if (packageCount > 0) {
        symbolTable.addConstantUtf8(Constants.MODULE_PACKAGES);
        // 6 attribute header bytes, and 2 bytes for package_count.
        size += 8 + packageIndex.length;
      }
      if (mainClassIndex > 0) {
        symbolTable.addConstantUtf8(Constants.MODULE_MAIN_CLASS);
        // 6 attribute header bytes, and 2 bytes for main_class_index.
        size += 8;
      }
      return size;
    }
  
    /**
     * Puts the Module, ModulePackages and ModuleMainClass attributes generated by this ModuleWriter
     * in the given ByteVector.
     *
     * @param output where the attributes must be put.
     */
    void putAttributes(final ByteVector output) {
      // 6 bytes for name, flags and version, and 5 * 2 bytes for counts.
      int moduleAttributeLength =
          16 + requires.length + exports.length + opens.length + usesIndex.length + provides.length;
      output
          .putShort(symbolTable.addConstantUtf8(Constants.MODULE))
          .putInt(moduleAttributeLength)
          .putShort(moduleNameIndex)
          .putShort(moduleFlags)
          .putShort(moduleVersionIndex)
          .putShort(requiresCount)
          .putByteArray(requires.data, 0, requires.length)
          .putShort(exportsCount)
          .putByteArray(exports.data, 0, exports.length)
          .putShort(opensCount)
          .putByteArray(opens.data, 0, opens.length)
          .putShort(usesCount)
          .putByteArray(usesIndex.data, 0, usesIndex.length)
          .putShort(providesCount)
          .putByteArray(provides.data, 0, provides.length);
      if (packageCount > 0) {
        output
            .putShort(symbolTable.addConstantUtf8(Constants.MODULE_PACKAGES))
            .putInt(2 + packageIndex.length)
            .putShort(packageCount)
            .putByteArray(packageIndex.data, 0, packageIndex.length);
      }
      if (mainClassIndex > 0) {
        output
            .putShort(symbolTable.addConstantUtf8(Constants.MODULE_MAIN_CLASS))
            .putInt(2)
            .putShort(mainClassIndex);
      }
    }
  }
  