src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java
author rriggs
Sun, 29 Oct 2017 18:20:53 -0400
changeset 47471 304ef03403b1
parent 47216 71c04702a3d5
permissions -rw-r--r--
8190323: "the the" typos Reviewed-by: rriggs Contributed-by: christoph.dreis@freenet.de

/*
 * 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;
        }
    }
}