001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.bcel.classfile; 020 021import java.io.DataInput; 022import java.io.DataInputStream; 023import java.io.DataOutputStream; 024import java.io.IOException; 025import java.util.HashMap; 026import java.util.Map; 027 028import org.apache.bcel.Const; 029import org.apache.bcel.util.Args; 030 031/** 032 * Abstract super class for <em>Attribute</em> objects. Currently the <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>, <em>Exceptiontable</em>, 033 * <em>LineNumberTable</em>, <em>LocalVariableTable</em>, <em>InnerClasses</em> and <em>Synthetic</em> attributes are supported. The <em>Unknown</em> attribute 034 * stands for non-standard-attributes. 035 * 036 * <pre> 037 * attribute_info { 038 * u2 attribute_name_index; 039 * u4 attribute_length; 040 * u1 info[attribute_length]; 041 * } 042 * </pre> 043 * 044 * @see ConstantValue 045 * @see SourceFile 046 * @see Code 047 * @see Unknown 048 * @see ExceptionTable 049 * @see LineNumberTable 050 * @see LocalVariableTable 051 * @see InnerClasses 052 * @see Synthetic 053 * @see Deprecated 054 * @see Signature 055 */ 056public abstract class Attribute implements Cloneable, Node { 057 058 private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off 059 060 private static final Map<String, Object> READERS = new HashMap<>(); 061 062 /** 063 * Empty array. 064 * 065 * @since 6.6.0 066 */ 067 public static final Attribute[] EMPTY_ARRAY = {}; 068 069 /** 070 * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the 071 * standard attributes such as "LineNumberTable", because those are handled internally. 072 * 073 * @param name the name of the attribute as stored in the class file. 074 * @param attributeReader the reader object. 075 * @deprecated (6.0) Use {@link #addAttributeReader(String, UnknownAttributeReader)} instead. 076 */ 077 @java.lang.Deprecated 078 public static void addAttributeReader(final String name, final AttributeReader attributeReader) { 079 READERS.put(name, attributeReader); 080 } 081 082 /** 083 * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the 084 * standard attributes such as "LineNumberTable", because those are handled internally. 085 * 086 * @param name the name of the attribute as stored in the class file. 087 * @param unknownAttributeReader the reader object. 088 */ 089 public static void addAttributeReader(final String name, final UnknownAttributeReader unknownAttributeReader) { 090 READERS.put(name, unknownAttributeReader); 091 } 092 093 /** 094 * Prints a message to stderr if debug mode is enabled. 095 * 096 * @param msg the message to print. 097 */ 098 protected static void println(final String msg) { 099 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}