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