• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // ASM: a very small and fast Java bytecode manipulation framework
2 // Copyright (c) 2000-2011 INRIA, France Telecom
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // 1. Redistributions of source code must retain the above copyright
9 //    notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 //    notice, this list of conditions and the following disclaimer in the
12 //    documentation and/or other materials provided with the distribution.
13 // 3. Neither the name of the copyright holders nor the names of its
14 //    contributors may be used to endorse or promote products derived from
15 //    this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 // THE POSSIBILITY OF SUCH DAMAGE.
28 package org.objectweb.asm.signature;
29 
30 import org.objectweb.asm.Opcodes;
31 
32 /**
33  * A SignatureVisitor that generates signature literals, as defined in the Java Virtual Machine
34  * Specification (JVMS).
35  *
36  * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1">JVMS
37  *     4.7.9.1</a>
38  * @author Thomas Hallgren
39  * @author Eric Bruneton
40  */
41 public class SignatureWriter extends SignatureVisitor {
42 
43   /** The builder used to construct the visited signature. */
44   private final StringBuilder stringBuilder;
45 
46   /** Whether the visited signature contains formal type parameters. */
47   private boolean hasFormals;
48 
49   /** Whether the visited signature contains method parameter types. */
50   private boolean hasParameters;
51 
52   /**
53    * The stack used to keep track of class types that have arguments. Each element of this stack is
54    * a boolean encoded in one bit. The top of the stack is the least significant bit. The bottom of
55    * the stack is a sentinel element always equal to 1 (used to detect when the stack is full).
56    * Pushing false = {@code <<= 1}, pushing true = {@code ( <<= 1) | 1}, popping = {@code >>>= 1}.
57    *
58    * <p>Class type arguments must be surrounded with '&lt;' and '&gt;' and, because
59    *
60    * <ol>
61    *   <li>class types can be nested (because type arguments can themselves be class types),
62    *   <li>SignatureWriter always returns 'this' in each visit* method (to avoid allocating new
63    *       SignatureWriter instances),
64    * </ol>
65    *
66    * <p>we need a stack to properly balance these angle brackets. A new element is pushed on this
67    * stack for each new visited type, and popped when the visit of this type ends (either in
68    * visitEnd, or because visitInnerClassType is called).
69    */
70   private int argumentStack = 1;
71 
72   /** Constructs a new {@link SignatureWriter}. */
SignatureWriter()73   public SignatureWriter() {
74     this(new StringBuilder());
75   }
76 
SignatureWriter(final StringBuilder stringBuilder)77   private SignatureWriter(final StringBuilder stringBuilder) {
78     super(/* latest api =*/ Opcodes.ASM9);
79     this.stringBuilder = stringBuilder;
80   }
81 
82   // -----------------------------------------------------------------------------------------------
83   // Implementation of the SignatureVisitor interface
84   // -----------------------------------------------------------------------------------------------
85 
86   @Override
visitFormalTypeParameter(final String name)87   public void visitFormalTypeParameter(final String name) {
88     if (!hasFormals) {
89       hasFormals = true;
90       stringBuilder.append('<');
91     }
92     stringBuilder.append(name);
93     stringBuilder.append(':');
94   }
95 
96   @Override
visitClassBound()97   public SignatureVisitor visitClassBound() {
98     return this;
99   }
100 
101   @Override
visitInterfaceBound()102   public SignatureVisitor visitInterfaceBound() {
103     stringBuilder.append(':');
104     return this;
105   }
106 
107   @Override
visitSuperclass()108   public SignatureVisitor visitSuperclass() {
109     endFormals();
110     return this;
111   }
112 
113   @Override
visitInterface()114   public SignatureVisitor visitInterface() {
115     return this;
116   }
117 
118   @Override
visitParameterType()119   public SignatureVisitor visitParameterType() {
120     endFormals();
121     if (!hasParameters) {
122       hasParameters = true;
123       stringBuilder.append('(');
124     }
125     return this;
126   }
127 
128   @Override
visitReturnType()129   public SignatureVisitor visitReturnType() {
130     endFormals();
131     if (!hasParameters) {
132       stringBuilder.append('(');
133     }
134     stringBuilder.append(')');
135     return this;
136   }
137 
138   @Override
visitExceptionType()139   public SignatureVisitor visitExceptionType() {
140     stringBuilder.append('^');
141     return this;
142   }
143 
144   @Override
visitBaseType(final char descriptor)145   public void visitBaseType(final char descriptor) {
146     stringBuilder.append(descriptor);
147   }
148 
149   @Override
visitTypeVariable(final String name)150   public void visitTypeVariable(final String name) {
151     stringBuilder.append('T');
152     stringBuilder.append(name);
153     stringBuilder.append(';');
154   }
155 
156   @Override
visitArrayType()157   public SignatureVisitor visitArrayType() {
158     stringBuilder.append('[');
159     return this;
160   }
161 
162   @Override
visitClassType(final String name)163   public void visitClassType(final String name) {
164     stringBuilder.append('L');
165     stringBuilder.append(name);
166     // Pushes 'false' on the stack, meaning that this type does not have type arguments (as far as
167     // we can tell at this point).
168     argumentStack <<= 1;
169   }
170 
171   @Override
visitInnerClassType(final String name)172   public void visitInnerClassType(final String name) {
173     endArguments();
174     stringBuilder.append('.');
175     stringBuilder.append(name);
176     // Pushes 'false' on the stack, meaning that this type does not have type arguments (as far as
177     // we can tell at this point).
178     argumentStack <<= 1;
179   }
180 
181   @Override
visitTypeArgument()182   public void visitTypeArgument() {
183     // If the top of the stack is 'false', this means we are visiting the first type argument of the
184     // currently visited type. We therefore need to append a '<', and to replace the top stack
185     // element with 'true' (meaning that the current type does have type arguments).
186     if ((argumentStack & 1) == 0) {
187       argumentStack |= 1;
188       stringBuilder.append('<');
189     }
190     stringBuilder.append('*');
191   }
192 
193   @Override
visitTypeArgument(final char wildcard)194   public SignatureVisitor visitTypeArgument(final char wildcard) {
195     // If the top of the stack is 'false', this means we are visiting the first type argument of the
196     // currently visited type. We therefore need to append a '<', and to replace the top stack
197     // element with 'true' (meaning that the current type does have type arguments).
198     if ((argumentStack & 1) == 0) {
199       argumentStack |= 1;
200       stringBuilder.append('<');
201     }
202     if (wildcard != '=') {
203       stringBuilder.append(wildcard);
204     }
205     // If the stack is full, start a nested one by returning a new SignatureWriter.
206     return (argumentStack & (1 << 31)) == 0 ? this : new SignatureWriter(stringBuilder);
207   }
208 
209   @Override
visitEnd()210   public void visitEnd() {
211     endArguments();
212     stringBuilder.append(';');
213   }
214 
215   /**
216    * Returns the signature that was built by this signature writer.
217    *
218    * @return the signature that was built by this signature writer.
219    */
220   @Override
toString()221   public String toString() {
222     return stringBuilder.toString();
223   }
224 
225   // -----------------------------------------------------------------------------------------------
226   // Utility methods
227   // -----------------------------------------------------------------------------------------------
228 
229   /** Ends the formal type parameters section of the signature. */
endFormals()230   private void endFormals() {
231     if (hasFormals) {
232       hasFormals = false;
233       stringBuilder.append('>');
234     }
235   }
236 
237   /** Ends the type arguments of a class or inner class type. */
endArguments()238   private void endArguments() {
239     // If the top of the stack is 'true', this means that some type arguments have been visited for
240     // the type whose visit is now ending. We therefore need to append a '>', and to pop one element
241     // from the stack.
242     if ((argumentStack & 1) == 1) {
243       stringBuilder.append('>');
244     }
245     argumentStack >>>= 1;
246   }
247 }
248