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 * 017 */ 018package org.apache.bcel.classfile; 019 020import java.io.DataInput; 021import java.io.DataInputStream; 022import java.io.DataOutputStream; 023import java.io.IOException; 024import java.util.HashMap; 025import java.util.Map; 026 027import org.apache.bcel.Const; 028 029/** 030 * Abstract super class for <em>Attribute</em> objects. Currently the 031 * <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>, 032 * <em>Exceptiontable</em>, <em>LineNumberTable</em>, 033 * <em>LocalVariableTable</em>, <em>InnerClasses</em> and 034 * <em>Synthetic</em> attributes are supported. The <em>Unknown</em> 035 * attribute stands for non-standard-attributes. 036 * 037 * @see ConstantValue 038 * @see SourceFile 039 * @see Code 040 * @see Unknown 041 * @see ExceptionTable 042 * @see LineNumberTable 043 * @see LocalVariableTable 044 * @see InnerClasses 045 * @see Synthetic 046 * @see Deprecated 047 * @see Signature 048 */ 049public abstract class Attribute implements Cloneable, Node { 050 051 private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off 052 053 private static final Map<String, Object> readers = new HashMap<>(); 054 055 /** 056 * Add an Attribute reader capable of parsing (user-defined) attributes 057 * named "name". You should not add readers for the standard attributes such 058 * as "LineNumberTable", because those are handled internally. 059 * 060 * @param name the name of the attribute as stored in the class file 061 * @param r the reader object 062 * @deprecated (6.0) Use {@link #addAttributeReader(String, UnknownAttributeReader)} instead 063 */ 064 @java.lang.Deprecated 065 public static void addAttributeReader(final String name, final AttributeReader r) 066 { 067 readers.put(name, r); 068 } 069 070 /** 071 * Add an Attribute reader capable of parsing (user-defined) attributes 072 * named "name". You should not add readers for the standard attributes such 073 * as "LineNumberTable", because those are handled internally. 074 * 075 * @param name the name of the attribute as stored in the class file 076 * @param r the reader object 077 */ 078 public static void addAttributeReader(final String name, final UnknownAttributeReader r) 079 { 080 readers.put(name, r); 081 } 082 083 protected static void println(final String msg) { 084 if (debug) { 085 System.err.println(msg); 086 } 087 } 088 089 /** 090 * Class method reads one attribute from the input data stream. This method 091 * must not be accessible from the outside. It is called by the Field and 092 * Method constructor methods. 093 * 094 * @see Field 095 * @see Method 096 * 097 * @param file Input stream 098 * @param constant_pool Array of constants 099 * @return Attribute 100 * @throws IOException 101 * @throws ClassFormatException 102 * @since 6.0 103 */ 104 public static Attribute readAttribute(final DataInput file, final ConstantPool constant_pool) 105 throws IOException, ClassFormatException 106 { 107 byte tag = Const.ATTR_UNKNOWN; // Unknown attribute 108 // Get class name from constant pool via `name_index' indirection 109 final int name_index = file.readUnsignedShort(); 110 final ConstantUtf8 c = (ConstantUtf8) constant_pool.getConstant(name_index, Const.CONSTANT_Utf8); 111 final String name = c.getBytes(); 112 113 // Length of data in bytes 114 final int length = file.readInt(); 115 116 // Compare strings to find known attribute 117 for (byte i = 0; i < Const.KNOWN_ATTRIBUTES; i++) 118 { 119 if (name.equals(Const.getAttributeName(i))) 120 { 121 tag = i; // found! 122 break; 123 } 124 } 125 126 // Call proper constructor, depending on `tag' 127 switch (tag) 128 { 129 case Const.ATTR_UNKNOWN: 130 final Object r = readers.get(name); 131 if (r instanceof UnknownAttributeReader) 132 { 133 return ((UnknownAttributeReader) r).createAttribute(name_index, length, file, constant_pool); 134 } 135 return new Unknown(name_index, length, file, constant_pool); 136 case Const.ATTR_CONSTANT_VALUE: 137 return new ConstantValue(name_index, length, file, constant_pool); 138 case Const.ATTR_SOURCE_FILE: 139 return new SourceFile(name_index, length, file, constant_pool); 140 case Const.ATTR_CODE: 141 return new Code(name_index, length, file, constant_pool); 142 case Const.ATTR_EXCEPTIONS: 143 return new ExceptionTable(name_index, length, file, constant_pool); 144 case Const.ATTR_LINE_NUMBER_TABLE: 145 return new LineNumberTable(name_index, length, file, constant_pool); 146 case Const.ATTR_LOCAL_VARIABLE_TABLE: 147 return new LocalVariableTable(name_index, length, file, constant_pool); 148 case Const.ATTR_INNER_CLASSES: 149 return new InnerClasses(name_index, length, file, constant_pool); 150 case Const.ATTR_SYNTHETIC: 151 return new Synthetic(name_index, length, file, constant_pool); 152 case Const.ATTR_DEPRECATED: 153 return new Deprecated(name_index, length, file, constant_pool); 154 case Const.ATTR_PMG: 155 return new PMGClass(name_index, length, file, constant_pool); 156 case Const.ATTR_SIGNATURE: 157 return new Signature(name_index, length, file, constant_pool); 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(name_index, length, file, constant_pool); 163 case Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS: 164 return new RuntimeVisibleAnnotations(name_index, length, file, constant_pool); 165 case Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS: 166 return new RuntimeInvisibleAnnotations(name_index, length, file, constant_pool); 167 case Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS: 168 return new RuntimeVisibleParameterAnnotations(name_index, length, file, constant_pool); 169 case Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS: 170 return new RuntimeInvisibleParameterAnnotations(name_index, length, file, constant_pool); 171 case Const.ATTR_ANNOTATION_DEFAULT: 172 return new AnnotationDefault(name_index, length, file, constant_pool); 173 case Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE: 174 return new LocalVariableTypeTable(name_index, length, file, constant_pool); 175 case Const.ATTR_ENCLOSING_METHOD: 176 return new EnclosingMethod(name_index, length, file, constant_pool); 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(name_index, length, file, constant_pool); 181 case Const.ATTR_BOOTSTRAP_METHODS: 182 return new BootstrapMethods(name_index, length, file, constant_pool); 183 case Const.ATTR_METHOD_PARAMETERS: 184 return new MethodParameters(name_index, length, file, constant_pool); 185 case Const.ATTR_MODULE: 186 return new Module(name_index, length, file, constant_pool); 187 case Const.ATTR_MODULE_PACKAGES: 188 return new ModulePackages(name_index, length, file, constant_pool); 189 case Const.ATTR_MODULE_MAIN_CLASS: 190 return new ModuleMainClass(name_index, length, file, constant_pool); 191 case Const.ATTR_NEST_HOST: 192 return new NestHost(name_index, length, file, constant_pool); 193 case Const.ATTR_NEST_MEMBERS: 194 return new NestMembers(name_index, length, file, constant_pool); 195 default: 196 // Never reached 197 throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag); 198 } 199 } 200 201 /** 202 * Class method reads one attribute from the input data stream. This method 203 * must not be accessible from the outside. It is called by the Field and 204 * Method constructor methods. 205 * 206 * @see Field 207 * @see Method 208 * 209 * @param file Input stream 210 * @param constant_pool Array of constants 211 * @return Attribute 212 * @throws IOException 213 * @throws ClassFormatException 214 */ 215 public static Attribute readAttribute(final DataInputStream file, final ConstantPool constant_pool) 216 throws IOException, ClassFormatException 217 { 218 return readAttribute((DataInput) file, constant_pool); 219 } 220 221 /** 222 * Remove attribute reader 223 * 224 * @param name the name of the attribute as stored in the class file 225 */ 226 public static void removeAttributeReader(final String name) 227 { 228 readers.remove(name); 229 } 230 231 /** 232 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 233 */ 234 @java.lang.Deprecated 235 protected int name_index; // Points to attribute name in constant pool TODO make private (has getter & setter) 236 237 /** 238 * @deprecated (since 6.0) (since 6.0) will be made private; do not access directly, use getter/setter 239 */ 240 @java.lang.Deprecated 241 protected int length; // Content length of attribute field TODO make private (has getter & setter) 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 byte tag; // Tag to distinguish subclasses TODO make private & final; supposed to be immutable 248 249 /** 250 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 251 */ 252 @java.lang.Deprecated 253 protected ConstantPool constant_pool; // TODO make private (has getter & setter) 254 255 protected Attribute(final byte tag, final int name_index, final int length, final ConstantPool constant_pool) 256 { 257 this.tag = tag; 258 this.name_index = name_index; 259 this.length = length; 260 this.constant_pool = constant_pool; 261 } 262 263 /** 264 * Called by objects that are traversing the nodes of the tree implicitely 265 * defined by the contents of a Java class. I.e., the hierarchy of methods, 266 * fields, attributes, etc. spawns a tree of objects. 267 * 268 * @param v 269 * Visitor object 270 */ 271 @Override 272 public abstract void accept(Visitor v); 273 274 /** 275 * Use copy() if you want to have a deep copy(), i.e., with all references 276 * copied correctly. 277 * 278 * @return shallow copy of this attribute 279 */ 280 @Override 281 public Object clone() 282 { 283 Attribute attr = null; 284 try 285 { 286 attr = (Attribute) super.clone(); 287 } 288 catch (final CloneNotSupportedException e) 289 { 290 throw new Error("Clone Not Supported"); // never happens 291 } 292 return attr; 293 } 294 295 /** 296 * @return deep copy of this attribute 297 */ 298 public abstract Attribute copy(ConstantPool _constant_pool); 299 300 /** 301 * Dump attribute to file stream in binary format. 302 * 303 * @param file 304 * Output file stream 305 * @throws IOException 306 */ 307 public void dump(final DataOutputStream file) throws IOException 308 { 309 file.writeShort(name_index); 310 file.writeInt(length); 311 } 312 313 /** 314 * @return Constant pool used by this object. 315 * @see ConstantPool 316 */ 317 public final ConstantPool getConstantPool() 318 { 319 return constant_pool; 320 } 321 322 /** 323 * @return Length of attribute field in bytes. 324 */ 325 public final int getLength() 326 { 327 return length; 328 } 329 330 /** 331 * @return Name of attribute 332 * @since 6.0 333 */ 334 public String getName() 335 { 336 final ConstantUtf8 c = (ConstantUtf8) constant_pool.getConstant(name_index, Const.CONSTANT_Utf8); 337 return c.getBytes(); 338 } 339 340 /** 341 * @return Name index in constant pool of attribute name. 342 */ 343 public final int getNameIndex() 344 { 345 return name_index; 346 } 347 348 /** 349 * @return Tag of attribute, i.e., its type. Value may not be altered, thus there is no setTag() method. 350 */ 351 public final byte getTag() 352 { 353 return tag; 354 } 355 356 /** 357 * @param constant_pool Constant pool to be used for this object. 358 * @see ConstantPool 359 */ 360 public final void setConstantPool(final ConstantPool constant_pool) 361 { 362 this.constant_pool = constant_pool; 363 } 364 365 /** 366 * @param length length in bytes. 367 */ 368 public final void setLength(final int length) 369 { 370 this.length = length; 371 } 372 373 /** 374 * @param name_index of attribute. 375 */ 376 public final void setNameIndex(final int name_index) 377 { 378 this.name_index = name_index; 379 } 380 381 /** 382 * @return attribute name. 383 */ 384 @Override 385 public String toString() 386 { 387 return Const.getAttributeName(tag); 388 } 389}