1/*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.  Oracle designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Oracle in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25/*
26 * This file is available under and governed by the GNU General Public
27 * License version 2 only, as published by the Free Software Foundation.
28 * However, the following notice accompanied the original version of this
29 * file:
30 *
31 * ASM: a very small and fast Java bytecode manipulation framework
32 * Copyright (c) 2000-2011 INRIA, France Telecom
33 * All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 *    notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 *    notice, this list of conditions and the following disclaimer in the
42 *    documentation and/or other materials provided with the distribution.
43 * 3. Neither the name of the copyright holders nor the names of its
44 *    contributors may be used to endorse or promote products derived from
45 *    this software without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
57 * THE POSSIBILITY OF SUCH DAMAGE.
58 */
59package jdk.internal.org.objectweb.asm.util;
60
61import jdk.internal.org.objectweb.asm.Opcodes;
62import jdk.internal.org.objectweb.asm.signature.SignatureVisitor;
63
64/**
65 * A {@link SignatureVisitor} that prints a disassembled view of the signature
66 * it visits.
67 *
68 * @author Eugene Kuleshov
69 * @author Eric Bruneton
70 */
71public final class TraceSignatureVisitor extends SignatureVisitor {
72
73    private final StringBuilder declaration;
74
75    private boolean isInterface;
76
77    private boolean seenFormalParameter;
78
79    private boolean seenInterfaceBound;
80
81    private boolean seenParameter;
82
83    private boolean seenInterface;
84
85    private StringBuilder returnType;
86
87    private StringBuilder exceptions;
88
89    /**
90     * Stack used to keep track of class types that have arguments. Each element
91     * of this stack is a boolean encoded in one bit. The top of the stack is
92     * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
93     * /2.
94     */
95    private int argumentStack;
96
97    /**
98     * Stack used to keep track of array class types. Each element of this stack
99     * is a boolean encoded in one bit. The top of the stack is the lowest order
100     * bit. Pushing false = *2, pushing true = *2+1, popping = /2.
101     */
102    private int arrayStack;
103
104    private String separator = "";
105
106    public TraceSignatureVisitor(final int access) {
107        super(Opcodes.ASM5);
108        isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
109        this.declaration = new StringBuilder();
110    }
111
112    private TraceSignatureVisitor(final StringBuilder buf) {
113        super(Opcodes.ASM5);
114        this.declaration = buf;
115    }
116
117    @Override
118    public void visitFormalTypeParameter(final String name) {
119        declaration.append(seenFormalParameter ? ", " : "<").append(name);
120        seenFormalParameter = true;
121        seenInterfaceBound = false;
122    }
123
124    @Override
125    public SignatureVisitor visitClassBound() {
126        separator = " extends ";
127        startType();
128        return this;
129    }
130
131    @Override
132    public SignatureVisitor visitInterfaceBound() {
133        separator = seenInterfaceBound ? ", " : " extends ";
134        seenInterfaceBound = true;
135        startType();
136        return this;
137    }
138
139    @Override
140    public SignatureVisitor visitSuperclass() {
141        endFormals();
142        separator = " extends ";
143        startType();
144        return this;
145    }
146
147    @Override
148    public SignatureVisitor visitInterface() {
149        separator = seenInterface ? ", " : isInterface ? " extends "
150                : " implements ";
151        seenInterface = true;
152        startType();
153        return this;
154    }
155
156    @Override
157    public SignatureVisitor visitParameterType() {
158        endFormals();
159        if (seenParameter) {
160            declaration.append(", ");
161        } else {
162            seenParameter = true;
163            declaration.append('(');
164        }
165        startType();
166        return this;
167    }
168
169    @Override
170    public SignatureVisitor visitReturnType() {
171        endFormals();
172        if (seenParameter) {
173            seenParameter = false;
174        } else {
175            declaration.append('(');
176        }
177        declaration.append(')');
178        returnType = new StringBuilder();
179        return new TraceSignatureVisitor(returnType);
180    }
181
182    @Override
183    public SignatureVisitor visitExceptionType() {
184        if (exceptions == null) {
185            exceptions = new StringBuilder();
186        } else {
187            exceptions.append(", ");
188        }
189        // startType();
190        return new TraceSignatureVisitor(exceptions);
191    }
192
193    @Override
194    public void visitBaseType(final char descriptor) {
195        switch (descriptor) {
196        case 'V':
197            declaration.append("void");
198            break;
199        case 'B':
200            declaration.append("byte");
201            break;
202        case 'J':
203            declaration.append("long");
204            break;
205        case 'Z':
206            declaration.append("boolean");
207            break;
208        case 'I':
209            declaration.append("int");
210            break;
211        case 'S':
212            declaration.append("short");
213            break;
214        case 'C':
215            declaration.append("char");
216            break;
217        case 'F':
218            declaration.append("float");
219            break;
220        // case 'D':
221        default:
222            declaration.append("double");
223            break;
224        }
225        endType();
226    }
227
228    @Override
229    public void visitTypeVariable(final String name) {
230        declaration.append(name);
231        endType();
232    }
233
234    @Override
235    public SignatureVisitor visitArrayType() {
236        startType();
237        arrayStack |= 1;
238        return this;
239    }
240
241    @Override
242    public void visitClassType(final String name) {
243        if ("java/lang/Object".equals(name)) {
244            // Map<java.lang.Object,java.util.List>
245            // or
246            // abstract public V get(Object key); (seen in Dictionary.class)
247            // should have Object
248            // but java.lang.String extends java.lang.Object is unnecessary
249            boolean needObjectClass = argumentStack % 2 != 0 || seenParameter;
250            if (needObjectClass) {
251                declaration.append(separator).append(name.replace('/', '.'));
252            }
253        } else {
254            declaration.append(separator).append(name.replace('/', '.'));
255        }
256        separator = "";
257        argumentStack *= 2;
258    }
259
260    @Override
261    public void visitInnerClassType(final String name) {
262        if (argumentStack % 2 != 0) {
263            declaration.append('>');
264        }
265        argumentStack /= 2;
266        declaration.append('.');
267        declaration.append(separator).append(name.replace('/', '.'));
268        separator = "";
269        argumentStack *= 2;
270    }
271
272    @Override
273    public void visitTypeArgument() {
274        if (argumentStack % 2 == 0) {
275            ++argumentStack;
276            declaration.append('<');
277        } else {
278            declaration.append(", ");
279        }
280        declaration.append('?');
281    }
282
283    @Override
284    public SignatureVisitor visitTypeArgument(final char tag) {
285        if (argumentStack % 2 == 0) {
286            ++argumentStack;
287            declaration.append('<');
288        } else {
289            declaration.append(", ");
290        }
291
292        if (tag == EXTENDS) {
293            declaration.append("? extends ");
294        } else if (tag == SUPER) {
295            declaration.append("? super ");
296        }
297
298        startType();
299        return this;
300    }
301
302    @Override
303    public void visitEnd() {
304        if (argumentStack % 2 != 0) {
305            declaration.append('>');
306        }
307        argumentStack /= 2;
308        endType();
309    }
310
311    public String getDeclaration() {
312        return declaration.toString();
313    }
314
315    public String getReturnType() {
316        return returnType == null ? null : returnType.toString();
317    }
318
319    public String getExceptions() {
320        return exceptions == null ? null : exceptions.toString();
321    }
322
323    // -----------------------------------------------
324
325    private void endFormals() {
326        if (seenFormalParameter) {
327            declaration.append('>');
328            seenFormalParameter = false;
329        }
330    }
331
332    private void startType() {
333        arrayStack *= 2;
334    }
335
336    private void endType() {
337        if (arrayStack % 2 == 0) {
338            arrayStack /= 2;
339        } else {
340            while (arrayStack % 2 != 0) {
341                arrayStack /= 2;
342                declaration.append("[]");
343            }
344        }
345    }
346}
347