1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 import java.io.File; 20 import java.io.IOException; 21 22 import javax.imageio.stream.FileImageInputStream; 23 24 import org.apache.bcel.Constants; 25 import org.apache.bcel.classfile.Attribute; 26 import org.apache.bcel.classfile.ClassFormatException; 27 import org.apache.bcel.classfile.Constant; 28 import org.apache.bcel.classfile.ConstantPool; 29 import org.apache.bcel.classfile.Field; 30 import org.apache.bcel.classfile.Method; 31 import org.apache.bcel.util.BCELifier; 32 33 /** 34 * Display Java .class file data. 35 * Output is based on javap tool. 36 * Built using the BCEL libary. 37 * 38 */ 39 40 41 class ClassDumper { 42 43 private FileImageInputStream file; 44 private String file_name; 45 private int class_name_index; 46 private int superclass_name_index; 47 private int major; 48 private int minor; // Compiler version 49 private int access_flags; // Access rights of parsed class 50 private int[] interfaces; // Names of implemented interfaces 51 private ConstantPool constant_pool; // collection of constants 52 private Constant[] constant_items; // collection of constants 53 private Field[] fields; // class fields, i.e., its variables 54 private Method[] methods; // methods defined in the class 55 private Attribute[] attributes; // attributes defined in the class 56 57 /** 58 * Parse class from the given stream. 59 * 60 * @param file Input stream 61 * @param file_name File name 62 */ ClassDumper(FileImageInputStream file, String file_name)63 public ClassDumper (FileImageInputStream file, String file_name) { 64 this.file_name = file_name; 65 this.file = file; 66 } 67 68 /** 69 * Parse the given Java class file and return an object that represents 70 * the contained data, i.e., constants, methods, fields and commands. 71 * A <em>ClassFormatException</em> is raised, if the file is not a valid 72 * .class file. (This does not include verification of the byte code as it 73 * is performed by the java interpreter). 74 * 75 * @throws IOException 76 * @throws ClassFormatException 77 */ dump()78 public void dump () throws IOException, ClassFormatException { 79 try { 80 // Check magic tag of class file 81 processID(); 82 // Get compiler version 83 processVersion(); 84 // process constant pool entries 85 processConstantPool(); 86 // Get class information 87 processClassInfo(); 88 // Get interface information, i.e., implemented interfaces 89 processInterfaces(); 90 // process class fields, i.e., the variables of the class 91 processFields(); 92 // process class methods, i.e., the functions in the class 93 processMethods(); 94 // process class attributes 95 processAttributes(); 96 } finally { 97 // Processed everything of interest, so close the file 98 try { 99 if (file != null) { 100 file.close(); 101 } 102 } catch (IOException ioe) { 103 //ignore close exceptions 104 } 105 } 106 } 107 108 /** 109 * Check whether the header of the file is ok. 110 * Of course, this has to be the first action on successive file reads. 111 * @throws IOException 112 * @throws ClassFormatException 113 */ processID()114 private final void processID () throws IOException, ClassFormatException { 115 final int magic = file.readInt(); 116 if (magic != Constants.JVM_CLASSFILE_MAGIC) { 117 throw new ClassFormatException(file_name + " is not a Java .class file"); 118 } 119 System.out.println("Java Class Dump"); 120 System.out.println(" file: " + file_name); 121 System.out.printf("%nClass header:%n"); 122 System.out.printf(" magic: %X%n", magic); 123 } 124 125 /** 126 * Process major and minor version of compiler which created the file. 127 * @throws IOException 128 * @throws ClassFormatException 129 */ processVersion()130 private final void processVersion () throws IOException, ClassFormatException { 131 minor = file.readUnsignedShort(); 132 System.out.printf(" minor version: %s%n", minor); 133 134 major = file.readUnsignedShort(); 135 System.out.printf(" major version: %s%n", major); 136 } 137 138 /** 139 * Process constant pool entries. 140 * @throws IOException 141 * @throws ClassFormatException 142 */ processConstantPool()143 private final void processConstantPool () throws IOException, ClassFormatException { 144 byte tag; 145 int constant_pool_count = file.readUnsignedShort(); 146 constant_items = new Constant[constant_pool_count]; 147 constant_pool = new ConstantPool(constant_items); 148 149 // constant_pool[0] is unused by the compiler 150 System.out.printf("%nConstant pool(%d):%n", constant_pool_count - 1); 151 152 for (int i = 1; i < constant_pool_count; i++) { 153 constant_items[i] = Constant.readConstant(file); 154 // i'm sure there is a better way to do this 155 if (i < 10) { 156 System.out.printf(" #%1d = ", i); 157 } else if (i <100) { 158 System.out.printf(" #%2d = ", i); 159 } else { 160 System.out.printf(" #%d = ", i); 161 } 162 System.out.println(constant_items[i]); 163 164 // All eight byte constants take up two spots in the constant pool 165 tag = constant_items[i].getTag(); 166 if ((tag == Constants.CONSTANT_Double) || 167 (tag == Constants.CONSTANT_Long)) { 168 i++; 169 } 170 } 171 } 172 173 /** 174 * Process information about the class and its super class. 175 * @throws IOException 176 * @throws ClassFormatException 177 */ processClassInfo()178 private final void processClassInfo () throws IOException, ClassFormatException { 179 access_flags = file.readUnsignedShort(); 180 /* Interfaces are implicitely abstract, the flag should be set 181 * according to the JVM specification. 182 */ 183 if ((access_flags & Constants.ACC_INTERFACE) != 0) { 184 access_flags |= Constants.ACC_ABSTRACT; 185 } 186 if (((access_flags & Constants.ACC_ABSTRACT) != 0) 187 && ((access_flags & Constants.ACC_FINAL) != 0)) { 188 throw new ClassFormatException("Class " + file_name + 189 " can't be both final and abstract"); 190 } 191 192 System.out.printf("%nClass info:%n"); 193 System.out.println(" flags: " + BCELifier.printFlags(access_flags, 194 BCELifier.FLAGS.CLASS)); 195 class_name_index = file.readUnsignedShort(); 196 System.out.printf(" this_class: %d (", class_name_index); 197 System.out.println(constantToString(class_name_index) + ")"); 198 199 superclass_name_index = file.readUnsignedShort(); 200 System.out.printf(" super_class: %d (", superclass_name_index); 201 System.out.println(constantToString(superclass_name_index) + ")"); 202 } 203 204 /** 205 * Process information about the interfaces implemented by this class. 206 * @throws IOException 207 * @throws ClassFormatException 208 */ processInterfaces()209 private final void processInterfaces () throws IOException, ClassFormatException { 210 int interfaces_count; 211 interfaces_count = file.readUnsignedShort(); 212 interfaces = new int[interfaces_count]; 213 214 System.out.printf("%nInterfaces(%d):%n", interfaces_count); 215 216 for (int i = 0; i < interfaces_count; i++) { 217 interfaces[i] = file.readUnsignedShort(); 218 // i'm sure there is a better way to do this 219 if (i < 10) { 220 System.out.printf(" #%1d = ", i); 221 } else if (i <100) { 222 System.out.printf(" #%2d = ", i); 223 } else { 224 System.out.printf(" #%d = ", i); 225 } 226 System.out.println(interfaces[i] + " (" + 227 constant_pool.getConstantString(interfaces[i], 228 Constants.CONSTANT_Class) + ")"); 229 } 230 } 231 232 /** 233 * Process information about the fields of the class, i.e., its variables. 234 * @throws IOException 235 * @throws ClassFormatException 236 */ processFields()237 private final void processFields () throws IOException, ClassFormatException { 238 int fields_count; 239 fields_count = file.readUnsignedShort(); 240 fields = new Field[fields_count]; 241 242 // sometimes fields[0] is magic used for serialization 243 System.out.printf("%nFields(%d):%n", fields_count); 244 245 for (int i = 0; i < fields_count; i++) { 246 processFieldOrMethod(); 247 if (i < fields_count - 1) { 248 System.out.println(); 249 } 250 } 251 } 252 253 /** 254 * Process information about the methods of the class. 255 * @throws IOException 256 * @throws ClassFormatException 257 */ processMethods()258 private final void processMethods () throws IOException, ClassFormatException { 259 int methods_count; 260 methods_count = file.readUnsignedShort(); 261 methods = new Method[methods_count]; 262 263 System.out.printf("%nMethods(%d):%n", methods_count); 264 265 for (int i = 0; i < methods_count; i++) { 266 processFieldOrMethod(); 267 if (i < methods_count - 1) { 268 System.out.println(); 269 } 270 } 271 } 272 273 /** 274 * Process information about the attributes of the class. 275 * @throws IOException 276 * @throws ClassFormatException 277 */ processAttributes()278 private final void processAttributes () throws IOException, ClassFormatException { 279 int attributes_count; 280 attributes_count = file.readUnsignedShort(); 281 attributes = new Attribute[attributes_count]; 282 283 System.out.printf("%nAttributes(%d):%n", attributes_count); 284 285 for (int i = 0; i < attributes_count; i++) { 286 attributes[i] = Attribute.readAttribute(file, constant_pool); 287 System.out.printf(" %s%n", attributes[i]); 288 } 289 } 290 291 /** 292 * Construct object from file stream. 293 * @param file Input stream 294 * @throws IOException 295 * @throws ClassFormatException 296 */ processFieldOrMethod()297 private final void processFieldOrMethod () throws IOException, ClassFormatException { 298 int access_flags = file.readUnsignedShort(); 299 int name_index = file.readUnsignedShort(); 300 System.out.printf(" name_index: %d (", name_index); 301 System.out.println(constantToString(name_index) + ")"); 302 System.out.println(" access_flags: " + BCELifier.printFlags(access_flags, 303 BCELifier.FLAGS.METHOD)); 304 int descriptor_index = file.readUnsignedShort(); 305 System.out.printf(" descriptor_index: %d (", descriptor_index); 306 System.out.println(constantToString(descriptor_index) + ")"); 307 308 int attributes_count = file.readUnsignedShort(); 309 Attribute[] attributes = new Attribute[attributes_count]; 310 System.out.println(" attribute count: " + attributes_count); 311 312 for (int i = 0; i < attributes_count; i++) { 313 // going to peek ahead a bit 314 file.mark(); 315 int attribute_name_index = file.readUnsignedShort(); 316 int attribute_length = file.readInt(); 317 // restore file location 318 file.reset(); 319 // Usefull for debugging 320 // System.out.printf(" attribute_name_index: %d (", attribute_name_index); 321 // System.out.println(constantToString(attribute_name_index) + ")"); 322 // System.out.printf(" atribute offset in file: %x%n", + file.getStreamPosition()); 323 // System.out.println(" atribute_length: " + attribute_length); 324 325 // A stronger verification test would be to read attribute_length bytes 326 // into a buffer. Then pass that buffer to readAttribute and also 327 // verify we're at EOF of the buffer on return. 328 329 long pos1 = file.getStreamPosition(); 330 attributes[i] = Attribute.readAttribute(file, constant_pool); 331 long pos2 = file.getStreamPosition(); 332 if ((pos2 - pos1) != (attribute_length + 6)) { 333 System.out.printf("%nWHOOPS attribute_length: %d pos2-pos1-6: %d pos1: %x(%d) pos2: %x(%d)%n", 334 attribute_length, pos2-pos1-6, pos1, pos1, pos2, pos2); 335 } 336 System.out.printf(" "); 337 System.out.println(attributes[i]); 338 } 339 } 340 constantToString(int index)341 private final String constantToString (int index) { 342 Constant c = constant_items[index]; 343 return constant_pool.constantToString(c); 344 } 345 346 } 347 348 class DumpClass { 349 main(String[] args)350 public static void main (String[] args) throws IOException { 351 352 if (args.length != 1) { 353 throw new RuntimeException("Require filename as only argument"); 354 } 355 356 FileImageInputStream file = new FileImageInputStream(new File(args[0])); 357 358 ClassDumper cd = new ClassDumper(file, args[0]); 359 cd.dump(); 360 361 System.out.printf("End of Class Dump%n"); 362 363 } 364 } 365