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 org.mockito.asm.AnnotationVisitor;
33import org.mockito.asm.Attribute;
34import org.mockito.asm.Label;
35import org.mockito.asm.MethodVisitor;
36import org.mockito.asm.Opcodes;
37import org.mockito.asm.Type;
38import org.mockito.asm.signature.SignatureReader;
39
40import java.util.HashMap;
41import 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 */
49public 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