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 */ 30 package org.mockito.asm.util; 31 32 import org.mockito.asm.AnnotationVisitor; 33 import org.mockito.asm.Attribute; 34 import org.mockito.asm.Label; 35 import org.mockito.asm.MethodVisitor; 36 import org.mockito.asm.Opcodes; 37 import org.mockito.asm.Type; 38 import org.mockito.asm.signature.SignatureReader; 39 40 import java.util.HashMap; 41 import java.util.Map; 42 43 /** 44 * A {@link MethodVisitor} that prints a disassembled view of the methods it 45 * visits. 46 * 47 * @author Eric Bruneton 48 */ 49 public class TraceMethodVisitor extends TraceAbstractVisitor implements 50 MethodVisitor 51 { 52 53 /** 54 * The {@link MethodVisitor} to which this visitor delegates calls. May be 55 * <tt>null</tt>. 56 */ 57 protected MethodVisitor mv; 58 59 /** 60 * Tab for bytecode instructions. 61 */ 62 protected String tab2 = " "; 63 64 /** 65 * Tab for table and lookup switch instructions. 66 */ 67 protected String tab3 = " "; 68 69 /** 70 * Tab for labels. 71 */ 72 protected String ltab = " "; 73 74 /** 75 * The label names. This map associate String values to Label keys. 76 */ 77 protected final Map labelNames; 78 79 /** 80 * Constructs a new {@link TraceMethodVisitor}. 81 */ 82 public TraceMethodVisitor() { 83 this(null); 84 } 85 86 /** 87 * Constructs a new {@link TraceMethodVisitor}. 88 * 89 * @param mv the {@link MethodVisitor} to which this visitor delegates 90 * calls. May be <tt>null</tt>. 91 */ 92 public TraceMethodVisitor(final MethodVisitor mv) { 93 this.labelNames = new HashMap(); 94 this.mv = mv; 95 } 96 97 // ------------------------------------------------------------------------ 98 // Implementation of the MethodVisitor interface 99 // ------------------------------------------------------------------------ 100 101 public AnnotationVisitor visitAnnotation( 102 final String desc, 103 final boolean visible) 104 { 105 AnnotationVisitor av = super.visitAnnotation(desc, visible); 106 if (mv != null) { 107 ((TraceAnnotationVisitor) av).av = mv.visitAnnotation(desc, visible); 108 } 109 return av; 110 } 111 112 public void visitAttribute(final Attribute attr) { 113 buf.setLength(0); 114 buf.append(tab).append("ATTRIBUTE "); 115 appendDescriptor(-1, attr.type); 116 117 if (attr instanceof Traceable) { 118 ((Traceable) attr).trace(buf, labelNames); 119 } else { 120 buf.append(" : unknown\n"); 121 } 122 123 text.add(buf.toString()); 124 if (mv != null) { 125 mv.visitAttribute(attr); 126 } 127 } 128 129 public AnnotationVisitor visitAnnotationDefault() { 130 text.add(tab2 + "default="); 131 TraceAnnotationVisitor tav = createTraceAnnotationVisitor(); 132 text.add(tav.getText()); 133 text.add("\n"); 134 if (mv != null) { 135 tav.av = mv.visitAnnotationDefault(); 136 } 137 return tav; 138 } 139 140 public AnnotationVisitor visitParameterAnnotation( 141 final int parameter, 142 final String desc, 143 final boolean visible) 144 { 145 buf.setLength(0); 146 buf.append(tab2).append('@'); 147 appendDescriptor(FIELD_DESCRIPTOR, desc); 148 buf.append('('); 149 text.add(buf.toString()); 150 TraceAnnotationVisitor tav = createTraceAnnotationVisitor(); 151 text.add(tav.getText()); 152 text.add(visible ? ") // parameter " : ") // invisible, parameter "); 153 text.add(new Integer(parameter)); 154 text.add("\n"); 155 if (mv != null) { 156 tav.av = mv.visitParameterAnnotation(parameter, desc, visible); 157 } 158 return tav; 159 } 160 161 public void visitCode() { 162 if (mv != null) { 163 mv.visitCode(); 164 } 165 } 166 167 public void visitFrame( 168 final int type, 169 final int nLocal, 170 final Object[] local, 171 final int nStack, 172 final Object[] stack) 173 { 174 buf.setLength(0); 175 buf.append(ltab); 176 buf.append("FRAME "); 177 switch (type) { 178 case Opcodes.F_NEW: 179 case Opcodes.F_FULL: 180 buf.append("FULL ["); 181 appendFrameTypes(nLocal, local); 182 buf.append("] ["); 183 appendFrameTypes(nStack, stack); 184 buf.append(']'); 185 break; 186 case Opcodes.F_APPEND: 187 buf.append("APPEND ["); 188 appendFrameTypes(nLocal, local); 189 buf.append(']'); 190 break; 191 case Opcodes.F_CHOP: 192 buf.append("CHOP ").append(nLocal); 193 break; 194 case Opcodes.F_SAME: 195 buf.append("SAME"); 196 break; 197 case Opcodes.F_SAME1: 198 buf.append("SAME1 "); 199 appendFrameTypes(1, stack); 200 break; 201 } 202 buf.append('\n'); 203 text.add(buf.toString()); 204 205 if (mv != null) { 206 mv.visitFrame(type, nLocal, local, nStack, stack); 207 } 208 } 209 210 public void visitInsn(final int opcode) { 211 buf.setLength(0); 212 buf.append(tab2).append(OPCODES[opcode]).append('\n'); 213 text.add(buf.toString()); 214 215 if (mv != null) { 216 mv.visitInsn(opcode); 217 } 218 } 219 220 public void visitIntInsn(final int opcode, final int operand) { 221 buf.setLength(0); 222 buf.append(tab2) 223 .append(OPCODES[opcode]) 224 .append(' ') 225 .append(opcode == Opcodes.NEWARRAY 226 ? TYPES[operand] 227 : Integer.toString(operand)) 228 .append('\n'); 229 text.add(buf.toString()); 230 231 if (mv != null) { 232 mv.visitIntInsn(opcode, operand); 233 } 234 } 235 236 public void visitVarInsn(final int opcode, final int var) { 237 buf.setLength(0); 238 buf.append(tab2) 239 .append(OPCODES[opcode]) 240 .append(' ') 241 .append(var) 242 .append('\n'); 243 text.add(buf.toString()); 244 245 if (mv != null) { 246 mv.visitVarInsn(opcode, var); 247 } 248 } 249 250 public void visitTypeInsn(final int opcode, final String type) { 251 buf.setLength(0); 252 buf.append(tab2).append(OPCODES[opcode]).append(' '); 253 appendDescriptor(INTERNAL_NAME, type); 254 buf.append('\n'); 255 text.add(buf.toString()); 256 257 if (mv != null) { 258 mv.visitTypeInsn(opcode, type); 259 } 260 } 261 262 public void visitFieldInsn( 263 final int opcode, 264 final String owner, 265 final String name, 266 final String desc) 267 { 268 buf.setLength(0); 269 buf.append(tab2).append(OPCODES[opcode]).append(' '); 270 appendDescriptor(INTERNAL_NAME, owner); 271 buf.append('.').append(name).append(" : "); 272 appendDescriptor(FIELD_DESCRIPTOR, desc); 273 buf.append('\n'); 274 text.add(buf.toString()); 275 276 if (mv != null) { 277 mv.visitFieldInsn(opcode, owner, name, desc); 278 } 279 } 280 281 public void visitMethodInsn( 282 final int opcode, 283 final String owner, 284 final String name, 285 final String desc) 286 { 287 buf.setLength(0); 288 buf.append(tab2).append(OPCODES[opcode]).append(' '); 289 appendDescriptor(INTERNAL_NAME, owner); 290 buf.append('.').append(name).append(' '); 291 appendDescriptor(METHOD_DESCRIPTOR, desc); 292 buf.append('\n'); 293 text.add(buf.toString()); 294 295 if (mv != null) { 296 mv.visitMethodInsn(opcode, owner, name, desc); 297 } 298 } 299 300 public void visitJumpInsn(final int opcode, final Label label) { 301 buf.setLength(0); 302 buf.append(tab2).append(OPCODES[opcode]).append(' '); 303 appendLabel(label); 304 buf.append('\n'); 305 text.add(buf.toString()); 306 307 if (mv != null) { 308 mv.visitJumpInsn(opcode, label); 309 } 310 } 311 312 public void visitLabel(final Label label) { 313 buf.setLength(0); 314 buf.append(ltab); 315 appendLabel(label); 316 buf.append('\n'); 317 text.add(buf.toString()); 318 319 if (mv != null) { 320 mv.visitLabel(label); 321 } 322 } 323 324 public void visitLdcInsn(final Object cst) { 325 buf.setLength(0); 326 buf.append(tab2).append("LDC "); 327 if (cst instanceof String) { 328 AbstractVisitor.appendString(buf, (String) cst); 329 } else if (cst instanceof Type) { 330 buf.append(((Type) cst).getDescriptor()).append(".class"); 331 } else { 332 buf.append(cst); 333 } 334 buf.append('\n'); 335 text.add(buf.toString()); 336 337 if (mv != null) { 338 mv.visitLdcInsn(cst); 339 } 340 } 341 342 public void visitIincInsn(final int var, final int increment) { 343 buf.setLength(0); 344 buf.append(tab2) 345 .append("IINC ") 346 .append(var) 347 .append(' ') 348 .append(increment) 349 .append('\n'); 350 text.add(buf.toString()); 351 352 if (mv != null) { 353 mv.visitIincInsn(var, increment); 354 } 355 } 356 357 public void visitTableSwitchInsn( 358 final int min, 359 final int max, 360 final Label dflt, 361 final Label[] labels) 362 { 363 buf.setLength(0); 364 buf.append(tab2).append("TABLESWITCH\n"); 365 for (int i = 0; i < labels.length; ++i) { 366 buf.append(tab3).append(min + i).append(": "); 367 appendLabel(labels[i]); 368 buf.append('\n'); 369 } 370 buf.append(tab3).append("default: "); 371 appendLabel(dflt); 372 buf.append('\n'); 373 text.add(buf.toString()); 374 375 if (mv != null) { 376 mv.visitTableSwitchInsn(min, max, dflt, labels); 377 } 378 } 379 380 public void visitLookupSwitchInsn( 381 final Label dflt, 382 final int[] keys, 383 final Label[] labels) 384 { 385 buf.setLength(0); 386 buf.append(tab2).append("LOOKUPSWITCH\n"); 387 for (int i = 0; i < labels.length; ++i) { 388 buf.append(tab3).append(keys[i]).append(": "); 389 appendLabel(labels[i]); 390 buf.append('\n'); 391 } 392 buf.append(tab3).append("default: "); 393 appendLabel(dflt); 394 buf.append('\n'); 395 text.add(buf.toString()); 396 397 if (mv != null) { 398 mv.visitLookupSwitchInsn(dflt, keys, labels); 399 } 400 } 401 402 public void visitMultiANewArrayInsn(final String desc, final int dims) { 403 buf.setLength(0); 404 buf.append(tab2).append("MULTIANEWARRAY "); 405 appendDescriptor(FIELD_DESCRIPTOR, desc); 406 buf.append(' ').append(dims).append('\n'); 407 text.add(buf.toString()); 408 409 if (mv != null) { 410 mv.visitMultiANewArrayInsn(desc, dims); 411 } 412 } 413 414 public void visitTryCatchBlock( 415 final Label start, 416 final Label end, 417 final Label handler, 418 final String type) 419 { 420 buf.setLength(0); 421 buf.append(tab2).append("TRYCATCHBLOCK "); 422 appendLabel(start); 423 buf.append(' '); 424 appendLabel(end); 425 buf.append(' '); 426 appendLabel(handler); 427 buf.append(' '); 428 appendDescriptor(INTERNAL_NAME, type); 429 buf.append('\n'); 430 text.add(buf.toString()); 431 432 if (mv != null) { 433 mv.visitTryCatchBlock(start, end, handler, type); 434 } 435 } 436 437 public void visitLocalVariable( 438 final String name, 439 final String desc, 440 final String signature, 441 final Label start, 442 final Label end, 443 final int index) 444 { 445 buf.setLength(0); 446 buf.append(tab2).append("LOCALVARIABLE ").append(name).append(' '); 447 appendDescriptor(FIELD_DESCRIPTOR, desc); 448 buf.append(' '); 449 appendLabel(start); 450 buf.append(' '); 451 appendLabel(end); 452 buf.append(' ').append(index).append('\n'); 453 454 if (signature != null) { 455 buf.append(tab2); 456 appendDescriptor(FIELD_SIGNATURE, signature); 457 458 TraceSignatureVisitor sv = new TraceSignatureVisitor(0); 459 SignatureReader r = new SignatureReader(signature); 460 r.acceptType(sv); 461 buf.append(tab2) 462 .append("// declaration: ") 463 .append(sv.getDeclaration()) 464 .append('\n'); 465 } 466 text.add(buf.toString()); 467 468 if (mv != null) { 469 mv.visitLocalVariable(name, desc, signature, start, end, index); 470 } 471 } 472 473 public void visitLineNumber(final int line, final Label start) { 474 buf.setLength(0); 475 buf.append(tab2).append("LINENUMBER ").append(line).append(' '); 476 appendLabel(start); 477 buf.append('\n'); 478 text.add(buf.toString()); 479 480 if (mv != null) { 481 mv.visitLineNumber(line, start); 482 } 483 } 484 485 public void visitMaxs(final int maxStack, final int maxLocals) { 486 buf.setLength(0); 487 buf.append(tab2).append("MAXSTACK = ").append(maxStack).append('\n'); 488 text.add(buf.toString()); 489 490 buf.setLength(0); 491 buf.append(tab2).append("MAXLOCALS = ").append(maxLocals).append('\n'); 492 text.add(buf.toString()); 493 494 if (mv != null) { 495 mv.visitMaxs(maxStack, maxLocals); 496 } 497 } 498 499 public void visitEnd() { 500 super.visitEnd(); 501 502 if (mv != null) { 503 mv.visitEnd(); 504 } 505 } 506 507 // ------------------------------------------------------------------------ 508 // Utility methods 509 // ------------------------------------------------------------------------ 510 511 private void appendFrameTypes(final int n, final Object[] o) { 512 for (int i = 0; i < n; ++i) { 513 if (i > 0) { 514 buf.append(' '); 515 } 516 if (o[i] instanceof String) { 517 String desc = (String) o[i]; 518 if (desc.startsWith("[")) { 519 appendDescriptor(FIELD_DESCRIPTOR, desc); 520 } else { 521 appendDescriptor(INTERNAL_NAME, desc); 522 } 523 } else if (o[i] instanceof Integer) { 524 switch (((Integer) o[i]).intValue()) { 525 case 0: 526 appendDescriptor(FIELD_DESCRIPTOR, "T"); 527 break; 528 case 1: 529 appendDescriptor(FIELD_DESCRIPTOR, "I"); 530 break; 531 case 2: 532 appendDescriptor(FIELD_DESCRIPTOR, "F"); 533 break; 534 case 3: 535 appendDescriptor(FIELD_DESCRIPTOR, "D"); 536 break; 537 case 4: 538 appendDescriptor(FIELD_DESCRIPTOR, "J"); 539 break; 540 case 5: 541 appendDescriptor(FIELD_DESCRIPTOR, "N"); 542 break; 543 case 6: 544 appendDescriptor(FIELD_DESCRIPTOR, "U"); 545 break; 546 } 547 } else { 548 appendLabel((Label) o[i]); 549 } 550 } 551 } 552 553 /** 554 * Appends the name of the given label to {@link #buf buf}. Creates a new 555 * label name if the given label does not yet have one. 556 * 557 * @param l a label. 558 */ 559 protected void appendLabel(final Label l) { 560 String name = (String) labelNames.get(l); 561 if (name == null) { 562 name = "L" + labelNames.size(); 563 labelNames.put(l, name); 564 } 565 buf.append(name); 566 } 567 } 568