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