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