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.Opcodes;
33 import org.mockito.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  */
42 public class TraceSignatureVisitor implements SignatureVisitor {
43 
44     private final 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 
TraceSignatureVisitor(final int access)77     public TraceSignatureVisitor(final int access) {
78         isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
79         this.declaration = new StringBuffer();
80     }
81 
TraceSignatureVisitor(final StringBuffer buf)82     private TraceSignatureVisitor(final StringBuffer buf) {
83         this.declaration = buf;
84     }
85 
visitFormalTypeParameter(final String name)86     public void visitFormalTypeParameter(final String name) {
87         declaration.append(seenFormalParameter ? ", " : "<").append(name);
88         seenFormalParameter = true;
89         seenInterfaceBound = false;
90     }
91 
visitClassBound()92     public SignatureVisitor visitClassBound() {
93         separator = " extends ";
94         startType();
95         return this;
96     }
97 
visitInterfaceBound()98     public SignatureVisitor visitInterfaceBound() {
99         separator = seenInterfaceBound ? ", " : " extends ";
100         seenInterfaceBound = true;
101         startType();
102         return this;
103     }
104 
visitSuperclass()105     public SignatureVisitor visitSuperclass() {
106         endFormals();
107         separator = " extends ";
108         startType();
109         return this;
110     }
111 
visitInterface()112     public SignatureVisitor visitInterface() {
113         separator = seenInterface ? ", " : isInterface
114                 ? " extends "
115                 : " implements ";
116         seenInterface = true;
117         startType();
118         return this;
119     }
120 
visitParameterType()121     public SignatureVisitor visitParameterType() {
122         endFormals();
123         if (seenParameter) {
124             declaration.append(", ");
125         } else {
126             seenParameter = true;
127             declaration.append('(');
128         }
129         startType();
130         return this;
131     }
132 
visitReturnType()133     public SignatureVisitor visitReturnType() {
134         endFormals();
135         if (seenParameter) {
136             seenParameter = false;
137         } else {
138             declaration.append('(');
139         }
140         declaration.append(')');
141         returnType = new StringBuffer();
142         return new TraceSignatureVisitor(returnType);
143     }
144 
visitExceptionType()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 
visitBaseType(final char descriptor)155     public void visitBaseType(final 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 
visitTypeVariable(final String name)189     public void visitTypeVariable(final String name) {
190         declaration.append(name);
191         endType();
192     }
193 
visitArrayType()194     public SignatureVisitor visitArrayType() {
195         startType();
196         arrayStack |= 1;
197         return this;
198     }
199 
visitClassType(final String name)200     public void visitClassType(final String name) {
201         if ("java/lang/Object".equals(name)) {
202             // Map<java.lang.Object,java.util.List>
203             // or
204             // abstract public V get(Object key); (seen in Dictionary.class)
205             // should have Object
206             // but java.lang.String extends java.lang.Object is unnecessary
207             boolean needObjectClass = argumentStack % 2 != 0 || seenParameter;
208             if (needObjectClass) {
209                 declaration.append(separator).append(name.replace('/', '.'));
210             }
211         } else {
212             declaration.append(separator).append(name.replace('/', '.'));
213         }
214         separator = "";
215         argumentStack *= 2;
216     }
217 
visitInnerClassType(final String name)218     public void visitInnerClassType(final String name) {
219         if (argumentStack % 2 != 0) {
220             declaration.append('>');
221         }
222         argumentStack /= 2;
223         declaration.append('.');
224         declaration.append(separator).append(name.replace('/', '.'));
225         separator = "";
226         argumentStack *= 2;
227     }
228 
visitTypeArgument()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 
visitTypeArgument(final char tag)239     public SignatureVisitor visitTypeArgument(final char tag) {
240         if (argumentStack % 2 == 0) {
241             ++argumentStack;
242             declaration.append('<');
243         } else {
244             declaration.append(", ");
245         }
246 
247         if (tag == EXTENDS) {
248             declaration.append("? extends ");
249         } else if (tag == SUPER) {
250             declaration.append("? super ");
251         }
252 
253         startType();
254         return this;
255     }
256 
visitEnd()257     public void visitEnd() {
258         if (argumentStack % 2 != 0) {
259             declaration.append('>');
260         }
261         argumentStack /= 2;
262         endType();
263     }
264 
getDeclaration()265     public String getDeclaration() {
266         return declaration.toString();
267     }
268 
getReturnType()269     public String getReturnType() {
270         return returnType == null ? null : returnType.toString();
271     }
272 
getExceptions()273     public String getExceptions() {
274         return exceptions == null ? null : exceptions.toString();
275     }
276 
277     // -----------------------------------------------
278 
endFormals()279     private void endFormals() {
280         if (seenFormalParameter) {
281             declaration.append('>');
282             seenFormalParameter = false;
283         }
284     }
285 
startType()286     private void startType() {
287         arrayStack *= 2;
288     }
289 
endType()290     private void endType() {
291         if (arrayStack % 2 == 0) {
292             arrayStack /= 2;
293         } else {
294             while (arrayStack % 2 != 0) {
295                 arrayStack /= 2;
296                 declaration.append("[]");
297             }
298         }
299     }
300 }
301