001: /*
002: * ASM: a very small and fast Java bytecode manipulation framework
003: * Copyright (c) 2000-2005 INRIA, France Telecom
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: * 2. Redistributions in binary form must reproduce the above copyright
012: * notice, this list of conditions and the following disclaimer in the
013: * documentation and/or other materials provided with the distribution.
014: * 3. Neither the name of the copyright holders nor the names of its
015: * contributors may be used to endorse or promote products derived from
016: * this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
028: * THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.objectweb.asm.util;
031:
032: import org.objectweb.asm.AnnotationVisitor;
033: import org.objectweb.asm.Attribute;
034: import org.objectweb.asm.MethodVisitor;
035: import org.objectweb.asm.Label;
036: import org.objectweb.asm.Opcodes;
037: import org.objectweb.asm.Type;
038: import org.objectweb.asm.signature.SignatureReader;
039:
040: import java.util.HashMap;
041:
042: /**
043: * A {@link MethodVisitor} that prints a disassembled view of the methods it
044: * visits.
045: *
046: * @author Eric Bruneton
047: */
048: @SuppressWarnings("unchecked")
049: public class TraceMethodVisitor extends TraceAbstractVisitor implements
050: MethodVisitor {
051:
052: /**
053: * The {@link MethodVisitor} to which this visitor delegates calls. May be
054: * <tt>null</tt>.
055: */
056: protected MethodVisitor mv;
057:
058: /**
059: * Tab for bytecode instructions.
060: */
061: protected String tab2 = " ";
062:
063: /**
064: * Tab for table and lookup switch instructions.
065: */
066: protected String tab3 = " ";
067:
068: /**
069: * Tab for labels.
070: */
071: protected String ltab = " ";
072:
073: /**
074: * The label names. This map associate String values to Label keys.
075: */
076: protected final HashMap labelNames;
077:
078: /**
079: * Constructs a new {@link TraceMethodVisitor}.
080: */
081: public TraceMethodVisitor() {
082: this (null);
083: }
084:
085: /**
086: * Constructs a new {@link TraceMethodVisitor}.
087: *
088: * @param mv the {@link MethodVisitor} to which this visitor delegates
089: * calls. May be <tt>null</tt>.
090: */
091: public TraceMethodVisitor(final MethodVisitor mv) {
092: this .labelNames = new HashMap();
093: this .mv = mv;
094: }
095:
096: // ------------------------------------------------------------------------
097: // Implementation of the MethodVisitor interface
098: // ------------------------------------------------------------------------
099:
100: public AnnotationVisitor visitAnnotation(final String desc,
101: final boolean visible) {
102: AnnotationVisitor av = super .visitAnnotation(desc, visible);
103: if (mv != null) {
104: ((TraceAnnotationVisitor) av).av = mv.visitAnnotation(desc,
105: visible);
106: }
107: return av;
108: }
109:
110: public void visitAttribute(final Attribute attr) {
111: buf.setLength(0);
112: buf.append(tab).append("ATTRIBUTE ");
113: appendDescriptor(-1, attr.type);
114:
115: if (attr instanceof Traceable) {
116: ((Traceable) attr).trace(buf, labelNames);
117: } else {
118: buf.append(" : unknown\n");
119: }
120:
121: text.add(buf.toString());
122: if (mv != null) {
123: mv.visitAttribute(attr);
124: }
125: }
126:
127: public AnnotationVisitor visitAnnotationDefault() {
128: text.add(tab2 + "default=");
129: TraceAnnotationVisitor tav = createTraceAnnotationVisitor();
130: text.add(tav.getText());
131: text.add("\n");
132: if (mv != null) {
133: tav.av = mv.visitAnnotationDefault();
134: }
135: return tav;
136: }
137:
138: public AnnotationVisitor visitParameterAnnotation(
139: final int parameter, final String desc,
140: final boolean visible) {
141: buf.setLength(0);
142: buf.append(tab2).append('@');
143: appendDescriptor(FIELD_DESCRIPTOR, desc);
144: buf.append('(');
145: text.add(buf.toString());
146: TraceAnnotationVisitor tav = createTraceAnnotationVisitor();
147: text.add(tav.getText());
148: text.add(visible ? ") // parameter "
149: : ") // invisible, parameter ");
150: text.add(new Integer(parameter));
151: text.add("\n");
152: if (mv != null) {
153: tav.av = mv.visitParameterAnnotation(parameter, desc,
154: visible);
155: }
156: return tav;
157: }
158:
159: public void visitCode() {
160: if (mv != null) {
161: mv.visitCode();
162: }
163: }
164:
165: public void visitFrame(final int type, final int nLocal,
166: final Object[] local, final int nStack, final Object[] stack) {
167: buf.setLength(0);
168: buf.append(ltab);
169: buf.append("FRAME ");
170: switch (type) {
171: case Opcodes.F_NEW:
172: case Opcodes.F_FULL:
173: buf.append("FULL [");
174: appendFrameTypes(nLocal, local);
175: buf.append("] [");
176: appendFrameTypes(nStack, stack);
177: buf.append("]");
178: break;
179: case Opcodes.F_APPEND:
180: buf.append("APPEND [");
181: appendFrameTypes(nLocal, local);
182: buf.append("]");
183: break;
184: case Opcodes.F_CHOP:
185: buf.append("CHOP ").append(nLocal);
186: break;
187: case Opcodes.F_SAME:
188: buf.append("SAME");
189: break;
190: case Opcodes.F_SAME1:
191: buf.append("SAME1 ");
192: appendFrameTypes(1, stack);
193: break;
194: }
195: buf.append("\n");
196: text.add(buf.toString());
197:
198: if (mv != null) {
199: mv.visitFrame(type, nLocal, local, nStack, stack);
200: }
201: }
202:
203: public void visitInsn(final int opcode) {
204: buf.setLength(0);
205: buf.append(tab2).append(OPCODES[opcode]).append('\n');
206: text.add(buf.toString());
207:
208: if (mv != null) {
209: mv.visitInsn(opcode);
210: }
211: }
212:
213: public void visitIntInsn(final int opcode, final int operand) {
214: buf.setLength(0);
215: buf.append(tab2).append(OPCODES[opcode]).append(' ').append(
216: opcode == Opcodes.NEWARRAY ? TYPES[operand] : Integer
217: .toString(operand)).append('\n');
218: text.add(buf.toString());
219:
220: if (mv != null) {
221: mv.visitIntInsn(opcode, operand);
222: }
223: }
224:
225: public void visitVarInsn(final int opcode, final int var) {
226: buf.setLength(0);
227: buf.append(tab2).append(OPCODES[opcode]).append(' ')
228: .append(var).append('\n');
229: text.add(buf.toString());
230:
231: if (mv != null) {
232: mv.visitVarInsn(opcode, var);
233: }
234: }
235:
236: public void visitTypeInsn(final int opcode, final String desc) {
237: buf.setLength(0);
238: buf.append(tab2).append(OPCODES[opcode]).append(' ');
239: if (desc.startsWith("[")) {
240: appendDescriptor(FIELD_DESCRIPTOR, desc);
241: } else {
242: appendDescriptor(INTERNAL_NAME, desc);
243: }
244: buf.append('\n');
245: text.add(buf.toString());
246:
247: if (mv != null) {
248: mv.visitTypeInsn(opcode, desc);
249: }
250: }
251:
252: public void visitFieldInsn(final int opcode, final String owner,
253: final String name, final String desc) {
254: buf.setLength(0);
255: buf.append(tab2).append(OPCODES[opcode]).append(' ');
256: appendDescriptor(INTERNAL_NAME, owner);
257: buf.append('.').append(name).append(" : ");
258: appendDescriptor(FIELD_DESCRIPTOR, desc);
259: buf.append('\n');
260: text.add(buf.toString());
261:
262: if (mv != null) {
263: mv.visitFieldInsn(opcode, owner, name, desc);
264: }
265: }
266:
267: public void visitMethodInsn(final int opcode, final String owner,
268: final String name, final String desc) {
269: buf.setLength(0);
270: buf.append(tab2).append(OPCODES[opcode]).append(' ');
271: appendDescriptor(INTERNAL_NAME, owner);
272: buf.append('.').append(name).append(' ');
273: appendDescriptor(METHOD_DESCRIPTOR, desc);
274: buf.append('\n');
275: text.add(buf.toString());
276:
277: if (mv != null) {
278: mv.visitMethodInsn(opcode, owner, name, desc);
279: }
280: }
281:
282: public void visitJumpInsn(final int opcode, final Label label) {
283: buf.setLength(0);
284: buf.append(tab2).append(OPCODES[opcode]).append(' ');
285: appendLabel(label);
286: buf.append('\n');
287: text.add(buf.toString());
288:
289: if (mv != null) {
290: mv.visitJumpInsn(opcode, label);
291: }
292: }
293:
294: public void visitLabel(final Label label) {
295: buf.setLength(0);
296: buf.append(ltab);
297: appendLabel(label);
298: buf.append('\n');
299: text.add(buf.toString());
300:
301: if (mv != null) {
302: mv.visitLabel(label);
303: }
304: }
305:
306: public void visitLdcInsn(final Object cst) {
307: buf.setLength(0);
308: buf.append(tab2).append("LDC ");
309: if (cst instanceof String) {
310: AbstractVisitor.appendString(buf, (String) cst);
311: } else if (cst instanceof Type) {
312: buf.append(((Type) cst).getDescriptor() + ".class");
313: } else {
314: buf.append(cst);
315: }
316: buf.append('\n');
317: text.add(buf.toString());
318:
319: if (mv != null) {
320: mv.visitLdcInsn(cst);
321: }
322: }
323:
324: public void visitIincInsn(final int var, final int increment) {
325: buf.setLength(0);
326: buf.append(tab2).append("IINC ").append(var).append(' ')
327: .append(increment).append('\n');
328: text.add(buf.toString());
329:
330: if (mv != null) {
331: mv.visitIincInsn(var, increment);
332: }
333: }
334:
335: public void visitTableSwitchInsn(final int min, final int max,
336: final Label dflt, final Label labels[]) {
337: buf.setLength(0);
338: buf.append(tab2).append("TABLESWITCH\n");
339: for (int i = 0; i < labels.length; ++i) {
340: buf.append(tab3).append(min + i).append(": ");
341: appendLabel(labels[i]);
342: buf.append('\n');
343: }
344: buf.append(tab3).append("default: ");
345: appendLabel(dflt);
346: buf.append('\n');
347: text.add(buf.toString());
348:
349: if (mv != null) {
350: mv.visitTableSwitchInsn(min, max, dflt, labels);
351: }
352: }
353:
354: public void visitLookupSwitchInsn(final Label dflt,
355: final int keys[], final Label labels[]) {
356: buf.setLength(0);
357: buf.append(tab2).append("LOOKUPSWITCH\n");
358: for (int i = 0; i < labels.length; ++i) {
359: buf.append(tab3).append(keys[i]).append(": ");
360: appendLabel(labels[i]);
361: buf.append('\n');
362: }
363: buf.append(tab3).append("default: ");
364: appendLabel(dflt);
365: buf.append('\n');
366: text.add(buf.toString());
367:
368: if (mv != null) {
369: mv.visitLookupSwitchInsn(dflt, keys, labels);
370: }
371: }
372:
373: public void visitMultiANewArrayInsn(final String desc,
374: final int dims) {
375: buf.setLength(0);
376: buf.append(tab2).append("MULTIANEWARRAY ");
377: appendDescriptor(FIELD_DESCRIPTOR, desc);
378: buf.append(' ').append(dims).append('\n');
379: text.add(buf.toString());
380:
381: if (mv != null) {
382: mv.visitMultiANewArrayInsn(desc, dims);
383: }
384: }
385:
386: public void visitTryCatchBlock(final Label start, final Label end,
387: final Label handler, final String type) {
388: buf.setLength(0);
389: buf.append(tab2).append("TRYCATCHBLOCK ");
390: appendLabel(start);
391: buf.append(' ');
392: appendLabel(end);
393: buf.append(' ');
394: appendLabel(handler);
395: buf.append(' ');
396: appendDescriptor(INTERNAL_NAME, type);
397: buf.append('\n');
398: text.add(buf.toString());
399:
400: if (mv != null) {
401: mv.visitTryCatchBlock(start, end, handler, type);
402: }
403: }
404:
405: public void visitLocalVariable(final String name,
406: final String desc, final String signature,
407: final Label start, final Label end, final int index) {
408: buf.setLength(0);
409: buf.append(tab2).append("LOCALVARIABLE ").append(name).append(
410: ' ');
411: appendDescriptor(FIELD_DESCRIPTOR, desc);
412: buf.append(' ');
413: appendLabel(start);
414: buf.append(' ');
415: appendLabel(end);
416: buf.append(' ').append(index).append('\n');
417:
418: if (signature != null) {
419: buf.append(tab2);
420: appendDescriptor(FIELD_SIGNATURE, signature);
421:
422: TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
423: SignatureReader r = new SignatureReader(signature);
424: r.acceptType(sv);
425: buf.append(tab2).append("// declaration: ").append(
426: sv.getDeclaration()).append('\n');
427: }
428: text.add(buf.toString());
429:
430: if (mv != null) {
431: mv.visitLocalVariable(name, desc, signature, start, end,
432: index);
433: }
434: }
435:
436: public void visitLineNumber(final int line, final Label start) {
437: buf.setLength(0);
438: buf.append(tab2).append("LINENUMBER ").append(line).append(' ');
439: appendLabel(start);
440: buf.append('\n');
441: text.add(buf.toString());
442:
443: if (mv != null) {
444: mv.visitLineNumber(line, start);
445: }
446: }
447:
448: public void visitMaxs(final int maxStack, final int maxLocals) {
449: buf.setLength(0);
450: buf.append(tab2).append("MAXSTACK = ").append(maxStack).append(
451: '\n');
452: text.add(buf.toString());
453:
454: buf.setLength(0);
455: buf.append(tab2).append("MAXLOCALS = ").append(maxLocals)
456: .append('\n');
457: text.add(buf.toString());
458:
459: if (mv != null) {
460: mv.visitMaxs(maxStack, maxLocals);
461: }
462: }
463:
464: public void visitEnd() {
465: super .visitEnd();
466:
467: if (mv != null) {
468: mv.visitEnd();
469: }
470: }
471:
472: // ------------------------------------------------------------------------
473: // Utility methods
474: // ------------------------------------------------------------------------
475:
476: private void appendFrameTypes(final int n, final Object[] o) {
477: for (int i = 0; i < n; ++i) {
478: if (i > 0) {
479: buf.append(' ');
480: }
481: if (o[i] instanceof String) {
482: String desc = (String) o[i];
483: if (desc.startsWith("[")) {
484: appendDescriptor(FIELD_DESCRIPTOR, desc);
485: } else {
486: appendDescriptor(INTERNAL_NAME, desc);
487: }
488: } else if (o[i] instanceof Integer) {
489: switch (((Integer) o[i]).intValue()) {
490: case 0:
491: appendDescriptor(FIELD_DESCRIPTOR, "T");
492: break;
493: case 1:
494: appendDescriptor(FIELD_DESCRIPTOR, "I");
495: break;
496: case 2:
497: appendDescriptor(FIELD_DESCRIPTOR, "F");
498: break;
499: case 3:
500: appendDescriptor(FIELD_DESCRIPTOR, "D");
501: break;
502: case 4:
503: appendDescriptor(FIELD_DESCRIPTOR, "J");
504: break;
505: case 5:
506: appendDescriptor(FIELD_DESCRIPTOR, "N");
507: break;
508: case 6:
509: appendDescriptor(FIELD_DESCRIPTOR, "U");
510: break;
511: }
512: } else {
513: appendLabel((Label) o[i]);
514: }
515: }
516: }
517:
518: /**
519: * Appends the name of the given label to {@link #buf buf}. Creates a new
520: * label name if the given label does not yet have one.
521: *
522: * @param l a label.
523: */
524: protected void appendLabel(final Label l) {
525: String name = (String) labelNames.get(l);
526: if (name == null) {
527: name = "L" + labelNames.size();
528: labelNames.put(l, name);
529: }
530: buf.append(name);
531: }
532: }
|