/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import java.io.File; import java.io.IOException; import javax.imageio.stream.FileImageInputStream; import org.apache.bcel.Constants; import org.apache.bcel.classfile.Attribute; import org.apache.bcel.classfile.ClassFormatException; import org.apache.bcel.classfile.Constant; import org.apache.bcel.classfile.ConstantPool; import org.apache.bcel.classfile.Field; import org.apache.bcel.classfile.Method; import org.apache.bcel.util.BCELifier; /** * Display Java .class file data. * Output is based on javap tool. * Built using the BCEL libary. * */ class ClassDumper { private FileImageInputStream file; private String file_name; private int class_name_index; private int superclass_name_index; private int major; private int minor; // Compiler version private int access_flags; // Access rights of parsed class private int[] interfaces; // Names of implemented interfaces private ConstantPool constant_pool; // collection of constants private Constant[] constant_items; // collection of constants private Field[] fields; // class fields, i.e., its variables private Method[] methods; // methods defined in the class private Attribute[] attributes; // attributes defined in the class /** * Parse class from the given stream. * * @param file Input stream * @param file_name File name */ public ClassDumper (FileImageInputStream file, String file_name) { this.file_name = file_name; this.file = file; } /** * Parse the given Java class file and return an object that represents * the contained data, i.e., constants, methods, fields and commands. * A ClassFormatException is raised, if the file is not a valid * .class file. (This does not include verification of the byte code as it * is performed by the java interpreter). * * @throws IOException * @throws ClassFormatException */ public void dump () throws IOException, ClassFormatException { try { // Check magic tag of class file processID(); // Get compiler version processVersion(); // process constant pool entries processConstantPool(); // Get class information processClassInfo(); // Get interface information, i.e., implemented interfaces processInterfaces(); // process class fields, i.e., the variables of the class processFields(); // process class methods, i.e., the functions in the class processMethods(); // process class attributes processAttributes(); } finally { // Processed everything of interest, so close the file try { if (file != null) { file.close(); } } catch (IOException ioe) { //ignore close exceptions } } } /** * Check whether the header of the file is ok. * Of course, this has to be the first action on successive file reads. * @throws IOException * @throws ClassFormatException */ private final void processID () throws IOException, ClassFormatException { final int magic = file.readInt(); if (magic != Constants.JVM_CLASSFILE_MAGIC) { throw new ClassFormatException(file_name + " is not a Java .class file"); } System.out.println("Java Class Dump"); System.out.println(" file: " + file_name); System.out.printf("%nClass header:%n"); System.out.printf(" magic: %X%n", magic); } /** * Process major and minor version of compiler which created the file. * @throws IOException * @throws ClassFormatException */ private final void processVersion () throws IOException, ClassFormatException { minor = file.readUnsignedShort(); System.out.printf(" minor version: %s%n", minor); major = file.readUnsignedShort(); System.out.printf(" major version: %s%n", major); } /** * Process constant pool entries. * @throws IOException * @throws ClassFormatException */ private final void processConstantPool () throws IOException, ClassFormatException { byte tag; int constant_pool_count = file.readUnsignedShort(); constant_items = new Constant[constant_pool_count]; constant_pool = new ConstantPool(constant_items); // constant_pool[0] is unused by the compiler System.out.printf("%nConstant pool(%d):%n", constant_pool_count - 1); for (int i = 1; i < constant_pool_count; i++) { constant_items[i] = Constant.readConstant(file); // i'm sure there is a better way to do this if (i < 10) { System.out.printf(" #%1d = ", i); } else if (i <100) { System.out.printf(" #%2d = ", i); } else { System.out.printf(" #%d = ", i); } System.out.println(constant_items[i]); // All eight byte constants take up two spots in the constant pool tag = constant_items[i].getTag(); if ((tag == Constants.CONSTANT_Double) || (tag == Constants.CONSTANT_Long)) { i++; } } } /** * Process information about the class and its super class. * @throws IOException * @throws ClassFormatException */ private final void processClassInfo () throws IOException, ClassFormatException { access_flags = file.readUnsignedShort(); /* Interfaces are implicitely abstract, the flag should be set * according to the JVM specification. */ if ((access_flags & Constants.ACC_INTERFACE) != 0) { access_flags |= Constants.ACC_ABSTRACT; } if (((access_flags & Constants.ACC_ABSTRACT) != 0) && ((access_flags & Constants.ACC_FINAL) != 0)) { throw new ClassFormatException("Class " + file_name + " can't be both final and abstract"); } System.out.printf("%nClass info:%n"); System.out.println(" flags: " + BCELifier.printFlags(access_flags, BCELifier.FLAGS.CLASS)); class_name_index = file.readUnsignedShort(); System.out.printf(" this_class: %d (", class_name_index); System.out.println(constantToString(class_name_index) + ")"); superclass_name_index = file.readUnsignedShort(); System.out.printf(" super_class: %d (", superclass_name_index); System.out.println(constantToString(superclass_name_index) + ")"); } /** * Process information about the interfaces implemented by this class. * @throws IOException * @throws ClassFormatException */ private final void processInterfaces () throws IOException, ClassFormatException { int interfaces_count; interfaces_count = file.readUnsignedShort(); interfaces = new int[interfaces_count]; System.out.printf("%nInterfaces(%d):%n", interfaces_count); for (int i = 0; i < interfaces_count; i++) { interfaces[i] = file.readUnsignedShort(); // i'm sure there is a better way to do this if (i < 10) { System.out.printf(" #%1d = ", i); } else if (i <100) { System.out.printf(" #%2d = ", i); } else { System.out.printf(" #%d = ", i); } System.out.println(interfaces[i] + " (" + constant_pool.getConstantString(interfaces[i], Constants.CONSTANT_Class) + ")"); } } /** * Process information about the fields of the class, i.e., its variables. * @throws IOException * @throws ClassFormatException */ private final void processFields () throws IOException, ClassFormatException { int fields_count; fields_count = file.readUnsignedShort(); fields = new Field[fields_count]; // sometimes fields[0] is magic used for serialization System.out.printf("%nFields(%d):%n", fields_count); for (int i = 0; i < fields_count; i++) { processFieldOrMethod(); if (i < fields_count - 1) { System.out.println(); } } } /** * Process information about the methods of the class. * @throws IOException * @throws ClassFormatException */ private final void processMethods () throws IOException, ClassFormatException { int methods_count; methods_count = file.readUnsignedShort(); methods = new Method[methods_count]; System.out.printf("%nMethods(%d):%n", methods_count); for (int i = 0; i < methods_count; i++) { processFieldOrMethod(); if (i < methods_count - 1) { System.out.println(); } } } /** * Process information about the attributes of the class. * @throws IOException * @throws ClassFormatException */ private final void processAttributes () throws IOException, ClassFormatException { int attributes_count; attributes_count = file.readUnsignedShort(); attributes = new Attribute[attributes_count]; System.out.printf("%nAttributes(%d):%n", attributes_count); for (int i = 0; i < attributes_count; i++) { attributes[i] = Attribute.readAttribute(file, constant_pool); System.out.printf(" %s%n", attributes[i]); } } /** * Construct object from file stream. * @param file Input stream * @throws IOException * @throws ClassFormatException */ private final void processFieldOrMethod () throws IOException, ClassFormatException { int access_flags = file.readUnsignedShort(); int name_index = file.readUnsignedShort(); System.out.printf(" name_index: %d (", name_index); System.out.println(constantToString(name_index) + ")"); System.out.println(" access_flags: " + BCELifier.printFlags(access_flags, BCELifier.FLAGS.METHOD)); int descriptor_index = file.readUnsignedShort(); System.out.printf(" descriptor_index: %d (", descriptor_index); System.out.println(constantToString(descriptor_index) + ")"); int attributes_count = file.readUnsignedShort(); Attribute[] attributes = new Attribute[attributes_count]; System.out.println(" attribute count: " + attributes_count); for (int i = 0; i < attributes_count; i++) { // going to peek ahead a bit file.mark(); int attribute_name_index = file.readUnsignedShort(); int attribute_length = file.readInt(); // restore file location file.reset(); // Usefull for debugging // System.out.printf(" attribute_name_index: %d (", attribute_name_index); // System.out.println(constantToString(attribute_name_index) + ")"); // System.out.printf(" atribute offset in file: %x%n", + file.getStreamPosition()); // System.out.println(" atribute_length: " + attribute_length); // A stronger verification test would be to read attribute_length bytes // into a buffer. Then pass that buffer to readAttribute and also // verify we're at EOF of the buffer on return. long pos1 = file.getStreamPosition(); attributes[i] = Attribute.readAttribute(file, constant_pool); long pos2 = file.getStreamPosition(); if ((pos2 - pos1) != (attribute_length + 6)) { System.out.printf("%nWHOOPS attribute_length: %d pos2-pos1-6: %d pos1: %x(%d) pos2: %x(%d)%n", attribute_length, pos2-pos1-6, pos1, pos1, pos2, pos2); } System.out.printf(" "); System.out.println(attributes[i]); } } private final String constantToString (int index) { Constant c = constant_items[index]; return constant_pool.constantToString(c); } } class DumpClass { public static void main (String[] args) throws IOException { if (args.length != 1) { throw new RuntimeException("Require filename as only argument"); } FileImageInputStream file = new FileImageInputStream(new File(args[0])); ClassDumper cd = new ClassDumper(file, args[0]); cd.dump(); System.out.printf("End of Class Dump%n"); } }