blob: 7fc1b4d2f306e699a98387304746904fb7a53259 [file] [log] [blame]
/*
* Copyright 2016 Federico Tomassetti
*
* 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.github.javaparser.symbolsolver.javassistmodel;
import com.github.javaparser.resolution.MethodUsage;
import com.github.javaparser.resolution.UnsolvedSymbolException;
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedTypeParametrizable;
import com.github.javaparser.resolution.types.*;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
import javassist.*;
import javassist.bytecode.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author Federico Tomassetti
*/
class JavassistUtils {
static Optional<MethodUsage> getMethodUsage(CtClass ctClass, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver,
List<ResolvedTypeParameterDeclaration> typeParameters, List<ResolvedType> typeParameterValues) {
List<MethodUsage> methods = new ArrayList<>();
for (CtMethod method : ctClass.getDeclaredMethods()) {
if (method.getName().equals(name)
&& ((method.getMethodInfo().getAccessFlags() & AccessFlag.BRIDGE) == 0)
&& ((method.getMethodInfo().getAccessFlags() & AccessFlag.SYNTHETIC) == 0)) {
MethodUsage methodUsage = new MethodUsage(new JavassistMethodDeclaration(method, typeSolver));
for (int i = 0; i < typeParameters.size() && i < typeParameterValues.size(); i++) {
ResolvedTypeParameterDeclaration tpToReplace = typeParameters.get(i);
ResolvedType newValue = typeParameterValues.get(i);
methodUsage = methodUsage.replaceTypeParameter(tpToReplace, newValue);
}
methods.add(methodUsage);
// no need to search for overloaded/inherited methods if the method has no parameters
if (argumentsTypes.isEmpty() && methodUsage.getNoParams() == 0) {
return Optional.of(methodUsage);
}
}
}
try {
CtClass superClass = ctClass.getSuperclass();
if (superClass != null) {
Optional<MethodUsage> ref = JavassistUtils.getMethodUsage(superClass, name, argumentsTypes, typeSolver, typeParameters, typeParameterValues);
if (ref.isPresent()) {
methods.add(ref.get());
}
}
} catch (NotFoundException e) {
throw new RuntimeException(e);
}
try {
for (CtClass interfaze : ctClass.getInterfaces()) {
Optional<MethodUsage> ref = JavassistUtils.getMethodUsage(interfaze, name, argumentsTypes, typeSolver, typeParameters, typeParameterValues);
if (ref.isPresent()) {
methods.add(ref.get());
}
}
} catch (NotFoundException e) {
throw new RuntimeException(e);
}
return MethodResolutionLogic.findMostApplicableUsage(methods, name, argumentsTypes, typeSolver);
}
static ResolvedType signatureTypeToType(SignatureAttribute.Type signatureType, TypeSolver typeSolver, ResolvedTypeParametrizable typeParametrizable) {
if (signatureType instanceof SignatureAttribute.ClassType) {
SignatureAttribute.ClassType classType = (SignatureAttribute.ClassType) signatureType;
List<ResolvedType> typeArguments = classType.getTypeArguments() == null ? Collections.emptyList() : Arrays.stream(classType.getTypeArguments()).map(ta -> typeArgumentToType(ta, typeSolver, typeParametrizable)).collect(Collectors.toList());
ResolvedReferenceTypeDeclaration typeDeclaration = typeSolver.solveType(
removeTypeArguments(internalNameToCanonicalName(getTypeName(classType))));
return new ReferenceTypeImpl(typeDeclaration, typeArguments, typeSolver);
} else if (signatureType instanceof SignatureAttribute.TypeVariable) {
SignatureAttribute.TypeVariable typeVariableSignature = (SignatureAttribute.TypeVariable) signatureType;
Optional<ResolvedTypeParameterDeclaration> typeParameterDeclarationOpt = typeParametrizable.findTypeParameter(typeVariableSignature.getName());
if (!typeParameterDeclarationOpt.isPresent()) {
throw new UnsolvedSymbolException("Unable to solve TypeVariable " + typeVariableSignature);
}
ResolvedTypeParameterDeclaration typeParameterDeclaration = typeParameterDeclarationOpt.get();
return new ResolvedTypeVariable(typeParameterDeclaration);
} else if (signatureType instanceof SignatureAttribute.ArrayType) {
SignatureAttribute.ArrayType arrayType = (SignatureAttribute.ArrayType) signatureType;
return new ResolvedArrayType(signatureTypeToType(arrayType.getComponentType(), typeSolver, typeParametrizable));
} else if (signatureType instanceof SignatureAttribute.BaseType) {
SignatureAttribute.BaseType baseType = (SignatureAttribute.BaseType) signatureType;
if (baseType.toString().equals("void")) {
return ResolvedVoidType.INSTANCE;
} else {
return ResolvedPrimitiveType.byName(baseType.toString());
}
} else {
throw new RuntimeException(signatureType.getClass().getCanonicalName());
}
}
private static String getTypeName(SignatureAttribute.ClassType classType) {
SignatureAttribute.ClassType declaringClass = classType.getDeclaringClass();
return declaringClass == null ? classType.getName() : getTypeName(declaringClass) + "." + classType.getName();
}
private static String removeTypeArguments(String typeName) {
if (typeName.contains("<")) {
return typeName.substring(0, typeName.indexOf('<'));
} else {
return typeName;
}
}
static String internalNameToCanonicalName(String typeName) {
return typeName.replaceAll("\\$", ".");
}
private static ResolvedType objectTypeArgumentToType(SignatureAttribute.ObjectType typeArgument, TypeSolver typeSolver, ResolvedTypeParametrizable typeParametrizable) {
if (typeArgument instanceof SignatureAttribute.ClassType) {
return signatureTypeToType(typeArgument, typeSolver, typeParametrizable);
} else if (typeArgument instanceof SignatureAttribute.ArrayType) {
return signatureTypeToType(((SignatureAttribute.ArrayType) typeArgument).getComponentType(), typeSolver, typeParametrizable);
} else {
String typeName = typeArgument.jvmTypeName();
return getGenericParameterByName(typeName, typeParametrizable, typeSolver);
}
}
private static ResolvedType getGenericParameterByName(String typeName, ResolvedTypeParametrizable typeParametrizable, TypeSolver typeSolver) {
Optional<ResolvedType> type = typeParametrizable.findTypeParameter(typeName).map(ResolvedTypeVariable::new);
return type.orElseGet(() -> new ReferenceTypeImpl(
typeSolver.solveType(removeTypeArguments(internalNameToCanonicalName(typeName))),
typeSolver));
}
private static ResolvedType typeArgumentToType(SignatureAttribute.TypeArgument typeArgument, TypeSolver typeSolver, ResolvedTypeParametrizable typeParametrizable) {
if (typeArgument.isWildcard()) {
if (typeArgument.getType() == null) {
return ResolvedWildcard.UNBOUNDED;
} else if (typeArgument.getKind() == '+') {
return ResolvedWildcard.extendsBound(objectTypeArgumentToType(typeArgument.getType(), typeSolver, typeParametrizable));
} else if (typeArgument.getKind() == '-') {
return ResolvedWildcard.superBound(objectTypeArgumentToType(typeArgument.getType(), typeSolver, typeParametrizable));
} else {
throw new UnsupportedOperationException();
}
} else {
return objectTypeArgumentToType(typeArgument.getType(), typeSolver, typeParametrizable);
}
}
/**
* Returns the {@code paramNumber}th parameter of a method or constructor, if it is available.
* <p>
* The name is not available, if
* <ul>
* <li>the method is abstract, i.e. explicitly declared as abstract or it is a non-default interface method</li>
* <li>methods and constructors from jar files, which have been compiled without debug symbols</li>
* </ul>
* <p>
* The parameters are counted from 0, skipping the implicit {@code this} parameter of non-static methods.
*
* @param method the method to look into
* @param paramNumber the number of the parameter to look for
* @return the found parameter name or empty, if the name is not available
*/
static Optional<String> extractParameterName(CtBehavior method, int paramNumber) {
MethodInfo methodInfo = method.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
if (codeAttribute != null) {
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute
.tag);
if (attr != null) {
int pos = Modifier.isStatic(method.getModifiers()) ? 0 : 1;
return Optional.ofNullable(attr.variableName(paramNumber + pos));
}
}
return Optional.empty();
}
}