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