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      protected static void println(final String msg) {
94          if (debug) {
95              System.err.println(msg);
96          }
97      }
98  
99      /**
100      * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
101      * is called by the Field and Method constructor methods.
102      *
103      * @see Field
104      * @see Method
105      * @param dataInput Input stream
106      * @param constantPool Array of constants
107      * @return Attribute
108      * @throws IOException if an I/O error occurs.
109      * @since 6.0
110      */
111     public static Attribute readAttribute(final DataInput dataInput, final ConstantPool constantPool) throws IOException {
112         byte tag = Const.ATTR_UNKNOWN; // Unknown attribute
113         // Get class name from constant pool via 'name_index' indirection
114         final int nameIndex = dataInput.readUnsignedShort();
115         final String name = constantPool.getConstantUtf8(nameIndex).getBytes();
116 
117         // Length of data in bytes
118         final int length = dataInput.readInt();
119 
120         // Compare strings to find known attribute
121         for (byte i = 0; i < Const.KNOWN_ATTRIBUTES; i++) {
122             if (name.equals(Const.getAttributeName(i))) {
123                 tag = i; // found!
124                 break;
125             }
126         }
127 
128         // Call proper constructor, depending on 'tag'
129         switch (tag) {
130         case Const.ATTR_UNKNOWN:
131             final Object r = READERS.get(name);
132             if (r instanceof UnknownAttributeReader) {
133                 return ((UnknownAttributeReader) r).createAttribute(nameIndex, length, dataInput, constantPool);
134             }
135             return new Unknown(nameIndex, length, dataInput, constantPool);
136         case Const.ATTR_CONSTANT_VALUE:
137             return new ConstantValue(nameIndex, length, dataInput, constantPool);
138         case Const.ATTR_SOURCE_FILE:
139             return new SourceFile(nameIndex, length, dataInput, constantPool);
140         case Const.ATTR_CODE:
141             return new Code(nameIndex, length, dataInput, constantPool);
142         case Const.ATTR_EXCEPTIONS:
143             return new ExceptionTable(nameIndex, length, dataInput, constantPool);
144         case Const.ATTR_LINE_NUMBER_TABLE:
145             return new LineNumberTable(nameIndex, length, dataInput, constantPool);
146         case Const.ATTR_LOCAL_VARIABLE_TABLE:
147             return new LocalVariableTable(nameIndex, length, dataInput, constantPool);
148         case Const.ATTR_INNER_CLASSES:
149             return new InnerClasses(nameIndex, length, dataInput, constantPool);
150         case Const.ATTR_SYNTHETIC:
151             return new Synthetic(nameIndex, length, dataInput, constantPool);
152         case Const.ATTR_DEPRECATED:
153             return new Deprecated(nameIndex, length, dataInput, constantPool);
154         case Const.ATTR_PMG:
155             return new PMGClass(nameIndex, length, dataInput, constantPool);
156         case Const.ATTR_SIGNATURE:
157             return new Signature(nameIndex, length, dataInput, constantPool);
158         case Const.ATTR_STACK_MAP:
159             // old style stack map: unneeded for JDK5 and below;
160             // illegal(?) for JDK6 and above. So just delete with a warning.
161             println("Warning: Obsolete StackMap attribute ignored.");
162             return new Unknown(nameIndex, length, dataInput, constantPool);
163         case Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS:
164             return new RuntimeVisibleAnnotations(nameIndex, length, dataInput, constantPool);
165         case Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS:
166             return new RuntimeInvisibleAnnotations(nameIndex, length, dataInput, constantPool);
167         case Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
168             return new RuntimeVisibleParameterAnnotations(nameIndex, length, dataInput, constantPool);
169         case Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
170             return new RuntimeInvisibleParameterAnnotations(nameIndex, length, dataInput, constantPool);
171         case Const.ATTR_ANNOTATION_DEFAULT:
172             return new AnnotationDefault(nameIndex, length, dataInput, constantPool);
173         case Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE:
174             return new LocalVariableTypeTable(nameIndex, length, dataInput, constantPool);
175         case Const.ATTR_ENCLOSING_METHOD:
176             return new EnclosingMethod(nameIndex, length, dataInput, constantPool);
177         case Const.ATTR_STACK_MAP_TABLE:
178             // read new style stack map: StackMapTable. The rest of the code
179             // calls this a StackMap for historical reasons.
180             return new StackMap(nameIndex, length, dataInput, constantPool);
181         case Const.ATTR_BOOTSTRAP_METHODS:
182             return new BootstrapMethods(nameIndex, length, dataInput, constantPool);
183         case Const.ATTR_METHOD_PARAMETERS:
184             return new MethodParameters(nameIndex, length, dataInput, constantPool);
185         case Const.ATTR_MODULE:
186             return new Module(nameIndex, length, dataInput, constantPool);
187         case Const.ATTR_MODULE_PACKAGES:
188             return new ModulePackages(nameIndex, length, dataInput, constantPool);
189         case Const.ATTR_MODULE_MAIN_CLASS:
190             return new ModuleMainClass(nameIndex, length, dataInput, constantPool);
191         case Const.ATTR_NEST_HOST:
192             return new NestHost(nameIndex, length, dataInput, constantPool);
193         case Const.ATTR_NEST_MEMBERS:
194             return new NestMembers(nameIndex, length, dataInput, constantPool);
195         case Const.ATTR_RECORD:
196             return new Record(nameIndex, length, dataInput, constantPool);
197         default:
198             // Never reached
199             throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag);
200         }
201     }
202 
203     /**
204      * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
205      * is called by the Field and Method constructor methods.
206      *
207      * @see Field
208      * @see Method
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 }