1/*** 2 * ASM: a very small and fast Java bytecode manipulation framework 3 * Copyright (c) 2000-2007 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 */ 30package org.mockito.asm.util; 31 32import java.io.FileInputStream; 33import java.io.PrintWriter; 34 35import org.mockito.asm.AnnotationVisitor; 36import org.mockito.asm.Attribute; 37import org.mockito.asm.ClassReader; 38import org.mockito.asm.ClassVisitor; 39import org.mockito.asm.FieldVisitor; 40import org.mockito.asm.MethodVisitor; 41import org.mockito.asm.Opcodes; 42import org.mockito.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 */ 94public 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 */ 119 public static void main(final String[] args) throws Exception { 120 int i = 0; 121 int flags = ClassReader.SKIP_DEBUG; 122 123 boolean ok = true; 124 if (args.length < 1 || args.length > 2) { 125 ok = false; 126 } 127 if (ok && "-debug".equals(args[0])) { 128 i = 1; 129 flags = 0; 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 flags); 151 } 152 153 /** 154 * Constructs a new {@link TraceClassVisitor}. 155 * 156 * @param pw the print writer to be used to print the class. 157 */ 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 */ 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 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("class "); 219 } 220 appendDescriptor(INTERNAL_NAME, name); 221 222 if (superName != null && !"java/lang/Object".equals(superName)) { 223 buf.append(" extends "); 224 appendDescriptor(INTERNAL_NAME, superName); 225 buf.append(' '); 226 } 227 if (interfaces != null && interfaces.length > 0) { 228 buf.append(" implements "); 229 for (int i = 0; i < interfaces.length; ++i) { 230 appendDescriptor(INTERNAL_NAME, interfaces[i]); 231 buf.append(' '); 232 } 233 } 234 buf.append(" {\n\n"); 235 236 text.add(buf.toString()); 237 238 if (cv != null) { 239 cv.visit(version, access, name, signature, superName, interfaces); 240 } 241 } 242 243 public void visitSource(final String file, final String debug) { 244 buf.setLength(0); 245 if (file != null) { 246 buf.append(tab) 247 .append("// compiled from: ") 248 .append(file) 249 .append('\n'); 250 } 251 if (debug != null) { 252 buf.append(tab) 253 .append("// debug info: ") 254 .append(debug) 255 .append('\n'); 256 } 257 if (buf.length() > 0) { 258 text.add(buf.toString()); 259 } 260 261 if (cv != null) { 262 cv.visitSource(file, debug); 263 } 264 } 265 266 public void visitOuterClass( 267 final String owner, 268 final String name, 269 final String desc) 270 { 271 buf.setLength(0); 272 buf.append(tab).append("OUTERCLASS "); 273 appendDescriptor(INTERNAL_NAME, owner); 274 buf.append(' '); 275 if (name != null) { 276 buf.append(name).append(' '); 277 } 278 appendDescriptor(METHOD_DESCRIPTOR, desc); 279 buf.append('\n'); 280 text.add(buf.toString()); 281 282 if (cv != null) { 283 cv.visitOuterClass(owner, name, desc); 284 } 285 } 286 287 public AnnotationVisitor visitAnnotation( 288 final String desc, 289 final boolean visible) 290 { 291 text.add("\n"); 292 AnnotationVisitor tav = super.visitAnnotation(desc, visible); 293 if (cv != null) { 294 ((TraceAnnotationVisitor) tav).av = cv.visitAnnotation(desc, 295 visible); 296 } 297 return tav; 298 } 299 300 public void visitAttribute(final Attribute attr) { 301 text.add("\n"); 302 super.visitAttribute(attr); 303 304 if (cv != null) { 305 cv.visitAttribute(attr); 306 } 307 } 308 309 public void visitInnerClass( 310 final String name, 311 final String outerName, 312 final String innerName, 313 final int access) 314 { 315 buf.setLength(0); 316 buf.append(tab).append("// access flags "); 317 buf.append(access & ~Opcodes.ACC_SUPER).append('\n'); 318 buf.append(tab); 319 appendAccess(access); 320 buf.append("INNERCLASS "); 321 appendDescriptor(INTERNAL_NAME, name); 322 buf.append(' '); 323 appendDescriptor(INTERNAL_NAME, outerName); 324 buf.append(' '); 325 appendDescriptor(INTERNAL_NAME, innerName); 326 buf.append('\n'); 327 text.add(buf.toString()); 328 329 if (cv != null) { 330 cv.visitInnerClass(name, outerName, innerName, access); 331 } 332 } 333 334 public FieldVisitor visitField( 335 final int access, 336 final String name, 337 final String desc, 338 final String signature, 339 final Object value) 340 { 341 buf.setLength(0); 342 buf.append('\n'); 343 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 344 buf.append(tab).append("// DEPRECATED\n"); 345 } 346 buf.append(tab).append("// access flags ").append(access).append('\n'); 347 if (signature != null) { 348 buf.append(tab); 349 appendDescriptor(FIELD_SIGNATURE, signature); 350 351 TraceSignatureVisitor sv = new TraceSignatureVisitor(0); 352 SignatureReader r = new SignatureReader(signature); 353 r.acceptType(sv); 354 buf.append(tab) 355 .append("// declaration: ") 356 .append(sv.getDeclaration()) 357 .append('\n'); 358 } 359 360 buf.append(tab); 361 appendAccess(access); 362 363 appendDescriptor(FIELD_DESCRIPTOR, desc); 364 buf.append(' ').append(name); 365 if (value != null) { 366 buf.append(" = "); 367 if (value instanceof String) { 368 buf.append('\"').append(value).append('\"'); 369 } else { 370 buf.append(value); 371 } 372 } 373 374 buf.append('\n'); 375 text.add(buf.toString()); 376 377 TraceFieldVisitor tav = createTraceFieldVisitor(); 378 text.add(tav.getText()); 379 380 if (cv != null) { 381 tav.fv = cv.visitField(access, name, desc, signature, value); 382 } 383 384 return tav; 385 } 386 387 public MethodVisitor visitMethod( 388 final int access, 389 final String name, 390 final String desc, 391 final String signature, 392 final String[] exceptions) 393 { 394 buf.setLength(0); 395 buf.append('\n'); 396 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 397 buf.append(tab).append("// DEPRECATED\n"); 398 } 399 buf.append(tab).append("// access flags ").append(access).append('\n'); 400 401 if (signature != null) { 402 buf.append(tab); 403 appendDescriptor(METHOD_SIGNATURE, signature); 404 405 TraceSignatureVisitor v = new TraceSignatureVisitor(0); 406 SignatureReader r = new SignatureReader(signature); 407 r.accept(v); 408 String genericDecl = v.getDeclaration(); 409 String genericReturn = v.getReturnType(); 410 String genericExceptions = v.getExceptions(); 411 412 buf.append(tab) 413 .append("// declaration: ") 414 .append(genericReturn) 415 .append(' ') 416 .append(name) 417 .append(genericDecl); 418 if (genericExceptions != null) { 419 buf.append(" throws ").append(genericExceptions); 420 } 421 buf.append('\n'); 422 } 423 424 buf.append(tab); 425 appendAccess(access); 426 if ((access & Opcodes.ACC_NATIVE) != 0) { 427 buf.append("native "); 428 } 429 if ((access & Opcodes.ACC_VARARGS) != 0) { 430 buf.append("varargs "); 431 } 432 if ((access & Opcodes.ACC_BRIDGE) != 0) { 433 buf.append("bridge "); 434 } 435 436 buf.append(name); 437 appendDescriptor(METHOD_DESCRIPTOR, desc); 438 if (exceptions != null && exceptions.length > 0) { 439 buf.append(" throws "); 440 for (int i = 0; i < exceptions.length; ++i) { 441 appendDescriptor(INTERNAL_NAME, exceptions[i]); 442 buf.append(' '); 443 } 444 } 445 446 buf.append('\n'); 447 text.add(buf.toString()); 448 449 TraceMethodVisitor tcv = createTraceMethodVisitor(); 450 text.add(tcv.getText()); 451 452 if (cv != null) { 453 tcv.mv = cv.visitMethod(access, name, desc, signature, exceptions); 454 } 455 456 return tcv; 457 } 458 459 public void visitEnd() { 460 text.add("}\n"); 461 462 print(pw); 463 pw.flush(); 464 465 if (cv != null) { 466 cv.visitEnd(); 467 } 468 } 469 470 // ------------------------------------------------------------------------ 471 // Utility methods 472 // ------------------------------------------------------------------------ 473 474 protected TraceFieldVisitor createTraceFieldVisitor() { 475 return new TraceFieldVisitor(); 476 } 477 478 protected TraceMethodVisitor createTraceMethodVisitor() { 479 return new TraceMethodVisitor(); 480 } 481 482 /** 483 * Appends a string representation of the given access modifiers to {@link 484 * #buf buf}. 485 * 486 * @param access some access modifiers. 487 */ 488 private void appendAccess(final int access) { 489 if ((access & Opcodes.ACC_PUBLIC) != 0) { 490 buf.append("public "); 491 } 492 if ((access & Opcodes.ACC_PRIVATE) != 0) { 493 buf.append("private "); 494 } 495 if ((access & Opcodes.ACC_PROTECTED) != 0) { 496 buf.append("protected "); 497 } 498 if ((access & Opcodes.ACC_FINAL) != 0) { 499 buf.append("final "); 500 } 501 if ((access & Opcodes.ACC_STATIC) != 0) { 502 buf.append("static "); 503 } 504 if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) { 505 buf.append("synchronized "); 506 } 507 if ((access & Opcodes.ACC_VOLATILE) != 0) { 508 buf.append("volatile "); 509 } 510 if ((access & Opcodes.ACC_TRANSIENT) != 0) { 511 buf.append("transient "); 512 } 513 if ((access & Opcodes.ACC_ABSTRACT) != 0) { 514 buf.append("abstract "); 515 } 516 if ((access & Opcodes.ACC_STRICT) != 0) { 517 buf.append("strictfp "); 518 } 519 if ((access & Opcodes.ACC_ENUM) != 0) { 520 buf.append("enum "); 521 } 522 } 523} 524