| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.bcel.Constants; |
| import org.apache.bcel.Repository; |
| import org.apache.bcel.classfile.ClassParser; |
| import org.apache.bcel.classfile.Code; |
| import org.apache.bcel.classfile.Constant; |
| import org.apache.bcel.classfile.ConstantClass; |
| import org.apache.bcel.classfile.ConstantPool; |
| import org.apache.bcel.classfile.ConstantUtf8; |
| import org.apache.bcel.classfile.JavaClass; |
| import org.apache.bcel.classfile.Method; |
| |
| /** |
| * 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>{@code -code} List byte code of methods</li> |
| * <li>{@code -brief} List byte codes briefly</li> |
| * <li>{@code -constants} Print constants table (constant pool)</li> |
| * <li>{@code -recurse} Usually intended to be used along with |
| * {@code -dependencies} When this flag is set, listclass will also print information |
| * about all classes which the target class depends on.</li> |
| * |
| * <li>{@code -dependencies} 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>{@code -exclude} 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 {@code -recurse} to exclude java, javax, and sun classes, |
| * and is recommended as otherwise the output from {@code -recurse} gets quite long and |
| * most of it is not interesting. Note that {@code -exclude} prevents listing of |
| * classes, it does not prevent class names from being printed in the |
| * {@code -dependencies} list.</li> |
| * <li>{@code -nocontents} 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> |
| * |
| * <a href="mailto:[email protected]">Thomas Wheeler</A> |
| * @version $Id$ |
| */ |
| public class listclass { |
| |
| boolean code; |
| boolean constants; |
| boolean verbose; |
| boolean classdep; |
| boolean nocontents; |
| boolean recurse; |
| Map<String, String> listedClasses; |
| List<String> exclude_name; |
| |
| public static void main(String[] argv) { |
| List<String> file_name = new ArrayList<String>(); |
| List<String> exclude_name = new ArrayList<String>(); |
| boolean code = false; |
| boolean constants = false; |
| boolean verbose = true; |
| boolean classdep = false; |
| boolean nocontents = false; |
| boolean recurse = false; |
| boolean exclude = false; |
| String name; |
| |
| // Parse command line arguments. |
| for (String arg : argv) { |
| if (arg.charAt(0) == '-') { // command line switch |
| if (arg.equals("-constants")) { |
| constants = true; |
| } else if (arg.equals("-code")) { |
| code = true; |
| } else if (arg.equals("-brief")) { |
| verbose = false; |
| } else if (arg.equals("-dependencies")) { |
| classdep = true; |
| } else if (arg.equals("-nocontents")) { |
| nocontents = true; |
| } else if (arg.equals("-recurse")) { |
| recurse = true; |
| } else if (arg.equals("-exclude")) { |
| exclude = true; |
| } else if (arg.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 " + arg + " ignored."); |
| } |
| } else { // add file name to list |
| if (exclude) { |
| exclude_name.add(arg); |
| } else { |
| file_name.add(arg); |
| } |
| } |
| } |
| |
| 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 = file_name.get(i); |
| |
| listClass.list(name); |
| } |
| } |
| } |
| |
| public listclass(boolean code, boolean constants, boolean verbose, boolean classdep, |
| boolean nocontents, boolean recurse, List<String> exclude_name) { |
| this.code = code; |
| this.constants = constants; |
| this.verbose = verbose; |
| this.classdep = classdep; |
| this.nocontents = nocontents; |
| this.recurse = recurse; |
| this.listedClasses = new HashMap<String, String>(); |
| 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(exclude_name.get(idx))) { |
| return; |
| } |
| } |
| |
| if (name.endsWith(".class")) { |
| java_class = new ClassParser(name).parse(); // May throw IOException |
| } else { |
| java_class = Repository.lookupClass(name); |
| } |
| |
| if (nocontents) { |
| System.out.println(java_class.getClassName()); |
| } else { |
| System.out.println(java_class); // Dump the contents |
| } |
| |
| if (constants) { |
| System.out.println(java_class.getConstantPool()); |
| } |
| |
| if (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 (String dependency : dependencies) { |
| list(dependency); |
| } |
| } |
| } 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) { |
| System.out.println("Dependencies:"); |
| for (String name : getClassDependencies(pool)) { |
| System.out.println("\t" + name); |
| } |
| } |
| |
| public static String[] getClassDependencies(ConstantPool pool) { |
| String[] tempArray = new String[pool.getLength()]; |
| int size = 0; |
| StringBuilder buf = new StringBuilder(); |
| |
| 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(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 (Method method : methods) { |
| System.out.println(method); |
| |
| Code code = method.getCode(); |
| if (code != null) { |
| System.out.println(code.toString(verbose)); |
| } |
| } |
| } |
| } |