Home | History | Annotate | Download | only in util
      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