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 '<' and '>' 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