001// ASM: a very small and fast Java bytecode manipulation framework
002// Copyright (c) 2000-2011 INRIA, France Telecom
003// All rights reserved.
004//
005// Redistribution and use in source and binary forms, with or without
006// modification, are permitted provided that the following conditions
007// are met:
008// 1. Redistributions of source code must retain the above copyright
009//    notice, this list of conditions and the following disclaimer.
010// 2. Redistributions in binary form must reproduce the above copyright
011//    notice, this list of conditions and the following disclaimer in the
012//    documentation and/or other materials provided with the distribution.
013// 3. Neither the name of the copyright holders nor the names of its
014//    contributors may be used to endorse or promote products derived from
015//    this software without specific prior written permission.
016//
017// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
021// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
022// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
023// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
024// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
025// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
026// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
027// THE POSSIBILITY OF SUCH DAMAGE.
028package io.ebean.enhance.asm.signature;
029
030import io.ebean.enhance.asm.Opcodes;
031
032/**
033 * A SignatureVisitor that generates signature literals, as defined in the Java Virtual Machine
034 * Specification (JVMS).
035 *
036 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1">JVMS
037 *     4.7.9.1</a>
038 * @author Thomas Hallgren
039 * @author Eric Bruneton
040 */
041public class SignatureWriter extends SignatureVisitor {
042
043  /** The builder used to construct the visited signature. */
044  private final StringBuilder stringBuilder = new StringBuilder();
045
046  /** Whether the visited signature contains formal type parameters. */
047  private boolean hasFormals;
048
049  /** Whether the visited signature contains method parameter types. */
050  private boolean hasParameters;
051
052  /**
053   * The stack used to keep track of class types that have arguments. Each element of this stack is
054   * a boolean encoded in one bit. The top of the stack is the least significant bit. Pushing false
055   * = *2, pushing true = *2+1, popping = /2.
056   *
057   * <p>Class type arguments must be surrounded with '&lt;' and '&gt;' and, because
058   *
059   * <ol>
060   *   <li>class types can be nested (because type arguments can themselves be class types),
061   *   <li>SignatureWriter always returns 'this' in each visit* method (to avoid allocating new
062   *       SignatureWriter instances),
063   * </ol>
064   *
065   * <p>we need a stack to properly balance these 'parentheses'. A new element is pushed on this
066   * stack for each new visited type, and popped when the visit of this type ends (either is
067   * visitEnd, or because visitInnerClassType is called).
068   */
069  private int argumentStack;
070
071  /** Constructs a new {@link SignatureWriter}. */
072  public SignatureWriter() {
073    super(Opcodes.ASM7);
074  }
075
076  // -----------------------------------------------------------------------------------------------
077  // Implementation of the SignatureVisitor interface
078  // -----------------------------------------------------------------------------------------------
079
080  @Override
081  public void visitFormalTypeParameter(final String name) {
082    if (!hasFormals) {
083      hasFormals = true;
084      stringBuilder.append('<');
085    }
086    stringBuilder.append(name);
087    stringBuilder.append(':');
088  }
089
090  @Override
091  public SignatureVisitor visitClassBound() {
092    return this;
093  }
094
095  @Override
096  public SignatureVisitor visitInterfaceBound() {
097    stringBuilder.append(':');
098    return this;
099  }
100
101  @Override
102  public SignatureVisitor visitSuperclass() {
103    endFormals();
104    return this;
105  }
106
107  @Override
108  public SignatureVisitor visitInterface() {
109    return this;
110  }
111
112  @Override
113  public SignatureVisitor visitParameterType() {
114    endFormals();
115    if (!hasParameters) {
116      hasParameters = true;
117      stringBuilder.append('(');
118    }
119    return this;
120  }
121
122  @Override
123  public SignatureVisitor visitReturnType() {
124    endFormals();
125    if (!hasParameters) {
126      stringBuilder.append('(');
127    }
128    stringBuilder.append(')');
129    return this;
130  }
131
132  @Override
133  public SignatureVisitor visitExceptionType() {
134    stringBuilder.append('^');
135    return this;
136  }
137
138  @Override
139  public void visitBaseType(final char descriptor) {
140    stringBuilder.append(descriptor);
141  }
142
143  @Override
144  public void visitTypeVariable(final String name) {
145    stringBuilder.append('T');
146    stringBuilder.append(name);
147    stringBuilder.append(';');
148  }
149
150  @Override
151  public SignatureVisitor visitArrayType() {
152    stringBuilder.append('[');
153    return this;
154  }
155
156  @Override
157  public void visitClassType(final String name) {
158    stringBuilder.append('L');
159    stringBuilder.append(name);
160    // Pushes 'false' on the stack, meaning that this type does not have type arguments (as far as
161    // we can tell at this point).
162    argumentStack *= 2;
163  }
164
165  @Override
166  public void visitInnerClassType(final String name) {
167    endArguments();
168    stringBuilder.append('.');
169    stringBuilder.append(name);
170    // Pushes 'false' on the stack, meaning that this type does not have type arguments (as far as
171    // we can tell at this point).
172    argumentStack *= 2;
173  }
174
175  @Override
176  public void visitTypeArgument() {
177    // If the top of the stack is 'false', this means we are visiting the first type argument of the
178    // currently visited type. We therefore need to append a '<', and to replace the top stack
179    // element with 'true' (meaning that the current type does have type arguments).
180    if (argumentStack % 2 == 0) {
181      argumentStack |= 1;
182      stringBuilder.append('<');
183    }
184    stringBuilder.append('*');
185  }
186
187  @Override
188  public SignatureVisitor visitTypeArgument(final char wildcard) {
189    // If the top of the stack is 'false', this means we are visiting the first type argument of the
190    // currently visited type. We therefore need to append a '<', and to replace the top stack
191    // element with 'true' (meaning that the current type does have type arguments).
192    if (argumentStack % 2 == 0) {
193      argumentStack |= 1;
194      stringBuilder.append('<');
195    }
196    if (wildcard != '=') {
197      stringBuilder.append(wildcard);
198    }
199    return this;
200  }
201
202  @Override
203  public void visitEnd() {
204    endArguments();
205    stringBuilder.append(';');
206  }
207
208  /**
209   * Returns the signature that was built by this signature writer.
210   *
211   * @return the signature that was built by this signature writer.
212   */
213  @Override
214  public String toString() {
215    return stringBuilder.toString();
216  }
217
218  // -----------------------------------------------------------------------------------------------
219  // Utility methods
220  // -----------------------------------------------------------------------------------------------
221
222  /** Ends the formal type parameters section of the signature. */
223  private void endFormals() {
224    if (hasFormals) {
225      hasFormals = false;
226      stringBuilder.append('>');
227    }
228  }
229
230  /** Ends the type arguments of a class or inner class type. */
231  private void endArguments() {
232    // If the top of the stack is 'true', this means that some type arguments have been visited for
233    // the type whose visit is now ending. We therefore need to append a '>', and to pop one element
234    // from the stack.
235    if (argumentStack % 2 == 1) {
236      stringBuilder.append('>');
237    }
238    argumentStack /= 2;
239  }
240}