1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /* 26 * This file is available under and governed by the GNU General Public 27 * License version 2 only, as published by the Free Software Foundation. 28 * However, the following notice accompanied the original version of this 29 * file: 30 * 31 * ASM: a very small and fast Java bytecode manipulation framework 32 * Copyright (c) 2000-2011 INRIA, France Telecom 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. Neither the name of the copyright holders nor the names of its 44 * contributors may be used to endorse or promote products derived from 45 * this software without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 57 * THE POSSIBILITY OF SUCH DAMAGE. 58 */ 59 package jdk.internal.org.objectweb.asm.util; 60 61 import java.util.Collections; 62 import java.util.HashMap; 63 import java.util.Map; 64 import jdk.internal.org.objectweb.asm.Opcodes; 65 import jdk.internal.org.objectweb.asm.signature.SignatureVisitor; 66 67 /** 68 * A {@link SignatureVisitor} that builds the Java generic type declaration corresponding to the 69 * signature it visits. 70 * 71 * @author Eugene Kuleshov 72 * @author Eric Bruneton 73 */ 74 public final class TraceSignatureVisitor extends SignatureVisitor { 75 76 private static final String COMMA_SEPARATOR = ", "; 77 private static final String EXTENDS_SEPARATOR = " extends "; 78 private static final String IMPLEMENTS_SEPARATOR = " implements "; 79 80 private static final Map<Character, String> BASE_TYPES; 81 82 static { 83 HashMap<Character, String> baseTypes = new HashMap<>(); 84 baseTypes.put('Z', "boolean"); 85 baseTypes.put('B', "byte"); 86 baseTypes.put('C', "char"); 87 baseTypes.put('S', "short"); 88 baseTypes.put('I', "int"); 89 baseTypes.put('J', "long"); 90 baseTypes.put('F', "float"); 91 baseTypes.put('D', "double"); 92 baseTypes.put('V', "void"); 93 BASE_TYPES = Collections.unmodifiableMap(baseTypes); 94 } 95 96 /** Whether the visited signature is a class signature of a Java interface. */ 97 private final boolean isInterface; 98 99 /** The Java generic type declaration corresponding to the visited signature. */ 100 private final StringBuilder declaration; 101 102 /** The Java generic method return type declaration corresponding to the visited signature. */ 103 private StringBuilder returnType; 104 105 /** The Java generic exception types declaration corresponding to the visited signature. */ 106 private StringBuilder exceptions; 107 108 /** Whether {@link #visitFormalTypeParameter} has been called. */ 109 private boolean formalTypeParameterVisited; 110 111 /** Whether {@link #visitInterfaceBound} has been called. */ 112 private boolean interfaceBoundVisited; 113 114 /** Whether {@link #visitParameterType} has been called. */ 115 private boolean parameterTypeVisited; 116 117 /** Whether {@link #visitInterface} has been called. */ 118 private boolean interfaceVisited; 119 120 /** 121 * The stack used to keep track of class types that have arguments. Each element of this stack is 122 * a boolean encoded in one bit. The top of the stack is the least significant bit. Pushing false 123 * = *2, pushing true = *2+1, popping = /2. 124 */ 125 private int argumentStack; 126 127 /** 128 * The stack used to keep track of array class types. Each element of this stack is a boolean 129 * encoded in one bit. The top of the stack is the lowest order bit. Pushing false = *2, pushing 130 * true = *2+1, popping = /2. 131 */ 132 private int arrayStack; 133 134 /** The separator to append before the next visited class or inner class type. */ 135 private String separator = ""; 136 137 /** 138 * Constructs a new {@link TraceSignatureVisitor}. 139 * 140 * @param accessFlags for class type signatures, the access flags of the class. 141 */ TraceSignatureVisitor(final int accessFlags)142 public TraceSignatureVisitor(final int accessFlags) { 143 super(/* latest api = */ Opcodes.ASM8); 144 this.isInterface = (accessFlags & Opcodes.ACC_INTERFACE) != 0; 145 this.declaration = new StringBuilder(); 146 } 147 TraceSignatureVisitor(final StringBuilder stringBuilder)148 private TraceSignatureVisitor(final StringBuilder stringBuilder) { 149 super(/* latest api = */ Opcodes.ASM8); 150 this.isInterface = false; 151 this.declaration = stringBuilder; 152 } 153 154 @Override visitFormalTypeParameter(final String name)155 public void visitFormalTypeParameter(final String name) { 156 declaration.append(formalTypeParameterVisited ? COMMA_SEPARATOR : "<").append(name); 157 formalTypeParameterVisited = true; 158 interfaceBoundVisited = false; 159 } 160 161 @Override visitClassBound()162 public SignatureVisitor visitClassBound() { 163 separator = EXTENDS_SEPARATOR; 164 startType(); 165 return this; 166 } 167 168 @Override visitInterfaceBound()169 public SignatureVisitor visitInterfaceBound() { 170 separator = interfaceBoundVisited ? COMMA_SEPARATOR : EXTENDS_SEPARATOR; 171 interfaceBoundVisited = true; 172 startType(); 173 return this; 174 } 175 176 @Override visitSuperclass()177 public SignatureVisitor visitSuperclass() { 178 endFormals(); 179 separator = EXTENDS_SEPARATOR; 180 startType(); 181 return this; 182 } 183 184 @Override visitInterface()185 public SignatureVisitor visitInterface() { 186 if (interfaceVisited) { 187 separator = COMMA_SEPARATOR; 188 } else { 189 separator = isInterface ? EXTENDS_SEPARATOR : IMPLEMENTS_SEPARATOR; 190 interfaceVisited = true; 191 } 192 startType(); 193 return this; 194 } 195 196 @Override visitParameterType()197 public SignatureVisitor visitParameterType() { 198 endFormals(); 199 if (parameterTypeVisited) { 200 declaration.append(COMMA_SEPARATOR); 201 } else { 202 declaration.append('('); 203 parameterTypeVisited = true; 204 } 205 startType(); 206 return this; 207 } 208 209 @Override visitReturnType()210 public SignatureVisitor visitReturnType() { 211 endFormals(); 212 if (parameterTypeVisited) { 213 parameterTypeVisited = false; 214 } else { 215 declaration.append('('); 216 } 217 declaration.append(')'); 218 returnType = new StringBuilder(); 219 return new TraceSignatureVisitor(returnType); 220 } 221 222 @Override visitExceptionType()223 public SignatureVisitor visitExceptionType() { 224 if (exceptions == null) { 225 exceptions = new StringBuilder(); 226 } else { 227 exceptions.append(COMMA_SEPARATOR); 228 } 229 return new TraceSignatureVisitor(exceptions); 230 } 231 232 @Override visitBaseType(final char descriptor)233 public void visitBaseType(final char descriptor) { 234 String baseType = BASE_TYPES.get(descriptor); 235 if (baseType == null) { 236 throw new IllegalArgumentException(); 237 } 238 declaration.append(baseType); 239 endType(); 240 } 241 242 @Override visitTypeVariable(final String name)243 public void visitTypeVariable(final String name) { 244 declaration.append(separator).append(name); 245 separator = ""; 246 endType(); 247 } 248 249 @Override visitArrayType()250 public SignatureVisitor visitArrayType() { 251 startType(); 252 arrayStack |= 1; 253 return this; 254 } 255 256 @Override visitClassType(final String name)257 public void visitClassType(final String name) { 258 if ("java/lang/Object".equals(name)) { 259 // 'Map<java.lang.Object,java.util.List>' or 'abstract public V get(Object key);' should have 260 // Object 'but java.lang.String extends java.lang.Object' is unnecessary. 261 boolean needObjectClass = argumentStack % 2 != 0 || parameterTypeVisited; 262 if (needObjectClass) { 263 declaration.append(separator).append(name.replace('/', '.')); 264 } 265 } else { 266 declaration.append(separator).append(name.replace('/', '.')); 267 } 268 separator = ""; 269 argumentStack *= 2; 270 } 271 272 @Override visitInnerClassType(final String name)273 public void visitInnerClassType(final String name) { 274 if (argumentStack % 2 != 0) { 275 declaration.append('>'); 276 } 277 argumentStack /= 2; 278 declaration.append('.'); 279 declaration.append(separator).append(name.replace('/', '.')); 280 separator = ""; 281 argumentStack *= 2; 282 } 283 284 @Override visitTypeArgument()285 public void visitTypeArgument() { 286 if (argumentStack % 2 == 0) { 287 ++argumentStack; 288 declaration.append('<'); 289 } else { 290 declaration.append(COMMA_SEPARATOR); 291 } 292 declaration.append('?'); 293 } 294 295 @Override visitTypeArgument(final char tag)296 public SignatureVisitor visitTypeArgument(final char tag) { 297 if (argumentStack % 2 == 0) { 298 ++argumentStack; 299 declaration.append('<'); 300 } else { 301 declaration.append(COMMA_SEPARATOR); 302 } 303 304 if (tag == EXTENDS) { 305 declaration.append("? extends "); 306 } else if (tag == SUPER) { 307 declaration.append("? super "); 308 } 309 310 startType(); 311 return this; 312 } 313 314 @Override visitEnd()315 public void visitEnd() { 316 if (argumentStack % 2 != 0) { 317 declaration.append('>'); 318 } 319 argumentStack /= 2; 320 endType(); 321 } 322 323 // ----------------------------------------------------------------------------------------------- 324 325 /** 326 * Returns the Java generic type declaration corresponding to the visited signature. 327 * 328 * @return the Java generic type declaration corresponding to the visited signature. 329 */ getDeclaration()330 public String getDeclaration() { 331 return declaration.toString(); 332 } 333 334 /** 335 * Returns the Java generic method return type declaration corresponding to the visited signature. 336 * 337 * @return the Java generic method return type declaration corresponding to the visited signature. 338 */ getReturnType()339 public String getReturnType() { 340 return returnType == null ? null : returnType.toString(); 341 } 342 343 /** 344 * Returns the Java generic exception types declaration corresponding to the visited signature. 345 * 346 * @return the Java generic exception types declaration corresponding to the visited signature. 347 */ getExceptions()348 public String getExceptions() { 349 return exceptions == null ? null : exceptions.toString(); 350 } 351 352 // ----------------------------------------------------------------------------------------------- 353 endFormals()354 private void endFormals() { 355 if (formalTypeParameterVisited) { 356 declaration.append('>'); 357 formalTypeParameterVisited = false; 358 } 359 } 360 startType()361 private void startType() { 362 arrayStack *= 2; 363 } 364 endType()365 private void endType() { 366 if (arrayStack % 2 == 0) { 367 arrayStack /= 2; 368 } else { 369 while (arrayStack % 2 != 0) { 370 arrayStack /= 2; 371 declaration.append("[]"); 372 } 373 } 374 } 375 } 376