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