xref: /aosp_15_r20/external/apache-commons-bcel/src/examples/ClassDumper.java (revision 0c56280ab0842982c46a149f7b9eaa497e31e292)
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