| /* |
| * 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 {@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. |
| * } |
| */ |
| 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. |
| * |
| * <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. |
| * |
| * <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. |
| * |
| * <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. |
| * |
| * <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) |
| */ |
| 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; |
| } |
| } |
| } |