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