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