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