1 /*** 2 * ASM: a very small and fast Java bytecode manipulation framework 3 * Copyright (c) 2000-2005 INRIA, France Telecom 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the copyright holders nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 * THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 package org.objectweb.asm.util; 31 32 import java.io.FileInputStream; 33 import java.io.PrintWriter; 34 35 import org.objectweb.asm.AnnotationVisitor; 36 import org.objectweb.asm.Attribute; 37 import org.objectweb.asm.ClassReader; 38 import org.objectweb.asm.ClassVisitor; 39 import org.objectweb.asm.MethodVisitor; 40 import org.objectweb.asm.Opcodes; 41 import org.objectweb.asm.FieldVisitor; 42 import org.objectweb.asm.signature.SignatureReader; 43 44 /** 45 * A {@link ClassVisitor} that prints a disassembled view of the classes it 46 * visits. This class visitor can be used alone (see the {@link #main main} 47 * method) to disassemble a class. It can also be used in the middle of class 48 * visitor chain to trace the class that is visited at a given point in this 49 * chain. This may be uselful for debugging purposes. <p> The trace printed when 50 * visiting the <tt>Hello</tt> class is the following: <p> <blockquote> 51 * 52 * <pre> 53 * // class version 49.0 (49) 54 * // access flags 33 55 * public class Hello { 56 * 57 * // compiled from: Hello.java 58 * 59 * // access flags 1 60 * public <init> ()V 61 * ALOAD 0 62 * INVOKESPECIAL java/lang/Object <init> ()V 63 * RETURN 64 * MAXSTACK = 1 65 * MAXLOCALS = 1 66 * 67 * // access flags 9 68 * public static main ([Ljava/lang/String;)V 69 * GETSTATIC java/lang/System out Ljava/io/PrintStream; 70 * LDC "hello" 71 * INVOKEVIRTUAL java/io/PrintStream println (Ljava/lang/String;)V 72 * RETURN 73 * MAXSTACK = 2 74 * MAXLOCALS = 1 75 * } 76 * </pre> 77 * 78 * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote> 79 * 80 * <pre> 81 * public class Hello { 82 * 83 * public static void main(String[] args) { 84 * System.out.println("hello"); 85 * } 86 * } 87 * </pre> 88 * 89 * </blockquote> 90 * 91 * @author Eric Bruneton 92 * @author Eugene Kuleshov 93 */ 94 public class TraceClassVisitor extends TraceAbstractVisitor implements 95 ClassVisitor 96 { 97 98 /** 99 * The {@link ClassVisitor} to which this visitor delegates calls. May be 100 * <tt>null</tt>. 101 */ 102 protected final ClassVisitor cv; 103 104 /** 105 * The print writer to be used to print the class. 106 */ 107 protected final PrintWriter pw; 108 109 /** 110 * Prints a disassembled view of the given class to the standard output. <p> 111 * Usage: TraceClassVisitor [-debug] <fully qualified class name or class 112 * file name > 113 * 114 * @param args the command line arguments. 115 * 116 * @throws Exception if the class cannot be found, or if an IO exception 117 * occurs. 118 */ main(final String[] args)119 public static void main(final String[] args) throws Exception { 120 int i = 0; 121 boolean skipDebug = true; 122 123 boolean ok = true; 124 if (args.length < 1 || args.length > 2) { 125 ok = false; 126 } 127 if (ok && args[0].equals("-debug")) { 128 i = 1; 129 skipDebug = false; 130 if (args.length != 2) { 131 ok = false; 132 } 133 } 134 if (!ok) { 135 System.err.println("Prints a disassembled view of the given class."); 136 System.err.println("Usage: TraceClassVisitor [-debug] " 137 + "<fully qualified class name or class file name>"); 138 return; 139 } 140 ClassReader cr; 141 if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1 142 || args[i].indexOf('/') > -1) 143 { 144 cr = new ClassReader(new FileInputStream(args[i])); 145 } else { 146 cr = new ClassReader(args[i]); 147 } 148 cr.accept(new TraceClassVisitor(new PrintWriter(System.out)), 149 getDefaultAttributes(), 150 skipDebug); 151 } 152 153 /** 154 * Constructs a new {@link TraceClassVisitor}. 155 * 156 * @param pw the print writer to be used to print the class. 157 */ TraceClassVisitor(final PrintWriter pw)158 public TraceClassVisitor(final PrintWriter pw) { 159 this(null, pw); 160 } 161 162 /** 163 * Constructs a new {@link TraceClassVisitor}. 164 * 165 * @param cv the {@link ClassVisitor} to which this visitor delegates calls. 166 * May be <tt>null</tt>. 167 * @param pw the print writer to be used to print the class. 168 */ TraceClassVisitor(final ClassVisitor cv, final PrintWriter pw)169 public TraceClassVisitor(final ClassVisitor cv, final PrintWriter pw) { 170 this.cv = cv; 171 this.pw = pw; 172 } 173 174 // ------------------------------------------------------------------------ 175 // Implementation of the ClassVisitor interface 176 // ------------------------------------------------------------------------ 177 visit( final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces)178 public void visit( 179 final int version, 180 final int access, 181 final String name, 182 final String signature, 183 final String superName, 184 final String[] interfaces) 185 { 186 int major = version & 0xFFFF; 187 int minor = version >>> 16; 188 buf.setLength(0); 189 buf.append("// class version ") 190 .append(major) 191 .append('.') 192 .append(minor) 193 .append(" (") 194 .append(version) 195 .append(")\n"); 196 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 197 buf.append("// DEPRECATED\n"); 198 } 199 buf.append("// access flags ").append(access).append('\n'); 200 201 appendDescriptor(CLASS_SIGNATURE, signature); 202 if (signature != null) { 203 TraceSignatureVisitor sv = new TraceSignatureVisitor(access); 204 SignatureReader r = new SignatureReader(signature); 205 r.accept(sv); 206 buf.append("// declaration: ") 207 .append(name) 208 .append(sv.getDeclaration()) 209 .append('\n'); 210 } 211 212 appendAccess(access & ~Opcodes.ACC_SUPER); 213 if ((access & Opcodes.ACC_ANNOTATION) != 0) { 214 buf.append("@interface "); 215 } else if ((access & Opcodes.ACC_INTERFACE) != 0) { 216 buf.append("interface "); 217 } else if ((access & Opcodes.ACC_ENUM) != 0) { 218 buf.append("enum "); 219 } else { 220 buf.append("class "); 221 } 222 appendDescriptor(INTERNAL_NAME, name); 223 224 if (superName != null && !superName.equals("java/lang/Object")) { 225 buf.append(" extends "); 226 appendDescriptor(INTERNAL_NAME, superName); 227 buf.append(' '); 228 } 229 if (interfaces != null && interfaces.length > 0) { 230 buf.append(" implements "); 231 for (int i = 0; i < interfaces.length; ++i) { 232 appendDescriptor(INTERNAL_NAME, interfaces[i]); 233 buf.append(' '); 234 } 235 } 236 buf.append(" {\n\n"); 237 238 text.add(buf.toString()); 239 240 if (cv != null) { 241 cv.visit(version, access, name, signature, superName, interfaces); 242 } 243 } 244 visitSource(final String file, final String debug)245 public void visitSource(final String file, final String debug) { 246 buf.setLength(0); 247 if (file != null) { 248 buf.append(tab) 249 .append("// compiled from: ") 250 .append(file) 251 .append('\n'); 252 } 253 if (debug != null) { 254 buf.append(tab) 255 .append("// debug info: ") 256 .append(debug) 257 .append('\n'); 258 } 259 if (buf.length() > 0) { 260 text.add(buf.toString()); 261 } 262 263 if (cv != null) { 264 cv.visitSource(file, debug); 265 } 266 } 267 visitOuterClass( final String owner, final String name, final String desc)268 public void visitOuterClass( 269 final String owner, 270 final String name, 271 final String desc) 272 { 273 buf.setLength(0); 274 buf.append(tab).append("OUTERCLASS "); 275 appendDescriptor(INTERNAL_NAME, owner); 276 // if enclosing name is null, so why should we show this info? 277 if (name != null) { 278 buf.append(' ').append(name).append(' '); 279 } else { 280 buf.append(' '); 281 } 282 appendDescriptor(METHOD_DESCRIPTOR, desc); 283 buf.append('\n'); 284 text.add(buf.toString()); 285 286 if (cv != null) { 287 cv.visitOuterClass(owner, name, desc); 288 } 289 } 290 visitAnnotation( final String desc, final boolean visible)291 public AnnotationVisitor visitAnnotation( 292 final String desc, 293 final boolean visible) 294 { 295 text.add("\n"); 296 AnnotationVisitor tav = super.visitAnnotation(desc, visible); 297 if (cv != null) { 298 ((TraceAnnotationVisitor) tav).av = cv.visitAnnotation(desc, 299 visible); 300 } 301 return tav; 302 } 303 visitAttribute(final Attribute attr)304 public void visitAttribute(final Attribute attr) { 305 text.add("\n"); 306 super.visitAttribute(attr); 307 308 if (cv != null) { 309 cv.visitAttribute(attr); 310 } 311 } 312 visitInnerClass( final String name, final String outerName, final String innerName, final int access)313 public void visitInnerClass( 314 final String name, 315 final String outerName, 316 final String innerName, 317 final int access) 318 { 319 buf.setLength(0); 320 buf.append(tab).append("// access flags ").append(access 321 & ~Opcodes.ACC_SUPER).append('\n'); 322 buf.append(tab); 323 appendAccess(access); 324 buf.append("INNERCLASS "); 325 if ((access & Opcodes.ACC_ENUM) != 0) { 326 buf.append("enum "); 327 } 328 appendDescriptor(INTERNAL_NAME, name); 329 buf.append(' '); 330 appendDescriptor(INTERNAL_NAME, outerName); 331 buf.append(' '); 332 appendDescriptor(INTERNAL_NAME, innerName); 333 buf.append('\n'); 334 text.add(buf.toString()); 335 336 if (cv != null) { 337 cv.visitInnerClass(name, outerName, innerName, access); 338 } 339 } 340 visitField( final int access, final String name, final String desc, final String signature, final Object value)341 public FieldVisitor visitField( 342 final int access, 343 final String name, 344 final String desc, 345 final String signature, 346 final Object value) 347 { 348 buf.setLength(0); 349 buf.append('\n'); 350 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 351 buf.append(tab).append("// DEPRECATED\n"); 352 } 353 buf.append(tab).append("// access flags ").append(access).append('\n'); 354 if (signature != null) { 355 buf.append(tab); 356 appendDescriptor(FIELD_SIGNATURE, signature); 357 358 TraceSignatureVisitor sv = new TraceSignatureVisitor(0); 359 SignatureReader r = new SignatureReader(signature); 360 r.acceptType(sv); 361 buf.append(tab) 362 .append("// declaration: ") 363 .append(sv.getDeclaration()) 364 .append('\n'); 365 } 366 367 buf.append(tab); 368 appendAccess(access); 369 if ((access & Opcodes.ACC_ENUM) != 0) { 370 buf.append("enum "); 371 } 372 373 appendDescriptor(FIELD_DESCRIPTOR, desc); 374 buf.append(' ').append(name); 375 if (value != null) { 376 buf.append(" = "); 377 if (value instanceof String) { 378 buf.append("\"").append(value).append("\""); 379 } else { 380 buf.append(value); 381 } 382 } 383 384 buf.append('\n'); 385 text.add(buf.toString()); 386 387 TraceFieldVisitor tav = createTraceFieldVisitor(); 388 text.add(tav.getText()); 389 390 if (cv != null) { 391 tav.fv = cv.visitField(access, name, desc, signature, value); 392 } 393 394 return tav; 395 } 396 visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions)397 public MethodVisitor visitMethod( 398 final int access, 399 final String name, 400 final String desc, 401 final String signature, 402 final String[] exceptions) 403 { 404 buf.setLength(0); 405 buf.append('\n'); 406 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 407 buf.append(tab).append("// DEPRECATED\n"); 408 } 409 buf.append(tab).append("// access flags ").append(access).append('\n'); 410 buf.append(tab); 411 appendDescriptor(METHOD_SIGNATURE, signature); 412 413 if (signature != null) { 414 TraceSignatureVisitor v = new TraceSignatureVisitor(0); 415 SignatureReader r = new SignatureReader(signature); 416 r.accept(v); 417 String genericDecl = v.getDeclaration(); 418 String genericReturn = v.getReturnType(); 419 String genericExceptions = v.getExceptions(); 420 421 buf.append(tab) 422 .append("// declaration: ") 423 .append(genericReturn) 424 .append(' ') 425 .append(name) 426 .append(genericDecl); 427 if (genericExceptions != null) { 428 buf.append(" throws ").append(genericExceptions); 429 } 430 buf.append('\n'); 431 } 432 433 appendAccess(access); 434 if ((access & Opcodes.ACC_NATIVE) != 0) { 435 buf.append("native "); 436 } 437 if ((access & Opcodes.ACC_VARARGS) != 0) { 438 buf.append("varargs "); 439 } 440 if ((access & Opcodes.ACC_BRIDGE) != 0) { 441 buf.append("bridge "); 442 } 443 444 buf.append(name); 445 appendDescriptor(METHOD_DESCRIPTOR, desc); 446 if (exceptions != null && exceptions.length > 0) { 447 buf.append(" throws "); 448 for (int i = 0; i < exceptions.length; ++i) { 449 appendDescriptor(INTERNAL_NAME, exceptions[i]); 450 buf.append(' '); 451 } 452 } 453 454 buf.append('\n'); 455 text.add(buf.toString()); 456 457 TraceMethodVisitor tcv = createTraceMethodVisitor(); 458 text.add(tcv.getText()); 459 460 if (cv != null) { 461 tcv.mv = cv.visitMethod(access, name, desc, signature, exceptions); 462 } 463 464 return tcv; 465 } 466 visitEnd()467 public void visitEnd() { 468 text.add("}\n"); 469 470 printList(pw, text); 471 pw.flush(); 472 473 if (cv != null) { 474 cv.visitEnd(); 475 } 476 } 477 478 // ------------------------------------------------------------------------ 479 // Utility methods 480 // ------------------------------------------------------------------------ 481 createTraceFieldVisitor()482 protected TraceFieldVisitor createTraceFieldVisitor() { 483 return new TraceFieldVisitor(); 484 } 485 createTraceMethodVisitor()486 protected TraceMethodVisitor createTraceMethodVisitor() { 487 return new TraceMethodVisitor(); 488 } 489 490 /** 491 * Appends a string representation of the given access modifiers to {@link 492 * #buf buf}. 493 * 494 * @param access some access modifiers. 495 */ appendAccess(final int access)496 private void appendAccess(final int access) { 497 if ((access & Opcodes.ACC_PUBLIC) != 0) { 498 buf.append("public "); 499 } 500 if ((access & Opcodes.ACC_PRIVATE) != 0) { 501 buf.append("private "); 502 } 503 if ((access & Opcodes.ACC_PROTECTED) != 0) { 504 buf.append("protected "); 505 } 506 if ((access & Opcodes.ACC_FINAL) != 0) { 507 buf.append("final "); 508 } 509 if ((access & Opcodes.ACC_STATIC) != 0) { 510 buf.append("static "); 511 } 512 if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) { 513 buf.append("synchronized "); 514 } 515 if ((access & Opcodes.ACC_VOLATILE) != 0) { 516 buf.append("volatile "); 517 } 518 if ((access & Opcodes.ACC_TRANSIENT) != 0) { 519 buf.append("transient "); 520 } 521 // if ((access & Constants.ACC_NATIVE) != 0) { 522 // buf.append("native "); 523 // } 524 if ((access & Opcodes.ACC_ABSTRACT) != 0) { 525 buf.append("abstract "); 526 } 527 if ((access & Opcodes.ACC_STRICT) != 0) { 528 buf.append("strictfp "); 529 } 530 if ((access & Opcodes.ACC_SYNTHETIC) != 0) { 531 buf.append("synthetic "); 532 } 533 } 534 } 535