// Copyright (c) 2003-present, Jodd Team (http://jodd.org) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. package jodd.proxetta.asm; import jodd.asm5.signature.SignatureVisitor; import jodd.asm5.Opcodes; import jodd.util.collection.IntArrayList; import jodd.mutable.MutableInteger; import jodd.proxetta.MethodInfo; import jodd.proxetta.ProxettaException; import jodd.proxetta.ClassInfo; import jodd.proxetta.AnnotationInfo; import jodd.asm.TraceSignatureVisitor; import java.util.List; import java.util.ArrayList; /** * Resolves method signature and holds all information. Uses {@link jodd.asm.TraceSignatureVisitor} from ASM library. * <pre> * MethodSignature = ( visitFormalTypeParameter visitClassBound? visitInterfaceBound* )* ( visitParameterType* visitReturnType visitExceptionType* ) * </pre> */ public class MethodSignatureVisitor extends TraceSignatureVisitor implements MethodInfo { protected int access; protected String methodName; protected String signature; protected int argumentsCount; protected int argumentsWords; protected MutableInteger returnOpcodeType; protected StringBuilder returnTypeName; protected String classname; protected String description; protected String rawSignature; protected AnnotationInfo[] annotations; protected boolean visitingArgument; protected StringBuilder argumentsOpcodeType; protected IntArrayList argumentsOffset; protected List<String> argumentsTypeNames; protected AnnotationInfo[][] argumentsAnnotation; protected String declaredClassName; protected ClassInfo targetClassInfo; protected boolean isStatic; // ---------------------------------------------------------------- ctors /* public MethodSignatureVisitor(String description) { this(); this.description = description; } */ public MethodSignatureVisitor(String methodName, final int access, String classname, String description, String signature, ClassInfo targetClassInfo) { this(); this.isInterface = (access & Opcodes.ACC_INTERFACE) != 0; this.isStatic = (access & Opcodes.ACC_STATIC) != 0; this.methodName = methodName; this.access = access; this.classname = classname; this.description = description; this.targetClassInfo = targetClassInfo; this.rawSignature = signature; } private MethodSignatureVisitor() { super(new StringBuilder()); } private MethodSignatureVisitor(final StringBuilder declaration) { super(declaration); } private MethodSignatureVisitor(final StringBuilder buf, MutableInteger returnOpcodeType, StringBuilder returnTypeName) { this(buf); this.returnOpcodeType = returnOpcodeType; this.returnTypeName = returnTypeName; } // ---------------------------------------------------------------- code @Override public SignatureVisitor visitParameterType() { super.visitParameterType(); visitingArgument = true; if (argumentsOpcodeType == null) { argumentsOpcodeType = new StringBuilder(); argumentsOffset = new IntArrayList(); argumentsTypeNames = new ArrayList<>(); argumentsOpcodeType.append('L'); argumentsOffset.add(0); argumentsTypeNames.add(null); } return this; } @Override public SignatureVisitor visitReturnType() { super.visitReturnType(); returnOpcodeType = new MutableInteger(); returnTypeName = new StringBuilder(); return new MethodSignatureVisitor(returnType, returnOpcodeType, returnTypeName); } @Override public SignatureVisitor visitExceptionType() { super.visitExceptionType(); return new MethodSignatureVisitor(exceptions); } @Override public void visitBaseType(final char descriptor) { String name = null; char type = descriptor; if (isArray()) { type = '['; name = getArrayDepthString() + descriptor; } super.visitBaseType(descriptor); maybeUseType(type, name); } /** * Visits a signature corresponding to a type variable. */ @Override public void visitTypeVariable(final String name) { super.visitTypeVariable(name); maybeUseType('L', name); // toask what is this? } /** * Visits a signature corresponding to an array type. */ @Override public SignatureVisitor visitArrayType() { super.visitArrayType(); return this; } /** * Starts the visit of a signature corresponding to a class or interface type. */ @Override public void visitClassType(final String name) { super.visitClassType(name); maybeUseType('L', 'L' + name + ';'); } // ---------------------------------------------------------------- method signature /** * Returns signature. */ public String getSignature() { if (signature == null) { signature = createSignature(); } return signature; } private String createSignature() { StringBuilder methodDeclaration = new StringBuilder(30); methodDeclaration.append(getReturnType()).append(' ').append(methodName).append(getDeclaration()); String genericExceptions = getExceptions(); if (genericExceptions != null) { methodDeclaration.append(" throws ").append(genericExceptions); } return methodDeclaration.toString(); } public String getRawSignature() { return rawSignature; } public String getMethodName() { return methodName; } public int getArgumentsCount() { return argumentsCount; } /** * @param index 1-base index */ public char getArgumentOpcodeType(int index) { return argumentsOpcodeType.charAt(index); } public String getArgumentTypeName(int i) { return argumentsTypeNames.get(i); } public int getArgumentOffset(int index) { return argumentsOffset.get(index); } public AnnotationInfo[] getArgumentAnnotations(int index) { return argumentsAnnotation[index]; } public int getAllArgumentsSize() { return argumentsWords; } public char getReturnOpcodeType() { return (char) returnOpcodeType.value; } public String getReturnTypeName() { return returnTypeName.toString(); } public int getAccessFlags() { return access; } public String getClassname() { return classname; } public String getDescription() { return description; } public AnnotationInfo[] getAnnotations() { return annotations; } public String getDeclaredClassName() { if (declaredClassName == null) { return classname; } return declaredClassName; } public void setDeclaredClassName(String declaredClassName) { this.declaredClassName = declaredClassName; } public boolean isTopLevelMethod() { return declaredClassName == null; } public ClassInfo getClassInfo() { return targetClassInfo; } // ---------------------------------------------------------------- utilities /** * Returns <code>true</code> if we are currently visiting an array. */ private boolean isArray() { return arrayStack != 0; } /** * Add '[' for current array depth. */ private String getArrayDepthString() { int aStack = arrayStack; // copy value StringBuilder ads = new StringBuilder(); while (aStack % 2 != 0) { aStack /= 2; ads.append('['); } return ads.toString(); } /** * Saves argument type if parameter is currently visiting, otherwise * saves return type. When saving arguments data, stores also current argument offset. */ private void maybeUseType(char type, String typeName) { if (visitingArgument) { if (isArray()) { type = '['; typeName = getArrayDepthString() + typeName; } if (type == 'V') { throw new ProxettaException("Method argument can't be void"); } argumentsCount++; argumentsOpcodeType.append(type); argumentsOffset.add(argumentsWords + 1); argumentsTypeNames.add(typeName); if ((type == 'D') || (type == 'J')) { argumentsWords += 2; } else { argumentsWords++; } visitingArgument = false; } else if (returnOpcodeType != null) { if (isArray()) { type = '['; typeName = getArrayDepthString() + typeName; } returnOpcodeType.value = type; if (returnTypeName.length() == 0) { // only set return type once, first time. // otherwise, if method signature has generic information, the returnTypeName // will be equals to last defined type in the signature, i.e. from the generics. //returnTypeName.setLength(0); if (typeName != null) { returnTypeName.append(typeName); } } } } // ---------------------------------------------------------------- toString /* @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MethodSignatureVisitor that = (MethodSignatureVisitor) o; return getSignature().equals(that.getSignature()); } @Override public int hashCode() { return getSignature().hashCode(); } */ @Override public String toString() { return getDeclaredClassName() + '#' + getMethodName() + getDescription(); } }