    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 *
    017 */
    018package org.apache.bcel.classfile;
    019
    020import java.io.DataInput;
    021import java.io.DataInputStream;
    022import java.io.DataOutputStream;
    023import java.io.IOException;
    024import java.util.HashMap;
    025import java.util.Map;
    026
    027import org.apache.bcel.Const;
    028
    029/**
    030 * Abstract super class for <em>Attribute</em> objects. Currently the <em>ConstantValue</em>, <em>SourceFile</em>,
    031 * <em>Code</em>, <em>Exceptiontable</em>, <em>LineNumberTable</em>, <em>LocalVariableTable</em>, <em>InnerClasses</em>
    032 * and <em>Synthetic</em> attributes are supported. The <em>Unknown</em> attribute stands for non-standard-attributes.
    033 *
    034 * @see ConstantValue
    035 * @see SourceFile
    036 * @see Code
    037 * @see Unknown
    038 * @see ExceptionTable
    039 * @see LineNumberTable
    040 * @see LocalVariableTable
    041 * @see InnerClasses
    042 * @see Synthetic
    043 * @see Deprecated
    044 * @see Signature
    045 */
    046public abstract class Attribute implements Cloneable, Node {
    047
    048    private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off
    049
    050    private static final Map<String, Object> READERS = new HashMap<>();
    051
    052    /**
    053     * Empty array.
    054     *
    055     * @since 6.6.0
    056     */
    057    public static final Attribute[] EMPTY_ARRAY = {};
    058
    059    /**
    060     * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
    061     * standard attributes such as "LineNumberTable", because those are handled internally.
    062     *
    063     * @param name the name of the attribute as stored in the class file
    064     * @param attributeReader the reader object
    065     * @deprecated (6.0) Use {@link #addAttributeReader(String, UnknownAttributeReader)} instead
    066     */
    067    @java.lang.Deprecated
    068    public static void addAttributeReader(final String name, final AttributeReader attributeReader) {
    069        READERS.put(name, attributeReader);
    070    }
    071
    072    /**
    073     * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
    074     * standard attributes such as "LineNumberTable", because those are handled internally.
    075     *
    076     * @param name the name of the attribute as stored in the class file
    077     * @param unknownAttributeReader the reader object
    078     */
    079    public static void addAttributeReader(final String name, final UnknownAttributeReader unknownAttributeReader) {
    080        READERS.put(name, unknownAttributeReader);
    081    }
    082
    083    protected static void println(final String msg) {
    084        if (debug) {
    085            System.err.println(msg);
    086        }
    087    }
    088
    089    /**
    090     * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
    091     * is called by the Field and Method constructor methods.
    092     *
    093     * @see Field
    094     * @see Method
    095     *
    096     * @param file Input stream
    097     * @param constant_pool Array of constants
    098     * @return Attribute
    099     * @throws IOException if an I/O error occurs.
    100     * @since 6.0
    101     */
    102    public static Attribute readAttribute(final DataInput file, final ConstantPool constant_pool) throws IOException {
    103        byte tag = Const.ATTR_UNKNOWN; // Unknown attribute
    104        // Get class name from constant pool via `name_index' indirection
    105        final int name_index = file.readUnsignedShort();
    106        final String name = constant_pool.getConstantUtf8(name_index).getBytes();
    107
    108        // Length of data in bytes
    109        final int length = file.readInt();
    110
    111        // Compare strings to find known attribute
    112        for (byte i = 0; i < Const.KNOWN_ATTRIBUTES; i++) {
    113            if (name.equals(Const.getAttributeName(i))) {
    114                tag = i; // found!
    115                break;
    116            }
    117        }
    118
    119        // Call proper constructor, depending on `tag'
    120        switch (tag) {
    121        case Const.ATTR_UNKNOWN:
    122            final Object r = READERS.get(name);
    123            if (r instanceof UnknownAttributeReader) {
    124                return ((UnknownAttributeReader) r).createAttribute(name_index, length, file, constant_pool);
    125            }
    126            return new Unknown(name_index, length, file, constant_pool);
    127        case Const.ATTR_CONSTANT_VALUE:
    128            return new ConstantValue(name_index, length, file, constant_pool);
    129        case Const.ATTR_SOURCE_FILE:
    130            return new SourceFile(name_index, length, file, constant_pool);
    131        case Const.ATTR_CODE:
    132            return new Code(name_index, length, file, constant_pool);
    133        case Const.ATTR_EXCEPTIONS:
    134            return new ExceptionTable(name_index, length, file, constant_pool);
    135        case Const.ATTR_LINE_NUMBER_TABLE:
    136            return new LineNumberTable(name_index, length, file, constant_pool);
    137        case Const.ATTR_LOCAL_VARIABLE_TABLE:
    138            return new LocalVariableTable(name_index, length, file, constant_pool);
    139        case Const.ATTR_INNER_CLASSES:
    140            return new InnerClasses(name_index, length, file, constant_pool);
    141        case Const.ATTR_SYNTHETIC:
    142            return new Synthetic(name_index, length, file, constant_pool);
    143        case Const.ATTR_DEPRECATED:
    144            return new Deprecated(name_index, length, file, constant_pool);
    145        case Const.ATTR_PMG:
    146            return new PMGClass(name_index, length, file, constant_pool);
    147        case Const.ATTR_SIGNATURE:
    148            return new Signature(name_index, length, file, constant_pool);
    149        case Const.ATTR_STACK_MAP:
    150            // old style stack map: unneeded for JDK5 and below;
    151            // illegal(?) for JDK6 and above. So just delete with a warning.
    152            println("Warning: Obsolete StackMap attribute ignored.");
    153            return new Unknown(name_index, length, file, constant_pool);
    154        case Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS:
    155            return new RuntimeVisibleAnnotations(name_index, length, file, constant_pool);
    156        case Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS:
    157            return new RuntimeInvisibleAnnotations(name_index, length, file, constant_pool);
    158        case Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
    159            return new RuntimeVisibleParameterAnnotations(name_index, length, file, constant_pool);
    160        case Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
    161            return new RuntimeInvisibleParameterAnnotations(name_index, length, file, constant_pool);
    162        case Const.ATTR_ANNOTATION_DEFAULT:
    163            return new AnnotationDefault(name_index, length, file, constant_pool);
    164        case Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE:
    165            return new LocalVariableTypeTable(name_index, length, file, constant_pool);
    166        case Const.ATTR_ENCLOSING_METHOD:
    167            return new EnclosingMethod(name_index, length, file, constant_pool);
    168        case Const.ATTR_STACK_MAP_TABLE:
    169            // read new style stack map: StackMapTable. The rest of the code
    170            // calls this a StackMap for historical reasons.
    171            return new StackMap(name_index, length, file, constant_pool);
    172        case Const.ATTR_BOOTSTRAP_METHODS:
    173            return new BootstrapMethods(name_index, length, file, constant_pool);
    174        case Const.ATTR_METHOD_PARAMETERS:
    175            return new MethodParameters(name_index, length, file, constant_pool);
    176        case Const.ATTR_MODULE:
    177            return new Module(name_index, length, file, constant_pool);
    178        case Const.ATTR_MODULE_PACKAGES:
    179            return new ModulePackages(name_index, length, file, constant_pool);
    180        case Const.ATTR_MODULE_MAIN_CLASS:
    181            return new ModuleMainClass(name_index, length, file, constant_pool);
    182        case Const.ATTR_NEST_HOST:
    183            return new NestHost(name_index, length, file, constant_pool);
    184        case Const.ATTR_NEST_MEMBERS:
    185            return new NestMembers(name_index, length, file, constant_pool);
    186        default:
    187            // Never reached
    188            throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag);
    189        }
    190    }
    191
    192    /**
    193     * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
    194     * is called by the Field and Method constructor methods.
    195     *
    196     * @see Field
    197     * @see Method
    198     *
    199     * @param file Input stream
    200     * @param constant_pool Array of constants
    201     * @return Attribute
    202     * @throws IOException if an I/O error occurs.
    203     */
    204    public static Attribute readAttribute(final DataInputStream file, final ConstantPool constant_pool) throws IOException {
    205        return readAttribute((DataInput) file, constant_pool);
    206    }
    207
    208    /**
    209     * Remove attribute reader
    210     *
    211     * @param name the name of the attribute as stored in the class file
    212     */
    213    public static void removeAttributeReader(final String name) {
    214        READERS.remove(name);
    215    }
    216
    217    /**
    218     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    219     */
    220    @java.lang.Deprecated
    221    protected int name_index; // Points to attribute name in constant pool TODO make private (has getter & setter)
    222
    223    /**
    224     * @deprecated (since 6.0) (since 6.0) will be made private; do not access directly, use getter/setter
    225     */
    226    @java.lang.Deprecated
    227    protected int length; // Content length of attribute field TODO make private (has getter & setter)
    228
    229    /**
    230     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    231     */
    232    @java.lang.Deprecated
    233    protected byte tag; // Tag to distinguish subclasses TODO make private & final; supposed to be immutable
    234
    235    /**
    236     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    237     */
    238    @java.lang.Deprecated
    239    protected ConstantPool constant_pool; // TODO make private (has getter & setter)
    240
    241    protected Attribute(final byte tag, final int name_index, final int length, final ConstantPool constant_pool) {
    242        this.tag = tag;
    243        this.name_index = name_index;
    244        this.length = length;
    245        this.constant_pool = constant_pool;
    246    }
    247
    248    /**
    249     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
    250     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
    251     *
    252     * @param v Visitor object
    253     */
    254    @Override
    255    public abstract void accept(Visitor v);
    256
    257    /**
    258     * Use copy() if you want to have a deep copy(), i.e., with all references copied correctly.
    259     *
    260     * @return shallow copy of this attribute
    261     */
    262    @Override
    263    public Object clone() {
    264        Attribute attr = null;
    265        try {
    266            attr = (Attribute) super.clone();
    267        } catch (final CloneNotSupportedException e) {
    268            throw new Error("Clone Not Supported"); // never happens
    269        }
    270        return attr;
    271    }
    272
    273    /**
    274     * @return deep copy of this attribute
    275     */
    276    public abstract Attribute copy(ConstantPool constantPool);
    277
    278    /**
    279     * Dump attribute to file stream in binary format.
    280     *
    281     * @param file Output file stream
    282     * @throws IOException if an I/O error occurs.
    283     */
    284    public void dump(final DataOutputStream file) throws IOException {
    285        file.writeShort(name_index);
    286        file.writeInt(length);
    287    }
    288
    289    /**
    290     * @return Constant pool used by this object.
    291     * @see ConstantPool
    292     */
    293    public final ConstantPool getConstantPool() {
    294        return constant_pool;
    295    }
    296
    297    /**
    298     * @return Length of attribute field in bytes.
    299     */
    300    public final int getLength() {
    301        return length;
    302    }
    303
    304    /**
    305     * @return Name of attribute
    306     * @since 6.0
    307     */
    308    public String getName() {
    309        return constant_pool.getConstantUtf8(name_index).getBytes();
    310    }
    311
    312    /**
    313     * @return Name index in constant pool of attribute name.
    314     */
    315    public final int getNameIndex() {
    316        return name_index;
    317    }
    318
    319    /**
    320     * @return Tag of attribute, i.e., its type. Value may not be altered, thus there is no setTag() method.
    321     */
    322    public final byte getTag() {
    323        return tag;
    324    }
    325
    326    /**
    327     * @param constant_pool Constant pool to be used for this object.
    328     * @see ConstantPool
    329     */
    330    public final void setConstantPool(final ConstantPool constant_pool) {
    331        this.constant_pool = constant_pool;
    332    }
    333
    334    /**
    335     * @param length length in bytes.
    336     */
    337    public final void setLength(final int length) {
    338        this.length = length;
    339    }
    340
    341    /**
    342     * @param name_index of attribute.
    343     */
    344    public final void setNameIndex(final int name_index) {
    345        this.name_index = name_index;
    346    }
    347
    348    /**
    349     * @return attribute name.
    350     */
    351    @Override
    352    public String toString() {
    353        return Const.getAttributeName(tag);
    354    }
    355}