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