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}