/*
 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package jdk.internal.module;

import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Builder;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Version;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import jdk.internal.misc.JavaLangModuleAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.org.objectweb.asm.Attribute;
import jdk.internal.org.objectweb.asm.ByteVector;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Label;
import static jdk.internal.module.ClassFileConstants.*;


Provides ASM implementations of Attribute to read and write the class file attributes in a module-info class file.
/** * Provides ASM implementations of {@code Attribute} to read and write the * class file attributes in a module-info class file. */
public final class ClassFileAttributes { private ClassFileAttributes() { }
Module_attribute { // See lang-vm.html for details. }
/** * Module_attribute { * // See lang-vm.html for details. * } */
public static class ModuleAttribute extends Attribute { private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess(); private ModuleDescriptor descriptor; private Version replacementVersion; public ModuleAttribute(ModuleDescriptor descriptor) { super(MODULE); this.descriptor = descriptor; } public ModuleAttribute(Version v) { super(MODULE); this.replacementVersion = v; } public ModuleAttribute() { super(MODULE); } @Override protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) { // module_name (CONSTANT_Module_info) String mn = cr.readModule(off, buf); off += 2; // module_flags int module_flags = cr.readUnsignedShort(off); off += 2; Set<ModuleDescriptor.Modifier> modifiers = new HashSet<>(); if ((module_flags & ACC_OPEN) != 0) modifiers.add(ModuleDescriptor.Modifier.OPEN); if ((module_flags & ACC_SYNTHETIC) != 0) modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC); if ((module_flags & ACC_MANDATED) != 0) modifiers.add(ModuleDescriptor.Modifier.MANDATED); Builder builder = JLMA.newModuleBuilder(mn, false, modifiers); // module_version String module_version = cr.readUTF8(off, buf); off += 2; if (replacementVersion != null) { builder.version(replacementVersion); } else if (module_version != null) { builder.version(module_version); } // requires_count and requires[requires_count] int requires_count = cr.readUnsignedShort(off); off += 2; for (int i=0; i<requires_count; i++) { // CONSTANT_Module_info String dn = cr.readModule(off, buf); off += 2; // requires_flags int requires_flags = cr.readUnsignedShort(off); off += 2; Set<Requires.Modifier> mods; if (requires_flags == 0) { mods = Collections.emptySet(); } else { mods = new HashSet<>(); if ((requires_flags & ACC_TRANSITIVE) != 0) mods.add(Requires.Modifier.TRANSITIVE); if ((requires_flags & ACC_STATIC_PHASE) != 0) mods.add(Requires.Modifier.STATIC); if ((requires_flags & ACC_SYNTHETIC) != 0) mods.add(Requires.Modifier.SYNTHETIC); if ((requires_flags & ACC_MANDATED) != 0) mods.add(Requires.Modifier.MANDATED); } // requires_version String requires_version = cr.readUTF8(off, buf); off += 2; if (requires_version == null) { builder.requires(mods, dn); } else { JLMA.requires(builder, mods, dn, requires_version); } } // exports_count and exports[exports_count] int exports_count = cr.readUnsignedShort(off); off += 2; if (exports_count > 0) { for (int i=0; i<exports_count; i++) { // CONSTANT_Package_info String pkg = cr.readPackage(off, buf).replace('/', '.'); off += 2; int exports_flags = cr.readUnsignedShort(off); off += 2; Set<Exports.Modifier> mods; if (exports_flags == 0) { mods = Collections.emptySet(); } else { mods = new HashSet<>(); if ((exports_flags & ACC_SYNTHETIC) != 0) mods.add(Exports.Modifier.SYNTHETIC); if ((exports_flags & ACC_MANDATED) != 0) mods.add(Exports.Modifier.MANDATED); } int exports_to_count = cr.readUnsignedShort(off); off += 2; if (exports_to_count > 0) { Set<String> targets = new HashSet<>(); for (int j=0; j<exports_to_count; j++) { String t = cr.readModule(off, buf); off += 2; targets.add(t); } builder.exports(mods, pkg, targets); } else { builder.exports(mods, pkg); } } } // opens_count and opens[opens_count] int open_count = cr.readUnsignedShort(off); off += 2; if (open_count > 0) { for (int i=0; i<open_count; i++) { // CONSTANT_Package_info String pkg = cr.readPackage(off, buf).replace('/', '.'); off += 2; int opens_flags = cr.readUnsignedShort(off); off += 2; Set<Opens.Modifier> mods; if (opens_flags == 0) { mods = Collections.emptySet(); } else { mods = new HashSet<>(); if ((opens_flags & ACC_SYNTHETIC) != 0) mods.add(Opens.Modifier.SYNTHETIC); if ((opens_flags & ACC_MANDATED) != 0) mods.add(Opens.Modifier.MANDATED); } int opens_to_count = cr.readUnsignedShort(off); off += 2; if (opens_to_count > 0) { Set<String> targets = new HashSet<>(); for (int j=0; j<opens_to_count; j++) { String t = cr.readModule(off, buf); off += 2; targets.add(t); } builder.opens(mods, pkg, targets); } else { builder.opens(mods, pkg); } } } // uses_count and uses_index[uses_count] int uses_count = cr.readUnsignedShort(off); off += 2; if (uses_count > 0) { for (int i=0; i<uses_count; i++) { String sn = cr.readClass(off, buf).replace('/', '.'); builder.uses(sn); off += 2; } } // provides_count and provides[provides_count] int provides_count = cr.readUnsignedShort(off); off += 2; if (provides_count > 0) { for (int i=0; i<provides_count; i++) { String service = cr.readClass(off, buf).replace('/', '.'); off += 2; int with_count = cr.readUnsignedShort(off); off += 2; List<String> providers = new ArrayList<>(); for (int j=0; j<with_count; j++) { String cn = cr.readClass(off, buf).replace('/', '.'); off += 2; providers.add(cn); } builder.provides(service, providers); } } return new ModuleAttribute(builder.build()); } @Override protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { assert descriptor != null; ByteVector attr = new ByteVector(); // module_name String mn = descriptor.name(); int module_name_index = cw.newModule(mn); attr.putShort(module_name_index); // module_flags Set<ModuleDescriptor.Modifier> modifiers = descriptor.modifiers(); int module_flags = 0; if (modifiers.contains(ModuleDescriptor.Modifier.OPEN)) module_flags |= ACC_OPEN; if (modifiers.contains(ModuleDescriptor.Modifier.SYNTHETIC)) module_flags |= ACC_SYNTHETIC; if (modifiers.contains(ModuleDescriptor.Modifier.MANDATED)) module_flags |= ACC_MANDATED; attr.putShort(module_flags); // module_version String vs = descriptor.rawVersion().orElse(null); if (vs == null) { attr.putShort(0); } else { int module_version_index = cw.newUTF8(vs); attr.putShort(module_version_index); } // requires_count attr.putShort(descriptor.requires().size()); // requires[requires_count] for (Requires r : descriptor.requires()) { int requires_index = cw.newModule(r.name()); attr.putShort(requires_index); int requires_flags = 0; if (r.modifiers().contains(Requires.Modifier.TRANSITIVE)) requires_flags |= ACC_TRANSITIVE; if (r.modifiers().contains(Requires.Modifier.STATIC)) requires_flags |= ACC_STATIC_PHASE; if (r.modifiers().contains(Requires.Modifier.SYNTHETIC)) requires_flags |= ACC_SYNTHETIC; if (r.modifiers().contains(Requires.Modifier.MANDATED)) requires_flags |= ACC_MANDATED; attr.putShort(requires_flags); int requires_version_index; vs = r.rawCompiledVersion().orElse(null); if (vs == null) { requires_version_index = 0; } else { requires_version_index = cw.newUTF8(vs); } attr.putShort(requires_version_index); } // exports_count and exports[exports_count]; attr.putShort(descriptor.exports().size()); for (Exports e : descriptor.exports()) { String pkg = e.source().replace('.', '/'); attr.putShort(cw.newPackage(pkg)); int exports_flags = 0; if (e.modifiers().contains(Exports.Modifier.SYNTHETIC)) exports_flags |= ACC_SYNTHETIC; if (e.modifiers().contains(Exports.Modifier.MANDATED)) exports_flags |= ACC_MANDATED; attr.putShort(exports_flags); if (e.isQualified()) { Set<String> ts = e.targets(); attr.putShort(ts.size()); ts.forEach(target -> attr.putShort(cw.newModule(target))); } else { attr.putShort(0); } } // opens_counts and opens[opens_counts] attr.putShort(descriptor.opens().size()); for (Opens obj : descriptor.opens()) { String pkg = obj.source().replace('.', '/'); attr.putShort(cw.newPackage(pkg)); int opens_flags = 0; if (obj.modifiers().contains(Opens.Modifier.SYNTHETIC)) opens_flags |= ACC_SYNTHETIC; if (obj.modifiers().contains(Opens.Modifier.MANDATED)) opens_flags |= ACC_MANDATED; attr.putShort(opens_flags); if (obj.isQualified()) { Set<String> ts = obj.targets(); attr.putShort(ts.size()); ts.forEach(target -> attr.putShort(cw.newModule(target))); } else { attr.putShort(0); } } // uses_count and uses_index[uses_count] if (descriptor.uses().isEmpty()) { attr.putShort(0); } else { attr.putShort(descriptor.uses().size()); for (String s : descriptor.uses()) { String service = s.replace('.', '/'); int index = cw.newClass(service); attr.putShort(index); } } // provides_count and provides[provides_count] if (descriptor.provides().isEmpty()) { attr.putShort(0); } else { attr.putShort(descriptor.provides().size()); for (Provides p : descriptor.provides()) { String service = p.service().replace('.', '/'); attr.putShort(cw.newClass(service)); int with_count = p.providers().size(); attr.putShort(with_count); for (String provider : p.providers()) { attr.putShort(cw.newClass(provider.replace('.', '/'))); } } } return attr; } }
ModulePackages attribute.
 
ModulePackages_attribute {
  // index to CONSTANT_utf8_info structure in constant pool representing
  // the string "ModulePackages"
  u2 attribute_name_index;
  u4 attribute_length;
  // the number of entries in the packages table
  u2 packages_count;
  { // index to CONSTANT_Package_info structure with the package name
    u2 package_index
  } packages[package_count];
 }</pre>
/** * ModulePackages attribute. * * <pre> {@code * * ModulePackages_attribute { * // index to CONSTANT_utf8_info structure in constant pool representing * // the string "ModulePackages" * u2 attribute_name_index; * u4 attribute_length; * * // the number of entries in the packages table * u2 packages_count; * { // index to CONSTANT_Package_info structure with the package name * u2 package_index * } packages[package_count]; * * }</pre> */
public static class ModulePackagesAttribute extends Attribute { private final Set<String> packages; public ModulePackagesAttribute(Set<String> packages) { super(MODULE_PACKAGES); this.packages = packages; } public ModulePackagesAttribute() { this(null); } @Override protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) { // package count int package_count = cr.readUnsignedShort(off); off += 2; // packages Set<String> packages = new HashSet<>(); for (int i=0; i<package_count; i++) { String pkg = cr.readPackage(off, buf).replace('/', '.'); packages.add(pkg); off += 2; } return new ModulePackagesAttribute(packages); } @Override protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { assert packages != null; ByteVector attr = new ByteVector(); // package_count attr.putShort(packages.size()); // packages packages.stream() .map(p -> p.replace('.', '/')) .forEach(p -> attr.putShort(cw.newPackage(p))); return attr; } }
ModuleMainClass attribute.
 
MainClass_attribute {
  // index to CONSTANT_utf8_info structure in constant pool representing
  // the string "ModuleMainClass"
  u2 attribute_name_index;
  u4 attribute_length;
  // index to CONSTANT_Class_info structure with the main class name
  u2 main_class_index;
 }
 
/** * ModuleMainClass attribute. * * <pre> {@code * * MainClass_attribute { * // index to CONSTANT_utf8_info structure in constant pool representing * // the string "ModuleMainClass" * u2 attribute_name_index; * u4 attribute_length; * * // index to CONSTANT_Class_info structure with the main class name * u2 main_class_index; * } * * } </pre> */
public static class ModuleMainClassAttribute extends Attribute { private final String mainClass; public ModuleMainClassAttribute(String mainClass) { super(MODULE_MAIN_CLASS); this.mainClass = mainClass; } public ModuleMainClassAttribute() { this(null); } @Override protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) { String value = cr.readClass(off, buf).replace('/', '.'); return new ModuleMainClassAttribute(value); } @Override protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { ByteVector attr = new ByteVector(); int index = cw.newClass(mainClass.replace('.', '/')); attr.putShort(index); return attr; } }
ModuleTarget attribute.
 
TargetPlatform_attribute {
  // index to CONSTANT_utf8_info structure in constant pool representing
  // the string "ModuleTarget"
  u2 attribute_name_index;
  u4 attribute_length;
  // index to CONSTANT_utf8_info structure with the target platform
  u2 target_platform_index;
 }
 
/** * ModuleTarget attribute. * * <pre> {@code * * TargetPlatform_attribute { * // index to CONSTANT_utf8_info structure in constant pool representing * // the string "ModuleTarget" * u2 attribute_name_index; * u4 attribute_length; * * // index to CONSTANT_utf8_info structure with the target platform * u2 target_platform_index; * } * * } </pre> */
public static class ModuleTargetAttribute extends Attribute { private final String targetPlatform; public ModuleTargetAttribute(String targetPlatform) { super(MODULE_TARGET); this.targetPlatform = targetPlatform; } public ModuleTargetAttribute() { this(null); } public String targetPlatform() { return targetPlatform; } @Override protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) { String targetPlatform = null; int target_platform_index = cr.readUnsignedShort(off); if (target_platform_index != 0) targetPlatform = cr.readUTF8(off, buf); off += 2; return new ModuleTargetAttribute(targetPlatform); } @Override protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { ByteVector attr = new ByteVector(); int target_platform_index = 0; if (targetPlatform != null && targetPlatform.length() > 0) target_platform_index = cw.newUTF8(targetPlatform); attr.putShort(target_platform_index); return attr; } }
ModuleHashes attribute.
 
ModuleHashes_attribute {
  // index to CONSTANT_utf8_info structure in constant pool representing
  // the string "ModuleHashes"
  u2 attribute_name_index;
  u4 attribute_length;
  // index to CONSTANT_utf8_info structure with algorithm name
  u2 algorithm_index;
  // the number of entries in the hashes table
  u2 hashes_count;
  {   u2 module_name_index (index to CONSTANT_Module_info structure)
      u2 hash_length;
      u1 hash[hash_length];
  } hashes[hashes_count];
 } </pre>
/** * ModuleHashes attribute. * * <pre> {@code * * ModuleHashes_attribute { * // index to CONSTANT_utf8_info structure in constant pool representing * // the string "ModuleHashes" * u2 attribute_name_index; * u4 attribute_length; * * // index to CONSTANT_utf8_info structure with algorithm name * u2 algorithm_index; * * // the number of entries in the hashes table * u2 hashes_count; * { u2 module_name_index (index to CONSTANT_Module_info structure) * u2 hash_length; * u1 hash[hash_length]; * } hashes[hashes_count]; * * } </pre> */
static class ModuleHashesAttribute extends Attribute { private final ModuleHashes hashes; ModuleHashesAttribute(ModuleHashes hashes) { super(MODULE_HASHES); this.hashes = hashes; } ModuleHashesAttribute() { this(null); } @Override protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) { String algorithm = cr.readUTF8(off, buf); off += 2; int hashes_count = cr.readUnsignedShort(off); off += 2; Map<String, byte[]> map = new HashMap<>(); for (int i=0; i<hashes_count; i++) { String mn = cr.readModule(off, buf); off += 2; int hash_length = cr.readUnsignedShort(off); off += 2; byte[] hash = new byte[hash_length]; for (int j=0; j<hash_length; j++) { hash[j] = (byte) (0xff & cr.readByte(off+j)); } off += hash_length; map.put(mn, hash); } ModuleHashes hashes = new ModuleHashes(algorithm, map); return new ModuleHashesAttribute(hashes); } @Override protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { ByteVector attr = new ByteVector(); int index = cw.newUTF8(hashes.algorithm()); attr.putShort(index); Set<String> names = hashes.names(); attr.putShort(names.size()); for (String mn : names) { byte[] hash = hashes.hashFor(mn); assert hash != null; attr.putShort(cw.newModule(mn)); attr.putShort(hash.length); for (byte b: hash) { attr.putByte(b); } } return attr; } }
ModuleResolution_attribute { u2 attribute_name_index; // "ModuleResolution" u4 attribute_length; // 2 u2 resolution_flags; The value of the resolution_flags item is a mask of flags used to denote properties of module resolution. The flags are as follows: // Optional 0x0001 (DO_NOT_RESOLVE_BY_DEFAULT) // At most one of: 0x0002 (WARN_DEPRECATED) 0x0004 (WARN_DEPRECATED_FOR_REMOVAL) 0x0008 (WARN_INCUBATING)
/** * ModuleResolution_attribute { * u2 attribute_name_index; // "ModuleResolution" * u4 attribute_length; // 2 * u2 resolution_flags; * * The value of the resolution_flags item is a mask of flags used to denote * properties of module resolution. The flags are as follows: * * // Optional * 0x0001 (DO_NOT_RESOLVE_BY_DEFAULT) * * // At most one of: * 0x0002 (WARN_DEPRECATED) * 0x0004 (WARN_DEPRECATED_FOR_REMOVAL) * 0x0008 (WARN_INCUBATING) */
static class ModuleResolutionAttribute extends Attribute { private final int value; ModuleResolutionAttribute() { super(MODULE_RESOLUTION); value = 0; } ModuleResolutionAttribute(int value) { super(MODULE_RESOLUTION); this.value = value; } @Override protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) { int flags = cr.readUnsignedShort(off); return new ModuleResolutionAttribute(flags); } @Override protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { ByteVector attr = new ByteVector(); attr.putShort(value); return attr; } } }