View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.bcel.classfile;
20  
21  import java.io.DataInput;
22  import java.io.DataInputStream;
23  import java.io.DataOutputStream;
24  import java.io.IOException;
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  import org.apache.bcel.Const;
29  import org.apache.bcel.util.Args;
30  
31  /**
32   * Abstract super class for <em>Attribute</em> objects. Currently the <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>, <em>Exceptiontable</em>,
33   * <em>LineNumberTable</em>, <em>LocalVariableTable</em>, <em>InnerClasses</em> and <em>Synthetic</em> attributes are supported. The <em>Unknown</em> attribute
34   * stands for non-standard-attributes.
35   *
36   * <pre>
37   * attribute_info {
38   *   u2 attribute_name_index;
39   *   u4 attribute_length;
40   *   u1 info[attribute_length];
41   * }
42   * </pre>
43   *
44   * @see ConstantValue
45   * @see SourceFile
46   * @see Code
47   * @see Unknown
48   * @see ExceptionTable
49   * @see LineNumberTable
50   * @see LocalVariableTable
51   * @see InnerClasses
52   * @see Synthetic
53   * @see Deprecated
54   * @see Signature
55   */
56  public abstract class Attribute implements Cloneable, Node {
57  
58      private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off
59  
60      private static final Map<String, Object> READERS = new HashMap<>();
61  
62      /**
63       * Empty array.
64       *
65       * @since 6.6.0
66       */
67      public static final Attribute[] EMPTY_ARRAY = {};
68  
69      /**
70       * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
71       * standard attributes such as "LineNumberTable", because those are handled internally.
72       *
73       * @param name the name of the attribute as stored in the class file.
74       * @param attributeReader the reader object.
75       * @deprecated (6.0) Use {@link #addAttributeReader(String, UnknownAttributeReader)} instead.
76       */
77      @java.lang.Deprecated
78      public static void addAttributeReader(final String name, final AttributeReader attributeReader) {
79          READERS.put(name, attributeReader);
80      }
81  
82      /**
83       * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
84       * standard attributes such as "LineNumberTable", because those are handled internally.
85       *
86       * @param name the name of the attribute as stored in the class file.
87       * @param unknownAttributeReader the reader object.
88       */
89      public static void addAttributeReader(final String name, final UnknownAttributeReader unknownAttributeReader) {
90          READERS.put(name, unknownAttributeReader);
91      }
92  
93      /**
94       * Prints a message to stderr if debug mode is enabled.
95       *
96       * @param msg the message to print.
97       */
98      protected static void println(final String msg) {
99          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 }