blob: 049c755f3a64b0e89728b8165ca7d8395a1368ac [file] [log] [blame]
import java.io.*;
import java.util.*;
import org.apache.bcel.classfile.*;
import org.apache.bcel.*;
/**
* Read class file(s) and display its contents. The command line usage is:
*
* <pre>java listclass [-constants] [-code] [-brief] [-dependencies] [-nocontents] [-recurse] class... [-exclude <list>]</pre>
* where
* <ul>
* <li><tt>-code</tt> List byte code of methods</li>
* <li><tt>-brief</tt> List byte codes briefly</li>
* <li><tt>-constants</tt> Print constants table (constant pool)</li>
* <li><tt>-recurse</tt> Usually intended to be used along with
* <tt>-dependencies</tt> When this flag is set, listclass will also print information
* about all classes which the target class depends on.</li>
*
* <li><tt>-dependencies</tt> Setting this flag makes listclass print a list of all
* classes which the target class depends on. Generated from getting all
* CONSTANT_Class constants from the constant pool.</li>
*
* <li><tt>-exclude</tt> All non-flag arguments after this flag are added to an
* 'exclusion list'. Target classes are compared with the members of the
* exclusion list. Any target class whose fully qualified name begins with a
* name in the exclusion list will not be analyzed/listed. This is meant
* primarily when using both <tt>-recurse</tt> to exclude java, javax, and sun classes,
* and is recommended as otherwise the output from <tt>-recurse</tt> gets quite long and
* most of it is not interesting. Note that <tt>-exclude</tt> prevents listing of
* classes, it does not prevent class names from being printed in the
* <tt>-dependencies</tt> list.</li>
* <li><tt>-nocontents</tt> Do not print JavaClass.toString() for the class. I added
* this because sometimes I'm only interested in dependency information.</li>
* </ul>
* <p>Here's a couple examples of how I typically use listclass:<br>
* <pre>java listclass -code MyClass</pre>
* Print information about the class and the byte code of the methods
* <pre>java listclass -nocontents -dependencies MyClass</pre>
* Print a list of all classes which MyClass depends on.
* <pre>java listclass -nocontents -recurse MyClass -exclude java. javax. sun.</pre>
* Print a recursive listing of all classes which MyClass depends on. Do not
* analyze classes beginning with "java.", "javax.", or "sun.".
* <pre>java listclass -nocontents -dependencies -recurse MyClass -exclude java.javax. sun.</pre>
* Print a recursive listing of dependency information for MyClass and its
* dependents. Do not analyze classes beginning with "java.", "javax.", or "sun."
* </p>
*
* @version $Id$
* @author <A HREF="http://www.berlin.de/~markus.dahm">M. Dahm</A>,
* <a href="mailto:twheeler@objectspace.com">Thomas Wheeler</A>
*/
public class listclass {
boolean code, constants, verbose, classdep, nocontents, recurse;
Hashtable listedClasses;
Vector exclude_name;
public static void main(String[] argv) {
Vector file_name = new Vector();
Vector exclude_name = new Vector();
boolean code = false, constants=false, verbose=true, classdep=false,
nocontents=false, recurse=false, exclude=false;
String name = null;
/* Parse command line arguments.
*/
for(int i=0; i < argv.length; i++) {
if(argv[i].charAt(0) == '-') { // command line switch
if(argv[i].equals("-constants"))
constants=true;
else if(argv[i].equals("-code"))
code=true;
else if(argv[i].equals("-brief"))
verbose=false;
else if(argv[i].equals("-dependencies"))
classdep=true;
else if(argv[i].equals("-nocontents"))
nocontents=true;
else if(argv[i].equals("-recurse"))
recurse=true;
else if(argv[i].equals("-exclude"))
exclude=true;
else if(argv[i].equals("-help")) {
System.out.println( "Usage: java listclass [-constants] [-code] [-brief] " +
"[-dependencies] [-nocontents] [-recurse] class... " +
"[-exclude <list>]\n" +
"-constants Print constants table (constant pool)\n" +
"-code Dump byte code of methods\n" +
"-brief Brief listing\n" +
"-dependencies Show class dependencies\n" +
"-nocontents Do not print field/method information\n" +
"-recurse Recurse into dependent classes\n" +
"-exclude <list> Do not list classes beginning with " +
"strings in <list>" );
System.exit( 0 );
} else
System.err.println("Unknown switch " + argv[i] + " ignored.");
} else { // add file name to list
if(exclude)
exclude_name.addElement(argv[i]);
else
file_name.addElement(argv[i]);
}
}
if(file_name.size() == 0)
System.err.println("list: No input files specified");
else {
listclass listClass = new listclass(code, constants, verbose, classdep,
nocontents, recurse, exclude_name);
for(int i=0; i < file_name.size(); i++) {
name = (String) file_name.elementAt(i);
listClass.list(name);
}
}
}
public listclass(boolean code, boolean constants, boolean verbose, boolean classdep,
boolean nocontents, boolean recurse, Vector exclude_name)
{
this.code = code;
this.constants = constants;
this.verbose = verbose;
this.classdep = classdep;
this.nocontents = nocontents;
this.recurse = recurse;
this.listedClasses = new Hashtable();
this.exclude_name = exclude_name;
}
/** Print the given class on screen
*/
public void list(String name) {
try {
JavaClass java_class;
if((listedClasses.get(name) != null) || name.startsWith("["))
return;
for(int idx = 0; idx < exclude_name.size(); idx++)
if(name.startsWith((String) exclude_name.elementAt(idx)))
return;
if((java_class = Repository.lookupClass(name)) == null)
java_class = new ClassParser(name).parse(); // May throw IOException
if(nocontents)
System.out.println(java_class.getClassName());
else
System.out.println(java_class); // Dump the contents
if(constants) // Dump the constant pool ?
System.out.println(java_class.getConstantPool());
if(code) // Dump the method code ?
printCode(java_class.getMethods(), verbose);
if(classdep)
printClassDependencies(java_class.getConstantPool());
listedClasses.put(name, name);
if(recurse) {
String[] dependencies = getClassDependencies(java_class.getConstantPool());
for(int idx = 0; idx < dependencies.length; idx++)
list(dependencies[idx]);
}
} catch(IOException e) {
System.out.println("Error loading class " + name + " (" + e.getMessage() + ")");
}
catch(Exception e) {
System.out.println("Error processing class " + name + " (" + e.getMessage() + ")");
}
}
/**
* Dump the list of classes this class is dependent on
*/
public static void printClassDependencies(ConstantPool pool) {
String[] names = getClassDependencies(pool);
System.out.println("Dependencies:");
for(int idx = 0; idx < names.length; idx++)
System.out.println("\t" + names[idx]);
}
public static String[] getClassDependencies(ConstantPool pool) {
String[] tempArray = new String[pool.getLength()];
int size = 0;
StringBuffer buf = new StringBuffer();
for(int idx = 0; idx < pool.getLength(); idx++) {
Constant c = pool.getConstant(idx);
if(c != null && c.getTag() == Constants.CONSTANT_Class) {
ConstantUtf8 c1 = (ConstantUtf8) pool.getConstant(((ConstantClass)c).getNameIndex());
buf.setLength(0);
buf.append(new String(c1.getBytes()));
for(int n = 0; n < buf.length(); n++) {
if(buf.charAt(n) == '/')
buf.setCharAt(n, '.');
}
tempArray[size++] = buf.toString();
}
}
String[] dependencies = new String[size];
System.arraycopy(tempArray, 0, dependencies, 0, size);
return dependencies;
}
/**
* Dump the disassembled code of all methods in the class.
*/
public static void printCode(Method[] methods, boolean verbose) {
for(int i=0; i < methods.length; i++) {
System.out.println(methods[i]);
Code code = methods[i].getCode();
if(code != null)
System.out.println(code.toString(verbose));
}
}
}