xref: /aosp_15_r20/external/apache-commons-bcel/src/examples/listclass.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.IOException;
20*0c56280aSSorin Basca import java.util.ArrayList;
21*0c56280aSSorin Basca import java.util.HashMap;
22*0c56280aSSorin Basca import java.util.List;
23*0c56280aSSorin Basca import java.util.Map;
24*0c56280aSSorin Basca 
25*0c56280aSSorin Basca import org.apache.bcel.Constants;
26*0c56280aSSorin Basca import org.apache.bcel.Repository;
27*0c56280aSSorin Basca import org.apache.bcel.classfile.ClassParser;
28*0c56280aSSorin Basca import org.apache.bcel.classfile.Code;
29*0c56280aSSorin Basca import org.apache.bcel.classfile.Constant;
30*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantClass;
31*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantPool;
32*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantUtf8;
33*0c56280aSSorin Basca import org.apache.bcel.classfile.JavaClass;
34*0c56280aSSorin Basca import org.apache.bcel.classfile.Method;
35*0c56280aSSorin Basca 
36*0c56280aSSorin Basca /**
37*0c56280aSSorin Basca  * Read class file(s) and display its contents. The command line usage is:
38*0c56280aSSorin Basca  *
39*0c56280aSSorin Basca  * <pre>java listclass [-constants] [-code] [-brief] [-dependencies] [-nocontents] [-recurse] class... [-exclude <list>]</pre>
40*0c56280aSSorin Basca  * where
41*0c56280aSSorin Basca  * <ul>
42*0c56280aSSorin Basca  * <li>{@code -code} List byte code of methods</li>
43*0c56280aSSorin Basca  * <li>{@code -brief} List byte codes briefly</li>
44*0c56280aSSorin Basca  * <li>{@code -constants} Print constants table (constant pool)</li>
45*0c56280aSSorin Basca  * <li>{@code -recurse}  Usually intended to be used along with
46*0c56280aSSorin Basca  * {@code -dependencies}  When this flag is set, listclass will also print information
47*0c56280aSSorin Basca  * about all classes which the target class depends on.</li>
48*0c56280aSSorin Basca  *
49*0c56280aSSorin Basca  * <li>{@code -dependencies}  Setting this flag makes listclass print a list of all
50*0c56280aSSorin Basca  * classes which the target class depends on.  Generated from getting all
51*0c56280aSSorin Basca  * CONSTANT_Class constants from the constant pool.</li>
52*0c56280aSSorin Basca  *
53*0c56280aSSorin Basca  * <li>{@code -exclude}  All non-flag arguments after this flag are added to an
54*0c56280aSSorin Basca  * 'exclusion list'.  Target classes are compared with the members of the
55*0c56280aSSorin Basca  * exclusion list.  Any target class whose fully qualified name begins with a
56*0c56280aSSorin Basca  * name in the exclusion list will not be analyzed/listed.  This is meant
57*0c56280aSSorin Basca  * primarily when using both {@code -recurse} to exclude java, javax, and sun classes,
58*0c56280aSSorin Basca  * and is recommended as otherwise the output from {@code -recurse} gets quite long and
59*0c56280aSSorin Basca  * most of it is not interesting.  Note that {@code -exclude} prevents listing of
60*0c56280aSSorin Basca  * classes, it does not prevent class names from being printed in the
61*0c56280aSSorin Basca  * {@code -dependencies} list.</li>
62*0c56280aSSorin Basca  * <li>{@code -nocontents} Do not print JavaClass.toString() for the class. I added
63*0c56280aSSorin Basca  * this because sometimes I'm only interested in dependency information.</li>
64*0c56280aSSorin Basca  * </ul>
65*0c56280aSSorin Basca  * <p>Here's a couple examples of how I typically use listclass:<br>
66*0c56280aSSorin Basca  * <pre>java listclass -code MyClass</pre>
67*0c56280aSSorin Basca  * Print information about the class and the byte code of the methods
68*0c56280aSSorin Basca  * <pre>java listclass -nocontents -dependencies MyClass</pre>
69*0c56280aSSorin Basca  * Print a list of all classes which MyClass depends on.
70*0c56280aSSorin Basca  * <pre>java listclass -nocontents -recurse MyClass -exclude java. javax. sun.</pre>
71*0c56280aSSorin Basca  * Print a recursive listing of all classes which MyClass depends on.  Do not
72*0c56280aSSorin Basca  * analyze classes beginning with "java.", "javax.", or "sun.".
73*0c56280aSSorin Basca  * <pre>java listclass -nocontents -dependencies -recurse MyClass -exclude java.javax. sun.</pre>
74*0c56280aSSorin Basca  * Print a recursive listing of dependency information for MyClass and its
75*0c56280aSSorin Basca  * dependents.  Do not analyze classes beginning with "java.", "javax.", or "sun."
76*0c56280aSSorin Basca  * </p>
77*0c56280aSSorin Basca  *
78*0c56280aSSorin Basca  *         <a href="mailto:[email protected]">Thomas Wheeler</A>
79*0c56280aSSorin Basca  * @version $Id$
80*0c56280aSSorin Basca  */
81*0c56280aSSorin Basca public class listclass {
82*0c56280aSSorin Basca 
83*0c56280aSSorin Basca     boolean code;
84*0c56280aSSorin Basca     boolean constants;
85*0c56280aSSorin Basca     boolean verbose;
86*0c56280aSSorin Basca     boolean classdep;
87*0c56280aSSorin Basca     boolean nocontents;
88*0c56280aSSorin Basca     boolean recurse;
89*0c56280aSSorin Basca     Map<String, String> listedClasses;
90*0c56280aSSorin Basca     List<String> exclude_name;
91*0c56280aSSorin Basca 
main(String[] argv)92*0c56280aSSorin Basca     public static void main(String[] argv) {
93*0c56280aSSorin Basca         List<String> file_name = new ArrayList<String>();
94*0c56280aSSorin Basca         List<String> exclude_name = new ArrayList<String>();
95*0c56280aSSorin Basca         boolean code = false;
96*0c56280aSSorin Basca         boolean constants = false;
97*0c56280aSSorin Basca         boolean verbose = true;
98*0c56280aSSorin Basca         boolean classdep = false;
99*0c56280aSSorin Basca         boolean nocontents = false;
100*0c56280aSSorin Basca         boolean recurse = false;
101*0c56280aSSorin Basca         boolean exclude = false;
102*0c56280aSSorin Basca         String name;
103*0c56280aSSorin Basca 
104*0c56280aSSorin Basca         // Parse command line arguments.
105*0c56280aSSorin Basca         for (String arg : argv) {
106*0c56280aSSorin Basca             if (arg.charAt(0) == '-') {  // command line switch
107*0c56280aSSorin Basca                 if (arg.equals("-constants")) {
108*0c56280aSSorin Basca                     constants = true;
109*0c56280aSSorin Basca                 } else if (arg.equals("-code")) {
110*0c56280aSSorin Basca                     code = true;
111*0c56280aSSorin Basca                 } else if (arg.equals("-brief")) {
112*0c56280aSSorin Basca                     verbose = false;
113*0c56280aSSorin Basca                 } else if (arg.equals("-dependencies")) {
114*0c56280aSSorin Basca                     classdep = true;
115*0c56280aSSorin Basca                 } else if (arg.equals("-nocontents")) {
116*0c56280aSSorin Basca                     nocontents = true;
117*0c56280aSSorin Basca                 } else if (arg.equals("-recurse")) {
118*0c56280aSSorin Basca                     recurse = true;
119*0c56280aSSorin Basca                 } else if (arg.equals("-exclude")) {
120*0c56280aSSorin Basca                     exclude = true;
121*0c56280aSSorin Basca                 } else if (arg.equals("-help")) {
122*0c56280aSSorin Basca                     System.out.println("Usage: java listclass [-constants] [-code] [-brief] " +
123*0c56280aSSorin Basca                             "[-dependencies] [-nocontents] [-recurse] class... " +
124*0c56280aSSorin Basca                             "[-exclude <list>]\n" +
125*0c56280aSSorin Basca                             "-constants       Print constants table (constant pool)\n" +
126*0c56280aSSorin Basca                             "-code            Dump byte code of methods\n" +
127*0c56280aSSorin Basca                             "-brief           Brief listing\n" +
128*0c56280aSSorin Basca                             "-dependencies    Show class dependencies\n" +
129*0c56280aSSorin Basca                             "-nocontents      Do not print field/method information\n" +
130*0c56280aSSorin Basca                             "-recurse         Recurse into dependent classes\n" +
131*0c56280aSSorin Basca                             "-exclude <list>  Do not list classes beginning with " +
132*0c56280aSSorin Basca                             "strings in <list>");
133*0c56280aSSorin Basca                     System.exit(0);
134*0c56280aSSorin Basca                 } else {
135*0c56280aSSorin Basca                     System.err.println("Unknown switch " + arg + " ignored.");
136*0c56280aSSorin Basca                 }
137*0c56280aSSorin Basca             } else { // add file name to list
138*0c56280aSSorin Basca                 if (exclude) {
139*0c56280aSSorin Basca                     exclude_name.add(arg);
140*0c56280aSSorin Basca                 } else {
141*0c56280aSSorin Basca                     file_name.add(arg);
142*0c56280aSSorin Basca                 }
143*0c56280aSSorin Basca             }
144*0c56280aSSorin Basca         }
145*0c56280aSSorin Basca 
146*0c56280aSSorin Basca         if (file_name.size() == 0) {
147*0c56280aSSorin Basca             System.err.println("list: No input files specified");
148*0c56280aSSorin Basca         } else {
149*0c56280aSSorin Basca             listclass listClass = new listclass(code, constants, verbose, classdep,
150*0c56280aSSorin Basca                     nocontents, recurse, exclude_name);
151*0c56280aSSorin Basca 
152*0c56280aSSorin Basca             for (int i = 0; i < file_name.size(); i++) {
153*0c56280aSSorin Basca                 name = file_name.get(i);
154*0c56280aSSorin Basca 
155*0c56280aSSorin Basca                 listClass.list(name);
156*0c56280aSSorin Basca             }
157*0c56280aSSorin Basca         }
158*0c56280aSSorin Basca     }
159*0c56280aSSorin Basca 
listclass(boolean code, boolean constants, boolean verbose, boolean classdep, boolean nocontents, boolean recurse, List<String> exclude_name)160*0c56280aSSorin Basca     public listclass(boolean code, boolean constants, boolean verbose, boolean classdep,
161*0c56280aSSorin Basca                      boolean nocontents, boolean recurse, List<String> exclude_name) {
162*0c56280aSSorin Basca         this.code = code;
163*0c56280aSSorin Basca         this.constants = constants;
164*0c56280aSSorin Basca         this.verbose = verbose;
165*0c56280aSSorin Basca         this.classdep = classdep;
166*0c56280aSSorin Basca         this.nocontents = nocontents;
167*0c56280aSSorin Basca         this.recurse = recurse;
168*0c56280aSSorin Basca         this.listedClasses = new HashMap<String, String>();
169*0c56280aSSorin Basca         this.exclude_name = exclude_name;
170*0c56280aSSorin Basca     }
171*0c56280aSSorin Basca 
172*0c56280aSSorin Basca     /**
173*0c56280aSSorin Basca      * Print the given class on screen
174*0c56280aSSorin Basca      */
list(String name)175*0c56280aSSorin Basca     public void list(String name) {
176*0c56280aSSorin Basca         try {
177*0c56280aSSorin Basca             JavaClass java_class;
178*0c56280aSSorin Basca 
179*0c56280aSSorin Basca             if ((listedClasses.get(name) != null) || name.startsWith("[")) {
180*0c56280aSSorin Basca                 return;
181*0c56280aSSorin Basca             }
182*0c56280aSSorin Basca 
183*0c56280aSSorin Basca             for (int idx = 0; idx < exclude_name.size(); idx++) {
184*0c56280aSSorin Basca                 if (name.startsWith(exclude_name.get(idx))) {
185*0c56280aSSorin Basca                     return;
186*0c56280aSSorin Basca                 }
187*0c56280aSSorin Basca             }
188*0c56280aSSorin Basca 
189*0c56280aSSorin Basca             if (name.endsWith(".class")) {
190*0c56280aSSorin Basca                 java_class = new ClassParser(name).parse(); // May throw IOException
191*0c56280aSSorin Basca             } else {
192*0c56280aSSorin Basca                 java_class = Repository.lookupClass(name);
193*0c56280aSSorin Basca             }
194*0c56280aSSorin Basca 
195*0c56280aSSorin Basca             if (nocontents) {
196*0c56280aSSorin Basca                 System.out.println(java_class.getClassName());
197*0c56280aSSorin Basca             } else {
198*0c56280aSSorin Basca                 System.out.println(java_class);             // Dump the contents
199*0c56280aSSorin Basca             }
200*0c56280aSSorin Basca 
201*0c56280aSSorin Basca             if (constants) {
202*0c56280aSSorin Basca                 System.out.println(java_class.getConstantPool());
203*0c56280aSSorin Basca             }
204*0c56280aSSorin Basca 
205*0c56280aSSorin Basca             if (code) {
206*0c56280aSSorin Basca                 printCode(java_class.getMethods(), verbose);
207*0c56280aSSorin Basca             }
208*0c56280aSSorin Basca 
209*0c56280aSSorin Basca             if (classdep) {
210*0c56280aSSorin Basca                 printClassDependencies(java_class.getConstantPool());
211*0c56280aSSorin Basca             }
212*0c56280aSSorin Basca 
213*0c56280aSSorin Basca             listedClasses.put(name, name);
214*0c56280aSSorin Basca 
215*0c56280aSSorin Basca             if (recurse) {
216*0c56280aSSorin Basca                 String[] dependencies = getClassDependencies(java_class.getConstantPool());
217*0c56280aSSorin Basca 
218*0c56280aSSorin Basca                 for (String dependency : dependencies) {
219*0c56280aSSorin Basca                     list(dependency);
220*0c56280aSSorin Basca                 }
221*0c56280aSSorin Basca             }
222*0c56280aSSorin Basca         } catch (IOException e) {
223*0c56280aSSorin Basca             System.out.println("Error loading class " + name + " (" + e.getMessage() + ")");
224*0c56280aSSorin Basca         } catch (Exception e) {
225*0c56280aSSorin Basca             System.out.println("Error processing class " + name + " (" + e.getMessage() + ")");
226*0c56280aSSorin Basca         }
227*0c56280aSSorin Basca     }
228*0c56280aSSorin Basca 
229*0c56280aSSorin Basca     /**
230*0c56280aSSorin Basca      * Dump the list of classes this class is dependent on
231*0c56280aSSorin Basca      */
printClassDependencies(ConstantPool pool)232*0c56280aSSorin Basca     public static void printClassDependencies(ConstantPool pool) {
233*0c56280aSSorin Basca         System.out.println("Dependencies:");
234*0c56280aSSorin Basca         for (String name : getClassDependencies(pool)) {
235*0c56280aSSorin Basca             System.out.println("\t" + name);
236*0c56280aSSorin Basca         }
237*0c56280aSSorin Basca     }
238*0c56280aSSorin Basca 
getClassDependencies(ConstantPool pool)239*0c56280aSSorin Basca     public static String[] getClassDependencies(ConstantPool pool) {
240*0c56280aSSorin Basca         String[] tempArray = new String[pool.getLength()];
241*0c56280aSSorin Basca         int size = 0;
242*0c56280aSSorin Basca         StringBuilder buf = new StringBuilder();
243*0c56280aSSorin Basca 
244*0c56280aSSorin Basca         for (int idx = 0; idx < pool.getLength(); idx++) {
245*0c56280aSSorin Basca             Constant c = pool.getConstant(idx);
246*0c56280aSSorin Basca             if (c != null && c.getTag() == Constants.CONSTANT_Class) {
247*0c56280aSSorin Basca                 ConstantUtf8 c1 = (ConstantUtf8) pool.getConstant(((ConstantClass) c).getNameIndex());
248*0c56280aSSorin Basca                 buf.setLength(0);
249*0c56280aSSorin Basca                 buf.append(c1.getBytes());
250*0c56280aSSorin Basca                 for (int n = 0; n < buf.length(); n++) {
251*0c56280aSSorin Basca                     if (buf.charAt(n) == '/') {
252*0c56280aSSorin Basca                         buf.setCharAt(n, '.');
253*0c56280aSSorin Basca                     }
254*0c56280aSSorin Basca                 }
255*0c56280aSSorin Basca 
256*0c56280aSSorin Basca                 tempArray[size++] = buf.toString();
257*0c56280aSSorin Basca             }
258*0c56280aSSorin Basca         }
259*0c56280aSSorin Basca 
260*0c56280aSSorin Basca         String[] dependencies = new String[size];
261*0c56280aSSorin Basca         System.arraycopy(tempArray, 0, dependencies, 0, size);
262*0c56280aSSorin Basca         return dependencies;
263*0c56280aSSorin Basca     }
264*0c56280aSSorin Basca 
265*0c56280aSSorin Basca     /**
266*0c56280aSSorin Basca      * Dump the disassembled code of all methods in the class.
267*0c56280aSSorin Basca      */
printCode(Method[] methods, boolean verbose)268*0c56280aSSorin Basca     public static void printCode(Method[] methods, boolean verbose) {
269*0c56280aSSorin Basca         for (Method method : methods) {
270*0c56280aSSorin Basca             System.out.println(method);
271*0c56280aSSorin Basca 
272*0c56280aSSorin Basca             Code code = method.getCode();
273*0c56280aSSorin Basca             if (code != null) {
274*0c56280aSSorin Basca                 System.out.println(code.toString(verbose));
275*0c56280aSSorin Basca             }
276*0c56280aSSorin Basca         }
277*0c56280aSSorin Basca     }
278*0c56280aSSorin Basca }
279