/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mkstubs.sourcer;
import com.android.mkstubs.Main;
import org.objectweb.asm.Type;
import org.objectweb.asm.signature.SignatureReader;
import org.objectweb.asm.signature.SignatureVisitor;
import org.objectweb.asm.signature.SignatureWriter;
import java.util.ArrayList;
/**
* A signature visitor that can be used to generate Java source corresponding to
* various types of signatures.
*
* Terminology: a "signature" is a type descriptor for generics. There are different types
* of signatures depending on the context where they are used, e.g. method declarations,
* method parameters, class declarations, etc..
*
* Note: most of the implementation is a duplicate of ASM's SignatureWriter with some
* slight variations.
*
* Note: When processing a method's signature, the signature order is the reverse of the source
* order, e.g. the signature is written as "(parameters)return-type" where we want to generate
* "return-type method-name (parameters)". To handle this case, the return-type and parameters
* are not output directly but are instead accumulated in internal variables that you can
* get later using {@link #getReturnType()}, {@link #getParameters()}, {@link #getSuperClass()}
* and {@link #formalsToString()}.
*/
class SignatureSourcer extends SignatureVisitor {
/**
* Buffer used to construct the signature.
*/
private final StringBuilder mBuf = new StringBuilder();
/**
* Buffer used to construct the formals signature.
*/
private final StringBuilder mFormalsBuf = new StringBuilder();
/**
* Indicates if the signature is currently processing formal type parameters.
*/
private boolean mWritingFormals;
/**
* Stack used to keep track of class types that have arguments. Each element
* of this stack is a boolean encoded in one bit. The top of the stack is
* the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
* /2.
*/
private int mArgumentStack;
/**
* {@link SignatureSourcer} generated when parsing the return type of this
* signature. Initially null.
*/
private SignatureSourcer mReturnType;
/**
* {@link SignatureSourcer} generated when parsing the super class of this
* signature. Initially null.
*/
private SignatureSourcer mSuperClass;
/**
* {@link SignatureSourcer}s for each parameters generated when parsing the method parameters
* of this signature. Initially empty but not null.
*/
private ArrayList mParameters = new ArrayList<>();
/**
* Constructs a new {@link SignatureWriter} object.
*/
public SignatureSourcer() {
super(Main.ASM_VERSION);
}
private StringBuilder getBuf() {
if (mWritingFormals) {
return mFormalsBuf;
} else {
return mBuf;
}
}
/**
* Contains the whole signature type when called by
* {@link SignatureReader#acceptType(SignatureVisitor)} or just the formals if
* called by {@link SignatureReader#accept(SignatureVisitor)}.
*/
@Override
public String toString() {
return mBuf.toString();
}
/**
* Will be non-null if a return type was processed
* by {@link SignatureReader#accept(SignatureVisitor)}
*/
public SignatureSourcer getReturnType() {
return mReturnType;
}
/**
* Will be non-empty if a parameters were processed
* by {@link SignatureReader#accept(SignatureVisitor)}
*/
public ArrayList getParameters() {
return mParameters;
}
/**
* True if the signature contains formal type parameters, which are available
* via {@link #formalsToString()} after calling {@link SignatureReader#accept(SignatureVisitor)}
*/
public boolean hasFormalsContent() {
return mFormalsBuf.length() > 0;
}
public String formalsToString() {
return mFormalsBuf.toString();
}
/**
* Will be non-null if a super class was processed
* by {@link SignatureReader#accept(SignatureVisitor)}
*/
public SignatureSourcer getSuperClass() {
return mSuperClass;
}
// ------------------------------------------------------------------------
// Implementation of the SignatureVisitor interface
// ------------------------------------------------------------------------
@Override
public void visitFormalTypeParameter(final String name) {
if (!mWritingFormals) {
mWritingFormals = true;
getBuf().append('<');
} else {
getBuf().append(", ");
}
getBuf().append(name);
getBuf().append(" extends ");
}
@Override
public SignatureVisitor visitClassBound() {
// we don't differentiate between visiting a sub class or interface type
return this;
}
@Override
public SignatureVisitor visitInterfaceBound() {
// we don't differentiate between visiting a sub class or interface type
return this;
}
@Override
public SignatureVisitor visitSuperclass() {
endFormals();
SignatureSourcer sourcer = new SignatureSourcer();
assert mSuperClass == null;
mSuperClass = sourcer;
return sourcer;
}
@Override
public SignatureVisitor visitInterface() {
return this;
}
@Override
public SignatureVisitor visitParameterType() {
endFormals();
SignatureSourcer sourcer = new SignatureSourcer();
mParameters.add(sourcer);
return sourcer;
}
@Override
public SignatureVisitor visitReturnType() {
endFormals();
SignatureSourcer sourcer = new SignatureSourcer();
assert mReturnType == null;
mReturnType = sourcer;
return sourcer;
}
@Override
public SignatureVisitor visitExceptionType() {
getBuf().append('^');
return this;
}
@Override
public void visitBaseType(final char descriptor) {
getBuf().append(Type.getType(Character.toString(descriptor)).getClassName());
}
@Override
public void visitTypeVariable(final String name) {
getBuf().append(name.replace('/', '.'));
}
@Override
public SignatureVisitor visitArrayType() {
getBuf().append('[');
return this;
}
@Override
public void visitClassType(final String name) {
getBuf().append(name.replace('/', '.'));
mArgumentStack *= 2;
}
@Override
public void visitInnerClassType(final String name) {
endArguments();
getBuf().append('.');
getBuf().append(name.replace('/', '.'));
mArgumentStack *= 2;
}
@Override
public void visitTypeArgument() {
if (mArgumentStack % 2 == 0) {
++mArgumentStack;
getBuf().append('<');
} else {
getBuf().append(", ");
}
getBuf().append('*');
}
@Override
public SignatureVisitor visitTypeArgument(final char wildcard) {
if (mArgumentStack % 2 == 0) {
++mArgumentStack;
getBuf().append('<');
} else {
getBuf().append(", ");
}
if (wildcard != '=') {
if (wildcard == '+') {
getBuf().append("? extends ");
} else if (wildcard == '-') {
getBuf().append("? super ");
} else {
// can this happen?
getBuf().append(wildcard);
}
}
return this;
}
@Override
public void visitEnd() {
endArguments();
}
// ------------------------------------------------------------------------
// Utility methods
// ------------------------------------------------------------------------
/**
* Ends the formal type parameters section of the signature.
*/
private void endFormals() {
if (mWritingFormals) {
getBuf().append('>');
mWritingFormals = false;
}
}
/**
* Ends the type arguments of a class or inner class type.
*/
private void endArguments() {
if (mArgumentStack % 2 != 0) {
getBuf().append('>');
}
mArgumentStack /= 2;
}
}