View Javadoc
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  package org.apache.bcel.classfile;
18  
19  import java.io.DataInput;
20  import java.io.DataInputStream;
21  import java.io.DataOutputStream;
22  import java.io.IOException;
23  import java.util.HashMap;
24  import java.util.Map;
25  
26  import org.apache.bcel.Const;
27  import org.apache.bcel.util.Args;
28  
29  /**
30   * Abstract super class for <em>Attribute</em> objects. Currently the <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>, <em>Exceptiontable</em>,
31   * <em>LineNumberTable</em>, <em>LocalVariableTable</em>, <em>InnerClasses</em> and <em>Synthetic</em> attributes are supported. The <em>Unknown</em> attribute
32   * stands for non-standard-attributes.
33   *
34   * <pre>
35   * attribute_info {
36   *   u2 attribute_name_index;
37   *   u4 attribute_length;
38   *   u1 info[attribute_length];
39   * }
40   * </pre>
41   *
42   * @see ConstantValue
43   * @see SourceFile
44   * @see Code
45   * @see Unknown
46   * @see ExceptionTable
47   * @see LineNumberTable
48   * @see LocalVariableTable
49   * @see InnerClasses
50   * @see Synthetic
51   * @see Deprecated
52   * @see Signature
53   */
54  public abstract class Attribute implements Cloneable, Node {
55  
56      private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off
57  
58      private static final Map<String, Object> READERS = new HashMap<>();
59  
60      /**
61       * Empty array.
62       *
63       * @since 6.6.0
64       */
65      public static final Attribute[] EMPTY_ARRAY = {};
66  
67      /**
68       * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
69       * standard attributes such as "LineNumberTable", because those are handled internally.
70       *
71       * @param name the name of the attribute as stored in the class file
72       * @param attributeReader the reader object
73       * @deprecated (6.0) Use {@link #addAttributeReader(String, UnknownAttributeReader)} instead
74       */
75      @java.lang.Deprecated
76      public static void addAttributeReader(final String name, final AttributeReader attributeReader) {
77          READERS.put(name, attributeReader);
78      }
79  
80      /**
81       * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
82       * standard attributes such as "LineNumberTable", because those are handled internally.
83       *
84       * @param name the name of the attribute as stored in the class file
85       * @param unknownAttributeReader the reader object
86       */
87      public static void addAttributeReader(final String name, final UnknownAttributeReader unknownAttributeReader) {
88          READERS.put(name, unknownAttributeReader);
89      }
90  
91      protected static void println(final String msg) {
92          if (debug) {
93              System.err.println(msg);
94          }
95      }
96  
97      /**
98       * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
99       * 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 }