1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  */
18 package org.apache.bcel.classfile;
19 
20 import java.io.DataInput;
21 import java.io.DataInputStream;
22 import java.io.DataOutputStream;
23 import java.io.IOException;
24 import java.util.HashMap;
25 import java.util.Map;
26 
27 import org.apache.bcel.Const;
28 
29 /**
30  * Abstract super class for <em>Attribute</em> objects. Currently the
31  * <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>,
32  * <em>Exceptiontable</em>, <em>LineNumberTable</em>,
33  * <em>LocalVariableTable</em>, <em>InnerClasses</em> and
34  * <em>Synthetic</em> attributes are supported. The <em>Unknown</em>
35  * attribute stands for non-standard-attributes.
36  *
37  * @version $Id$
38  * @see ConstantValue
39  * @see SourceFile
40  * @see Code
41  * @see Unknown
42  * @see ExceptionTable
43  * @see LineNumberTable
44  * @see LocalVariableTable
45  * @see InnerClasses
46  * @see Synthetic
47  * @see Deprecated
48  * @see Signature
49  */
50 public abstract class Attribute implements Cloneable, Node {
51 
52     /**
53      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
54      */
55     @java.lang.Deprecated
56     protected int name_index; // Points to attribute name in constant pool TODO make private (has getter & setter)
57 
58     /**
59      * @deprecated (since 6.0) (since 6.0) will be made private; do not access directly, use getter/setter
60      */
61     @java.lang.Deprecated
62     protected int length; // Content length of attribute field TODO make private (has getter & setter)
63 
64     /**
65      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
66      */
67     @java.lang.Deprecated
68     protected byte tag; // Tag to distinguish subclasses TODO make private & final; supposed to be immutable
69 
70     /**
71      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
72      */
73     @java.lang.Deprecated
74     protected ConstantPool constant_pool; // TODO make private (has getter & setter)
75 
Attribute(final byte tag, final int name_index, final int length, final ConstantPool constant_pool)76     protected Attribute(final byte tag, final int name_index, final int length, final ConstantPool constant_pool)
77     {
78         this.tag = tag;
79         this.name_index = name_index;
80         this.length = length;
81         this.constant_pool = constant_pool;
82     }
83 
84     /**
85      * Called by objects that are traversing the nodes of the tree implicitely
86      * defined by the contents of a Java class. I.e., the hierarchy of methods,
87      * fields, attributes, etc. spawns a tree of objects.
88      *
89      * @param v
90      *            Visitor object
91      */
92     @Override
accept(Visitor v)93     public abstract void accept(Visitor v);
94 
95     /**
96      * Dump attribute to file stream in binary format.
97      *
98      * @param file
99      *            Output file stream
100      * @throws IOException
101      */
dump(final DataOutputStream file)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
addAttributeReader(final String name, final AttributeReader r)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      */
addAttributeReader(final String name, final UnknownAttributeReader r)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      */
removeAttributeReader(final String name)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      */
readAttribute(final DataInputStream file, final ConstantPool constant_pool)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      */
readAttribute(final DataInput file, final ConstantPool constant_pool)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      */
getName()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      */
getLength()283     public final int getLength()
284     {
285         return length;
286     }
287 
288     /**
289      * @param length length in bytes.
290      */
setLength(final int length)291     public final void setLength(final int length)
292     {
293         this.length = length;
294     }
295 
296     /**
297      * @param name_index of attribute.
298      */
setNameIndex(final int name_index)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      */
getNameIndex()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      */
getTag()315     public final byte getTag()
316     {
317         return tag;
318     }
319 
320     /**
321      * @return Constant pool used by this object.
322      * @see ConstantPool
323      */
getConstantPool()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      */
setConstantPool(final ConstantPool constant_pool)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
clone()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      */
copy(ConstantPool _constant_pool)362     public abstract Attribute copy(ConstantPool _constant_pool);
363 
364     /**
365      * @return attribute name.
366      */
367     @Override
toString()368     public String toString()
369     {
370         return Const.getAttributeName(tag);
371     }
372 }
373