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