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 jdk.internal.org.objectweb.asm.Opcodes;
  62 import jdk.internal.org.objectweb.asm.signature.SignatureVisitor;
  63 
  64 /**
  65  * A {@link SignatureVisitor} that prints a disassembled view of the signature
  66  * it visits.
  67  *
  68  * @author Eugene Kuleshov
  69  * @author Eric Bruneton
  70  */
  71 public final class TraceSignatureVisitor extends SignatureVisitor {
  72 
  73     private final StringBuilder declaration;
  74 
  75     private boolean isInterface;
  76 
  77     private boolean seenFormalParameter;
  78 
  79     private boolean seenInterfaceBound;
  80 
  81     private boolean seenParameter;
  82 
  83     private boolean seenInterface;
  84 
  85     private StringBuilder returnType;
  86 
  87     private StringBuilder exceptions;
  88 
  89     /**
  90      * Stack used to keep track of class types that have arguments. Each element
  91      * of this stack is a boolean encoded in one bit. The top of the stack is
  92      * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
  93      * /2.
  94      */
  95     private int argumentStack;
  96 
  97     /**
  98      * Stack used to keep track of array class types. Each element of this stack
  99      * is a boolean encoded in one bit. The top of the stack is the lowest order
 100      * bit. Pushing false = *2, pushing true = *2+1, popping = /2.
 101      */
 102     private int arrayStack;
 103 
 104     private String separator = "";
 105 
 106     public TraceSignatureVisitor(final int access) {
 107         super(Opcodes.ASM6);
 108         isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
 109         this.declaration = new StringBuilder();
 110     }
 111 
 112     private TraceSignatureVisitor(final StringBuilder buf) {
 113         super(Opcodes.ASM6);
 114         this.declaration = buf;
 115     }
 116 
 117     @Override
 118     public void visitFormalTypeParameter(final String name) {
 119         declaration.append(seenFormalParameter ? ", " : "<").append(name);
 120         seenFormalParameter = true;
 121         seenInterfaceBound = false;
 122     }
 123 
 124     @Override
 125     public SignatureVisitor visitClassBound() {
 126         separator = " extends ";
 127         startType();
 128         return this;
 129     }
 130 
 131     @Override
 132     public SignatureVisitor visitInterfaceBound() {
 133         separator = seenInterfaceBound ? ", " : " extends ";
 134         seenInterfaceBound = true;
 135         startType();
 136         return this;
 137     }
 138 
 139     @Override
 140     public SignatureVisitor visitSuperclass() {
 141         endFormals();
 142         separator = " extends ";
 143         startType();
 144         return this;
 145     }
 146 
 147     @Override
 148     public SignatureVisitor visitInterface() {
 149         separator = seenInterface ? ", " : isInterface ? " extends "
 150                 : " implements ";
 151         seenInterface = true;
 152         startType();
 153         return this;
 154     }
 155 
 156     @Override
 157     public SignatureVisitor visitParameterType() {
 158         endFormals();
 159         if (seenParameter) {
 160             declaration.append(", ");
 161         } else {
 162             seenParameter = true;
 163             declaration.append('(');
 164         }
 165         startType();
 166         return this;
 167     }
 168 
 169     @Override
 170     public SignatureVisitor visitReturnType() {
 171         endFormals();
 172         if (seenParameter) {
 173             seenParameter = false;
 174         } else {
 175             declaration.append('(');
 176         }
 177         declaration.append(')');
 178         returnType = new StringBuilder();
 179         return new TraceSignatureVisitor(returnType);
 180     }
 181 
 182     @Override
 183     public SignatureVisitor visitExceptionType() {
 184         if (exceptions == null) {
 185             exceptions = new StringBuilder();
 186         } else {
 187             exceptions.append(", ");
 188         }
 189         // startType();
 190         return new TraceSignatureVisitor(exceptions);
 191     }
 192 
 193     @Override
 194     public void visitBaseType(final char descriptor) {
 195         switch (descriptor) {
 196         case 'V':
 197             declaration.append("void");
 198             break;
 199         case 'B':
 200             declaration.append("byte");
 201             break;
 202         case 'J':
 203             declaration.append("long");
 204             break;
 205         case 'Z':
 206             declaration.append("boolean");
 207             break;
 208         case 'I':
 209             declaration.append("int");
 210             break;
 211         case 'S':
 212             declaration.append("short");
 213             break;
 214         case 'C':
 215             declaration.append("char");
 216             break;
 217         case 'F':
 218             declaration.append("float");
 219             break;
 220         // case 'D':
 221         default:
 222             declaration.append("double");
 223             break;
 224         }
 225         endType();
 226     }
 227 
 228     @Override
 229     public void visitTypeVariable(final String name) {
 230         declaration.append(name);
 231         endType();
 232     }
 233 
 234     @Override
 235     public SignatureVisitor visitArrayType() {
 236         startType();
 237         arrayStack |= 1;
 238         return this;
 239     }
 240 
 241     @Override
 242     public void visitClassType(final String name) {
 243         if ("java/lang/Object".equals(name)) {
 244             // Map<java.lang.Object,java.util.List>
 245             // or
 246             // abstract public V get(Object key); (seen in Dictionary.class)
 247             // should have Object
 248             // but java.lang.String extends java.lang.Object is unnecessary
 249             boolean needObjectClass = argumentStack % 2 != 0 || seenParameter;
 250             if (needObjectClass) {
 251                 declaration.append(separator).append(name.replace('/', '.'));
 252             }
 253         } else {
 254             declaration.append(separator).append(name.replace('/', '.'));
 255         }
 256         separator = "";
 257         argumentStack *= 2;
 258     }
 259 
 260     @Override
 261     public void visitInnerClassType(final String name) {
 262         if (argumentStack % 2 != 0) {
 263             declaration.append('>');
 264         }
 265         argumentStack /= 2;
 266         declaration.append('.');
 267         declaration.append(separator).append(name.replace('/', '.'));
 268         separator = "";
 269         argumentStack *= 2;
 270     }
 271 
 272     @Override
 273     public void visitTypeArgument() {
 274         if (argumentStack % 2 == 0) {
 275             ++argumentStack;
 276             declaration.append('<');
 277         } else {
 278             declaration.append(", ");
 279         }
 280         declaration.append('?');
 281     }
 282 
 283     @Override
 284     public SignatureVisitor visitTypeArgument(final char tag) {
 285         if (argumentStack % 2 == 0) {
 286             ++argumentStack;
 287             declaration.append('<');
 288         } else {
 289             declaration.append(", ");
 290         }
 291 
 292         if (tag == EXTENDS) {
 293             declaration.append("? extends ");
 294         } else if (tag == SUPER) {
 295             declaration.append("? super ");
 296         }
 297 
 298         startType();
 299         return this;
 300     }
 301 
 302     @Override
 303     public void visitEnd() {
 304         if (argumentStack % 2 != 0) {
 305             declaration.append('>');
 306         }
 307         argumentStack /= 2;
 308         endType();
 309     }
 310 
 311     public String getDeclaration() {
 312         return declaration.toString();
 313     }
 314 
 315     public String getReturnType() {
 316         return returnType == null ? null : returnType.toString();
 317     }
 318 
 319     public String getExceptions() {
 320         return exceptions == null ? null : exceptions.toString();
 321     }
 322 
 323     // -----------------------------------------------
 324 
 325     private void endFormals() {
 326         if (seenFormalParameter) {
 327             declaration.append('>');
 328             seenFormalParameter = false;
 329         }
 330     }
 331 
 332     private void startType() {
 333         arrayStack *= 2;
 334     }
 335 
 336     private void endType() {
 337         if (arrayStack % 2 == 0) {
 338             arrayStack /= 2;
 339         } else {
 340             while (arrayStack % 2 != 0) {
 341                 arrayStack /= 2;
 342                 declaration.append("[]");
 343             }
 344         }
 345     }
 346 }