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 }