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