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         default:
195             // Never reached
196             throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag);
197         }
198     }
199 
200     /**
201      * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
202      * is called by the Field and Method constructor methods.
203      *
204      * @see Field
205      * @see Method
206      *
207      * @param dataInputStream Input stream
208      * @param constantPool Array of constants
209      * @return Attribute
210      * @throws IOException if an I/O error occurs.
211      */
212     public static Attribute readAttribute(final DataInputStream dataInputStream, final ConstantPool constantPool) throws IOException {
213         return readAttribute((DataInput) dataInputStream, constantPool);
214     }
215 
216     /**
217      * Remove attribute reader
218      *
219      * @param name the name of the attribute as stored in the class file
220      */
221     public static void removeAttributeReader(final String name) {
222         READERS.remove(name);
223     }
224 
225     /**
226      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
227      */
228     @java.lang.Deprecated
229     protected int name_index; // Points to attribute name in constant pool TODO make private (has getter & setter)
230 
231     /**
232      * @deprecated (since 6.0) (since 6.0) will be made private; do not access directly, use getter/setter
233      */
234     @java.lang.Deprecated
235     protected int length; // Content length of attribute field TODO make private (has getter & setter)
236 
237     /**
238      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
239      */
240     @java.lang.Deprecated
241     protected byte tag; // Tag to distinguish subclasses TODO make private & final; supposed to be immutable
242 
243     /**
244      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
245      */
246     @java.lang.Deprecated
247     protected ConstantPool constant_pool; // TODO make private (has getter & setter)
248 
249     /**
250      * Constructs an instance.
251      *
252      * <pre>
253      * attribute_info {
254      *   u2 attribute_name_index;
255      *   u4 attribute_length;
256      *   u1 info[attribute_length];
257      * }
258      * </pre>
259      *
260      * @param tag tag.
261      * @param nameIndex u2 name index.
262      * @param length u4 length.
263      * @param constantPool constant pool.
264      */
265     protected Attribute(final byte tag, final int nameIndex, final int length, final ConstantPool constantPool) {
266         this.tag = tag;
267         this.name_index = Args.requireU2(nameIndex, 0, constantPool.getLength(), getClass().getSimpleName() + " name index");
268         this.length = Args.requireU4(length, getClass().getSimpleName() + " attribute length");
269         this.constant_pool = constantPool;
270     }
271 
272     /**
273      * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
274      * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
275      *
276      * @param v Visitor object
277      */
278     @Override
279     public abstract void accept(Visitor v);
280 
281     /**
282      * Use copy() if you want to have a deep copy(), i.e., with all references copied correctly.
283      *
284      * @return shallow copy of this attribute
285      */
286     @Override
287     public Object clone() {
288         Attribute attr = null;
289         try {
290             attr = (Attribute) super.clone();
291         } catch (final CloneNotSupportedException e) {
292             throw new UnsupportedOperationException("Clone Not Supported", e); // never happens
293         }
294         return attr;
295     }
296 
297     /**
298      * @param constantPool constant pool to save.
299      * @return deep copy of this attribute.
300      */
301     public abstract Attribute copy(ConstantPool constantPool);
302 
303     /**
304      * Dumps attribute to file stream in binary format.
305      *
306      * @param file Output file stream
307      * @throws IOException if an I/O error occurs.
308      */
309     public void dump(final DataOutputStream file) throws IOException {
310         file.writeShort(name_index);
311         file.writeInt(length);
312     }
313 
314     /**
315      * @return Constant pool used by this object.
316      * @see ConstantPool
317      */
318     public final ConstantPool getConstantPool() {
319         return constant_pool;
320     }
321 
322     /**
323      * @return Length of attribute field in bytes.
324      */
325     public final int getLength() {
326         return length;
327     }
328 
329     /**
330      * @return Name of attribute
331      * @since 6.0
332      */
333     public String getName() {
334         return constant_pool.getConstantUtf8(name_index).getBytes();
335     }
336 
337     /**
338      * @return Name index in constant pool of attribute name.
339      */
340     public final int getNameIndex() {
341         return name_index;
342     }
343 
344     /**
345      * @return Tag of attribute, i.e., its type. Value may not be altered, thus there is no setTag() method.
346      */
347     public final byte getTag() {
348         return tag;
349     }
350 
351     /**
352      * @param constantPool Constant pool to be used for this object.
353      * @see ConstantPool
354      */
355     public final void setConstantPool(final ConstantPool constantPool) {
356         this.constant_pool = constantPool;
357     }
358 
359     /**
360      * @param length length in bytes.
361      */
362     public final void setLength(final int length) {
363         this.length = length;
364     }
365 
366     /**
367      * @param nameIndex of attribute.
368      */
369     public final void setNameIndex(final int nameIndex) {
370         this.name_index = nameIndex;
371     }
372 
373     /**
374      * @return attribute name.
375      */
376     @Override
377     public String toString() {
378         return Const.getAttributeName(tag);
379     }
380 }