1/***
2 * ASM: a very small and fast Java bytecode manipulation framework
3 * Copyright (c) 2000-2005 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.objectweb.asm.util;
31
32import org.objectweb.asm.Opcodes;
33import org.objectweb.asm.signature.SignatureVisitor;
34
35/**
36 * A {@link SignatureVisitor} that prints a disassembled view of the signature
37 * it visits.
38 *
39 * @author Eugene Kuleshov
40 * @author Eric Bruneton
41 */
42public class TraceSignatureVisitor implements SignatureVisitor {
43
44    private StringBuffer declaration;
45
46    private boolean isInterface;
47
48    private boolean seenFormalParameter;
49
50    private boolean seenInterfaceBound;
51
52    private boolean seenParameter;
53
54    private boolean seenInterface;
55
56    private StringBuffer returnType;
57
58    private StringBuffer exceptions;
59
60    /**
61     * Stack used to keep track of class types that have arguments. Each element
62     * of this stack is a boolean encoded in one bit. The top of the stack is
63     * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
64     * /2.
65     */
66    private int argumentStack;
67
68    /**
69     * Stack used to keep track of array class types. Each element of this stack
70     * is a boolean encoded in one bit. The top of the stack is the lowest order
71     * bit. Pushing false = *2, pushing true = *2+1, popping = /2.
72     */
73    private int arrayStack;
74
75    private String separator = "";
76
77    public TraceSignatureVisitor(int access) {
78        isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
79        this.declaration = new StringBuffer();
80    }
81
82    private TraceSignatureVisitor(StringBuffer buf) {
83        this.declaration = buf;
84    }
85
86    public void visitFormalTypeParameter(String name) {
87        declaration.append(seenFormalParameter ? ", " : "<").append(name);
88        seenFormalParameter = true;
89        seenInterfaceBound = false;
90    }
91
92    public SignatureVisitor visitClassBound() {
93        separator = " extends ";
94        startType();
95        return this;
96    }
97
98    public SignatureVisitor visitInterfaceBound() {
99        separator = seenInterfaceBound ? ", " : " extends ";
100        seenInterfaceBound = true;
101        startType();
102        return this;
103    }
104
105    public SignatureVisitor visitSuperclass() {
106        endFormals();
107        separator = " extends ";
108        startType();
109        return this;
110    }
111
112    public SignatureVisitor visitInterface() {
113        separator = seenInterface ? ", " : (isInterface
114                ? " extends "
115                : " implements ");
116        seenInterface = true;
117        startType();
118        return this;
119    }
120
121    public SignatureVisitor visitParameterType() {
122        endFormals();
123        if (!seenParameter) {
124            seenParameter = true;
125            declaration.append('(');
126        } else {
127            declaration.append(", ");
128        }
129        startType();
130        return this;
131    }
132
133    public SignatureVisitor visitReturnType() {
134        endFormals();
135        if (!seenParameter) {
136            declaration.append('(');
137        } else {
138            seenParameter = false;
139        }
140        declaration.append(')');
141        returnType = new StringBuffer();
142        return new TraceSignatureVisitor(returnType);
143    }
144
145    public SignatureVisitor visitExceptionType() {
146        if (exceptions == null) {
147            exceptions = new StringBuffer();
148        } else {
149            exceptions.append(", ");
150        }
151        // startType();
152        return new TraceSignatureVisitor(exceptions);
153    }
154
155    public void visitBaseType(char descriptor) {
156        switch (descriptor) {
157            case 'V':
158                declaration.append("void");
159                break;
160            case 'B':
161                declaration.append("byte");
162                break;
163            case 'J':
164                declaration.append("long");
165                break;
166            case 'Z':
167                declaration.append("boolean");
168                break;
169            case 'I':
170                declaration.append("int");
171                break;
172            case 'S':
173                declaration.append("short");
174                break;
175            case 'C':
176                declaration.append("char");
177                break;
178            case 'F':
179                declaration.append("float");
180                break;
181            // case 'D':
182            default:
183                declaration.append("double");
184                break;
185        }
186        endType();
187    }
188
189    public void visitTypeVariable(String name) {
190        declaration.append(name);
191        endType();
192    }
193
194    public SignatureVisitor visitArrayType() {
195        startType();
196        arrayStack |= 1;
197        return this;
198    }
199
200    public void visitClassType(String name) {
201        if (!"java/lang/Object".equals(name)) {
202            declaration.append(separator).append(name.replace('/', '.'));
203        } else {
204            // Map<java.lang.Object,java.util.List>
205            // or
206            // abstract public V get(Object key); (seen in Dictionary.class)
207            // should have Object
208            // but java.lang.String extends java.lang.Object is unnecessary
209            boolean needObjectClass = argumentStack % 2 == 1 || seenParameter;
210            if (needObjectClass) {
211                declaration.append(separator).append(name.replace('/', '.'));
212            }
213        }
214        separator = "";
215        argumentStack *= 2;
216    }
217
218    public void visitInnerClassType(String name) {
219        if (argumentStack % 2 == 1) {
220            declaration.append('>');
221        }
222        argumentStack /= 2;
223        declaration.append('.');
224        declaration.append(separator).append(name.replace('/', '.'));
225        separator = "";
226        argumentStack *= 2;
227    }
228
229    public void visitTypeArgument() {
230        if (argumentStack % 2 == 0) {
231            ++argumentStack;
232            declaration.append('<');
233        } else {
234            declaration.append(", ");
235        }
236        declaration.append('?');
237    }
238
239    public SignatureVisitor visitTypeArgument(char tag) {
240        if (argumentStack % 2 == 0) {
241            ++argumentStack;
242            declaration.append('<');
243        } else {
244            declaration.append(", ");
245        }
246
247        if (tag == SignatureVisitor.EXTENDS) {
248            declaration.append("? extends ");
249        } else if (tag == SignatureVisitor.SUPER) {
250            declaration.append("? super ");
251        }
252
253        startType();
254        return this;
255    }
256
257    public void visitEnd() {
258        if (argumentStack % 2 == 1) {
259            declaration.append('>');
260        }
261        argumentStack /= 2;
262        endType();
263    }
264
265    public String getDeclaration() {
266        return declaration.toString();
267    }
268
269    public String getReturnType() {
270        return returnType == null ? null : returnType.toString();
271    }
272
273    public String getExceptions() {
274        return exceptions == null ? null : exceptions.toString();
275    }
276
277    // -----------------------------------------------
278
279    private void endFormals() {
280        if (seenFormalParameter) {
281            declaration.append('>');
282            seenFormalParameter = false;
283        }
284    }
285
286    private void startType() {
287        arrayStack *= 2;
288    }
289
290    private void endType() {
291        if (arrayStack % 2 == 1) {
292            while (arrayStack % 2 == 1) {
293                arrayStack /= 2;
294                declaration.append("[]");
295            }
296        } else {
297            arrayStack /= 2;
298        }
299    }
300}
301