/*******************************************************************************
* Copyright (c) 2010 Michal Antkiewicz.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Michal Antkiewicz - initial API and implementation
******************************************************************************/
package ca.uwaterloo.gsd.fsml.javaMappingInterpreter;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.search.FieldReferenceMatch;
import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.ASTUtils;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.IAnalysisManagers;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.IHierarchicalCallGraphManager;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.IJavaASTManager;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.JavaModelUtils;
import ca.uwaterloo.gsd.fsml.stats.Stats;
/**
* @author Michal Antkiewicz <mantkiew@gsd.uwaterloo.ca>
*/
public class CodeQueries {
@SuppressWarnings("restriction")
public static Expression retrieveRHSFromMatch(IJavaASTManager javaAstManager, FieldReferenceMatch match, IProgressMonitor progressMonitor) {
// TODO: add support for markers!
// stats
final String logExpression = "retrieveRHSFromMatch: RHS expression retrieved";
final String logFailure = "retrieveRHSFromMatch: Unable to retrieve RHS";
Object object = ((IJavaElement) match.getElement()).getPrimaryElement();
IType declaringType = null;
if (object instanceof IMethod) {
declaringType = ((IMethod) object).getDeclaringType();
while (declaringType.getDeclaringType() != null)
declaringType = declaringType.getDeclaringType();
}
else {
Stats.INSTANCE.logError(logFailure);
return null;
}
CompilationUnit cu = javaAstManager.getCompilationUnit(declaringType);
ASTNode astNode = ASTNodeSearchUtil.findNode(match, cu);
if (astNode == null)
return null;
astNode = astNode.getParent();
if (astNode instanceof Assignment) {
Assignment assignment = (Assignment) astNode;
Stats.INSTANCE.logMessage(logExpression);
return assignment.getRightHandSide();
}
return null;
}
// methods factored out as individual mappings
/**
* Copied from Member.class
*/
public static boolean areSimilarMethods(
String name1, String[] params1,
String name2, String[] params2,
String[] simpleNames1) {
if (name1.equals(name2)) {
int params1Length = params1.length;
if (params1Length == params2.length) {
for (int i = 0; i < params1Length; i++) {
String simpleName1 =
simpleNames1 == null ?
Signature.getSimpleName(Signature.toString(Signature.getTypeErasure(params1[i]))) :
simpleNames1[i];
String simpleName2 = Signature.getSimpleName(Signature.toString(Signature.getTypeErasure(params2[i])));
if (!simpleName1.equals(simpleName2)) {
return false;
}
}
return true;
}
}
return false;
}
public static IType findITypeContainingASTNode(IJavaProject project, ASTNode astNode) {
// the node may not be inside the context class. Find IType of the node
TypeDeclaration typeDeclaration = (TypeDeclaration) ASTUtils.getAncestorOfType(astNode, ASTNode.TYPE_DECLARATION);
String fqName = null;
if (typeDeclaration != null) {
ITypeBinding binding = typeDeclaration.getName().resolveTypeBinding();
if (binding != null)
fqName = binding.getQualifiedName();
else {
String className = typeDeclaration.getName().getIdentifier();
PackageDeclaration packageDeclaration = ((CompilationUnit) typeDeclaration.getRoot()).getPackage();
String classQualifier = null;
if (packageDeclaration != null)
classQualifier = packageDeclaration.getName().getFullyQualifiedName() + ".";
fqName = classQualifier + className;
}
}
IType iTypeOfNode = null;
try {
iTypeOfNode = project.findType(fqName);
} catch (JavaModelException e) {
e.printStackTrace();
}
return iTypeOfNode;
}
/**
* @param fqTypeName
* @param signature
* @return true iff the signature represents fqTypeName
*/
public static boolean compareTypeSignatures(String fqTypeName, String signature) {
String fqResolvedName = Signature.createTypeSignature(fqTypeName, true);
String fqUnresolvedName = Signature.createTypeSignature(fqTypeName, false);
String sResolvedName = Signature.createTypeSignature(Signature.getSimpleName(fqTypeName), true);
String sUnresolvedName = Signature.createTypeSignature(Signature.getSimpleName(fqTypeName), false);
boolean fqResolved = fqResolvedName.equals(signature);
boolean fqUnresolved = fqUnresolvedName.equals(signature);
boolean sResolved = sResolvedName.equals(signature);
boolean sUnresolved = sUnresolvedName.equals(signature);
return fqResolved || fqUnresolved || sResolved || sUnresolved;
}
/**
* @param expression
* @param analysisManagers
* @return a list of expressions that are static values of the given expression. Null indicates unexpected problem during analysis.
* This method is an entry point for dataflow analysis.
*/
public static void getStaticExpressionsForExpression(Expression expression, IType contextIType, ArrayList<Expression> result, IAnalysisManagers analysisManagers, IProgressMonitor progressMonitor) {
if (ASTUtils.hasStaticValue(expression)) {
// the expression is a) a literal, 2) a static final (both array and non-array) variable, 3) enum literal
if (expression.getNodeType() == ASTNode.SIMPLE_NAME) {
IBinding iBinding = ((SimpleName) expression).resolveBinding();
CompilationUnit compilationUnit = (CompilationUnit) expression.getRoot();
// that is a static final variable, return the initializer instead of the identifier
result.add(ASTUtils.getInitializer(compilationUnit, (IVariableBinding) iBinding));
}
else if (expression.getNodeType() == ASTNode.QUALIFIED_NAME)
getStaticExpressionsForVariable((QualifiedName) expression, contextIType, result, analysisManagers, progressMonitor);
else
result.add(expression);
return;
}
switch (expression.getNodeType()) {
case ASTNode.SIMPLE_NAME:
IBinding resolvedBinding = ((SimpleName) expression).resolveBinding();
if (resolvedBinding instanceof IVariableBinding)
// a variable
CodeQueries.getStaticExpressionsForVariable((SimpleName) expression, contextIType, result, analysisManagers, progressMonitor);
break;
case ASTNode.QUALIFIED_NAME:
resolvedBinding = ((QualifiedName) expression).resolveBinding();
if (resolvedBinding instanceof IVariableBinding)
// a variable
CodeQueries.getStaticExpressionsForVariable((QualifiedName) expression, contextIType, result, analysisManagers, progressMonitor);
break;
case ASTNode.ARRAY_ACCESS:
// navigate to the simpleName of the array
Expression aux = expression;
while (aux instanceof ArrayAccess) {
ArrayAccess arrayAccess = (ArrayAccess) aux;
aux = arrayAccess.getArray();
}
if (!(aux instanceof SimpleName))
// should never happen!
break;
SimpleName arraySimpleName = (SimpleName) aux;
ArrayList<Expression> valueExpressions = new ArrayList<Expression>();
ArrayList<Expression> cellValueExpressions = new ArrayList<Expression>();
CodeQueries.getStaticExpressionsForVariable(arraySimpleName, contextIType, valueExpressions, analysisManagers, progressMonitor);
for (Expression valueExpression : valueExpressions) {
ArrayInitializer arrayInitializer = null;
switch (valueExpression.getNodeType()) {
case ASTNode.ARRAY_INITIALIZER:
arrayInitializer = (ArrayInitializer) valueExpression;
break;
case ASTNode.ARRAY_CREATION:
arrayInitializer = ((ArrayCreation) valueExpression).getInitializer();
break;
}
if (arrayInitializer != null) {
// iterate over the array depending on which of the indexes
// are not constants
ArrayAccess arrayAccess = (ArrayAccess) aux.getParent();
cellValueExpressions = CodeQueries.getStaticExpressionsForArrayAccess(arrayAccess, arrayInitializer);
if (cellValueExpressions != null)
result.addAll(cellValueExpressions);
}
}
break;
case ASTNode.METHOD_INVOCATION:
case ASTNode.SUPER_METHOD_INVOCATION:
case ASTNode.CLASS_INSTANCE_CREATION:
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION:
getStaticExpressionsForMethodInvocation(expression, contextIType, result, analysisManagers, progressMonitor);
break;
case ASTNode.PARENTHESIZED_EXPRESSION:
ParenthesizedExpression expression2 = (ParenthesizedExpression) expression;
getStaticExpressionsForExpression(expression2.getExpression(), contextIType, result, analysisManagers, progressMonitor);
break;
case ASTNode.CONDITIONAL_EXPRESSION:
ConditionalExpression conditionalExpression = (ConditionalExpression) expression;
getStaticExpressionsForExpression(conditionalExpression.getThenExpression(), contextIType, result, analysisManagers, progressMonitor);
getStaticExpressionsForExpression(conditionalExpression.getElseExpression(), contextIType, result, analysisManagers, progressMonitor);
break;
case ASTNode.CAST_EXPRESSION:
CastExpression castExpression = (CastExpression) expression;
getStaticExpressionsForExpression(castExpression.getExpression(), contextIType, result, analysisManagers, progressMonitor);
break;
}
}
public static void getStaticExpressionsForVariable(QualifiedName variable, IType contextIType, ArrayList<Expression> result, IAnalysisManagers analysisManagers, IProgressMonitor progressMonitor) {
IVariableBinding iVariableBinding;
if (variable.resolveBinding() instanceof IVariableBinding)
iVariableBinding = (IVariableBinding) variable.resolveBinding();
else
throw new IllegalArgumentException("Parameter 'variable' must be an identfier of a variable");
IVariableBinding iVariableDeclarationBinding = iVariableBinding.getVariableDeclaration();
IJavaElement iJavaElement = iVariableDeclarationBinding.getJavaElement();
IJavaASTManager astManager = analysisManagers.getJavaASTManager();
CompilationUnit variableCompilationUnit = astManager.getCompilationUnit(iJavaElement);
VariableDeclarationFragment variableDeclaration = ASTUtils.getVariableDeclarationFragmentNode(iVariableDeclarationBinding, variableCompilationUnit);
getStaticExpressionsForVariable(variableDeclaration.getName(), contextIType, result, analysisManagers, progressMonitor);
}
public static void getStaticExpressionsForVariable(SimpleName variable, IType contextIType, ArrayList<Expression> result, IAnalysisManagers analysisManagers, IProgressMonitor progressMonitor) {
IVariableBinding iVariableBinding;
if (variable.resolveBinding() instanceof IVariableBinding)
iVariableBinding = (IVariableBinding) variable.resolveBinding();
else
throw new IllegalArgumentException("Parameter 'variable' must be an identfier of a variable");
CompilationUnit variableCompilationUnit = (CompilationUnit) variable.getRoot();
// constant value first or static final array
if (variable.resolveConstantExpressionValue() != null ||
ASTUtils.isStaticFinalArrayField(iVariableBinding)) {
// constant expression value is null for static arrays
Expression initializer = ASTUtils.getInitializer(variableCompilationUnit, iVariableBinding);
if (initializer != null) {
getStaticExpressionsForExpression(initializer, contextIType, result, analysisManagers, progressMonitor);
return;
}
}
// regardless of the number of writes consider if a parameter and include the initalizer if present
// need to see if the variable is a parameter and look for method calls
if (iVariableBinding.isParameter()) {
MethodDeclaration methodDeclaration = (MethodDeclaration) ASTUtils.getAncestorOfType(variable, ASTNode.METHOD_DECLARATION);
SingleVariableDeclaration singleVariableDeclaration = null;
for (Object parameterObject : methodDeclaration.parameters()) {
SingleVariableDeclaration parameterVariableDeclaration = (SingleVariableDeclaration) parameterObject;
if (iVariableBinding.isEqualTo(parameterVariableDeclaration.resolveBinding())) {
singleVariableDeclaration = parameterVariableDeclaration;
break;
}
}
int index = methodDeclaration.parameters().indexOf(singleVariableDeclaration);
IMethodBinding iMethodBinding = methodDeclaration.resolveBinding();
for (ASTNode methodInvocationName : ASTUtils.findMathodInvocations(variableCompilationUnit, methodDeclaration, iMethodBinding)) {
ASTNode methodInvocationNode = methodInvocationName.getParent();
Expression argument = null;
switch (methodInvocationNode.getNodeType()) {
case ASTNode.METHOD_INVOCATION:
argument = (Expression) ((MethodInvocation) methodInvocationNode).arguments().get(index);
break;
case ASTNode.CLASS_INSTANCE_CREATION:
argument = (Expression) ((ClassInstanceCreation) methodInvocationNode).arguments().get(index);
break;
}
getStaticExpressionsForExpression(argument, contextIType, result, analysisManagers, progressMonitor);
}
} else {
Expression initializer = ASTUtils.getInitializer(variableCompilationUnit, iVariableBinding);
if (initializer != null)
getStaticExpressionsForExpression(initializer, contextIType, result, analysisManagers, progressMonitor);
}
// so there is no constant value. Need to find assignments.
List<ASTNode> writeAccesses = ASTUtils.findWriteAccesses(variableCompilationUnit, variable, iVariableBinding);
// Michal: include all write accesses
for (ASTNode writeAccess : writeAccesses) {
if (writeAccess.getParent() instanceof Assignment) {
Expression rhs = ((Assignment) writeAccess.getParent()).getRightHandSide();
getStaticExpressionsForExpression(rhs, contextIType, result, analysisManagers, progressMonitor);
}
}
}
/**
* @param call to a method or a constructor
* @param contextIType
* @param result
* @param analysisManagers
* @param progressMonitor
*/
public static void getStaticExpressionsForMethodInvocation(final Expression call, IType contextIType, ArrayList<Expression> result, final IAnalysisManagers analysisManagers, final IProgressMonitor progressMonitor) {
final IJavaASTManager javaAstManager = analysisManagers.getJavaASTManager();
final IHierarchicalCallGraphManager callGraphManager = analysisManagers.getHierarchicalCallGraphManager();
IMethod targetIMethod = null;
IMethod executingIMethod = null;
try {
switch (call.getNodeType()) {
case ASTNode.METHOD_INVOCATION:
MethodInvocation methodInvocation = (MethodInvocation) call;
targetIMethod = (IMethod) methodInvocation.resolveMethodBinding().getJavaElement();
if (methodInvocation.getExpression() == null || methodInvocation.getExpression().getNodeType() == ASTNode.THIS_EXPRESSION)
// a call in the hierarchy of the context type
executingIMethod = callGraphManager.getExecutingMethod(contextIType, targetIMethod);
else {
List<IMethod> implementations = callGraphManager.findImplementations(JavaModelUtils.getDeclaringType(targetIMethod), targetIMethod);
if (implementations.size() == 1)
executingIMethod = implementations.get(0);
else
executingIMethod = targetIMethod;
contextIType = JavaModelUtils.getDeclaringType(executingIMethod != null ? executingIMethod : targetIMethod);
}
break;
case ASTNode.CLASS_INSTANCE_CREATION:
ClassInstanceCreation classInstanceCreation = (ClassInstanceCreation) call;
targetIMethod = (IMethod) classInstanceCreation.resolveConstructorBinding().getJavaElement();
executingIMethod = callGraphManager.getExecutingMethod(contextIType, targetIMethod);
contextIType = JavaModelUtils.getDeclaringType(executingIMethod != null ? executingIMethod : targetIMethod);
break;
case ASTNode.SUPER_METHOD_INVOCATION:
SuperMethodInvocation superMethodInvocation = (SuperMethodInvocation) call;
targetIMethod = (IMethod) superMethodInvocation.resolveMethodBinding().getJavaElement();
executingIMethod = callGraphManager.getExecutingMethod(contextIType, targetIMethod);
// context remains the same
break;
default:
throw new IllegalArgumentException("Parameter 'call' must be either a MethodInvocation, a SuperMethodInvocation, or a ClassInstanceCreation");
}
callGraphManager.addCompilationUnit(JavaModelUtils.getTypeRoot(contextIType));
} catch (JavaModelException e) {
e.printStackTrace();
}
if (executingIMethod == null || !executingIMethod.exists())
// perhaps outside the hierarchy, proceed with the statically known method
executingIMethod = targetIMethod;
CompilationUnit executingMethodCU = javaAstManager.getCompilationUnit(executingIMethod);
if (executingMethodCU == null) {
System.err.println("cannot get a compilation unit for: " + executingIMethod);
return;
}
MethodDeclaration executingMethodDeclaration = ASTUtils.getMethodDeclarationNode(executingIMethod, executingMethodCU);
getStaticExpressionsForMethodDeclaration(executingMethodDeclaration, contextIType, result, analysisManagers, progressMonitor);
}
/**
* @param methodDeclaration
* @param progressMonitor
* @param javaAstManager
* @param callGraphManager
* @param result2
* @param argumentSummary contains 'true' at index i iff at the moment of method's invocation, the ith argument is known to have the static value.
* @return a list of expressions used in return statements that have static values of both simple and array types. Never null.
*/
public static void getStaticExpressionsForMethodDeclaration(final MethodDeclaration methodDeclaration, final IType contextIType, final ArrayList<Expression> result, final IAnalysisManagers analysisManagers, final IProgressMonitor progressMonitor) {
methodDeclaration.accept(new ASTVisitor() {
public boolean visit(ReturnStatement returnStatement) {
Expression expression = returnStatement.getExpression();
getStaticExpressionsForExpression(expression, contextIType, result, analysisManagers, progressMonitor);
return false;
}
@Override
public boolean visit(ClassInstanceCreation node) {
// prevent the analysis of the contents of the anonymous subclasses
return false;
}
});
}
public static ArrayList<Expression> getStaticExpressionsForArrayAccess(ArrayAccess arrayAccess, ArrayInitializer arrayInitializer) {
ArrayList<Expression> result = new ArrayList<Expression>();
Expression indexExpression = arrayAccess.getIndex();
if (indexExpression.getNodeType() == ASTNode.NUMBER_LITERAL) {
// we pick particular row and proceed
String indexString = ASTUtils.getStringValue(indexExpression);
int index = Integer.parseInt(indexString);
// if the code throws IndexOutOfBoundsException
if (index >= arrayInitializer.expressions().size())
return result;
Expression rowExpression = (Expression) arrayInitializer.expressions().get(index);
if (rowExpression.getNodeType() == ASTNode.ARRAY_INITIALIZER) {
// process recursively
if (arrayAccess.getParent().getNodeType() == ASTNode.ARRAY_ACCESS)
result.addAll(getStaticExpressionsForArrayAccess((ArrayAccess) arrayAccess.getParent(), (ArrayInitializer) rowExpression));
}
else if (rowExpression.resolveConstantExpressionValue() != null)
result.add(rowExpression);
}
else {
// we iterate over all rows
for (Object rowExpressionObject : arrayInitializer.expressions()) {
Expression rowExpression = (Expression) rowExpressionObject;
if (rowExpression.getNodeType() == ASTNode.ARRAY_INITIALIZER) {
// process recursively
if (arrayAccess.getParent().getNodeType() == ASTNode.ARRAY_ACCESS)
result.addAll(getStaticExpressionsForArrayAccess((ArrayAccess) arrayAccess.getParent(), (ArrayInitializer) rowExpression));
}
else if (rowExpression.resolveConstantExpressionValue() != null)
result.add(rowExpression);
}
}
return result;
}
/**
* Convenience function for creating single element results.
* @param expression
* @return
*/
public static ArrayList<Expression> newArrayList(Expression expression) {
ArrayList<Expression> result = new ArrayList<Expression>();
result.add(expression);
return result;
}
/**
* Convenience function for creating an empty result.
* @return
*/
public static ArrayList<Expression> newEmptyArrayList() {
ArrayList<Expression> result = new ArrayList<Expression>();
return result;
}
// most-specific-type inference
public static boolean isMoreSpecific(ITypeBinding specificTypeBinding, ITypeBinding generalTypeBinding) {
return !generalTypeBinding.getKey().equals(specificTypeBinding.getKey()) &&
specificTypeBinding.isAssignmentCompatible(generalTypeBinding);
}
public static ArrayList<Expression> getExpressionWithMostSpecificType(ArrayList<Expression> expressions) {
/*ArrayList<Expression> mostSpecificTypeExpression = new ArrayList<Expression>();
for (Expression expression : expressions) {
if (expression == null || expression instanceof NullLiteral)
continue;
if (mostSpecificTypeExpression == null) {
mostSpecificTypeExpression = expression;
continue;
}
if (isMoreSpecific(expression.resolveTypeBinding(), mostSpecificTypeExpression.resolveTypeBinding()))
mostSpecificTypeExpression = expression;
}
if (mostSpecificTypeExpression == null)
return null;
return mostSpecificTypeExpression;*/
// TODO: implement to remove false positives
return expressions;
}
public static ArrayList<Expression> getExpressionWithMostSpecificTypeForExpression(Expression expression, final IType contextIType, ArrayList<MethodDeclaration> visitedMethodDeclarations, final IAnalysisManagers analysisManagers, final IProgressMonitor progressMonitor) {
ArrayList<Expression> result = new ArrayList<Expression>();
switch (expression.getNodeType()) {
case ASTNode.SIMPLE_NAME:
return getExpressionWithMostSpecificTypeForVariable((SimpleName) expression, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor);
case ASTNode.QUALIFIED_NAME:
return getExpressionWithMostSpecificTypeForVariable((QualifiedName) expression, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor);
case ASTNode.PARENTHESIZED_EXPRESSION:
Expression subExpression = ((ParenthesizedExpression) expression).getExpression();
return getExpressionWithMostSpecificTypeForExpression(subExpression, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor);
case ASTNode.CONDITIONAL_EXPRESSION:
Expression thenExpression= ((ConditionalExpression) expression).getThenExpression();
Expression elseExpression= ((ConditionalExpression) expression).getElseExpression();
// get most-specific-type expressions
ArrayList<Expression> thenExpressions = getExpressionWithMostSpecificTypeForExpression(thenExpression, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor);
ArrayList<Expression> elseExpressions = getExpressionWithMostSpecificTypeForExpression(elseExpression, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor);
ArrayList<Expression> allExpressions = new ArrayList<Expression>();
allExpressions.addAll(thenExpressions);
allExpressions.addAll(elseExpressions);
result.addAll(getExpressionWithMostSpecificType(allExpressions));
break;
case ASTNode.CAST_EXPRESSION:
CastExpression castExpression = (CastExpression) expression;
// get most-specific-type expressions
ArrayList<Expression> expressions = getExpressionWithMostSpecificTypeForExpression(castExpression.getExpression(), contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor);
if (expressions.size() <= 1) {
result.add(castExpression);
break;
}
else
return expressions;
case ASTNode.METHOD_INVOCATION:
case ASTNode.SUPER_METHOD_INVOCATION:
return getExpressionWithMostSpecificTypeForMethodInvocation(expression, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor);
default:
result.add(expression);
}
// could not find anything with more specific type than the expression itself
return result;
}
public static ArrayList<Expression> getExpressionWithMostSpecificTypeForVariable(SimpleName variable, final IType contextIType, ArrayList<MethodDeclaration> visitedMethodDeclarations, final IAnalysisManagers analysisManagers, final IProgressMonitor progressMonitor) {
IVariableBinding iVariableBinding;
if (variable.resolveBinding() ==null){
return new ArrayList<Expression>();
}
if (variable.resolveBinding() instanceof IVariableBinding)
iVariableBinding = (IVariableBinding) variable.resolveBinding();
else
throw new IllegalArgumentException("Parameter 'variable' must be an identfier of a variable");
CompilationUnit variableCompilationUnit = (CompilationUnit) variable.getRoot();
ArrayList<Expression> result = new ArrayList<Expression>();
// find assignments
List<ASTNode> writeAccesses = ASTUtils.findWriteAccesses(variableCompilationUnit, variable, iVariableBinding);
for (ASTNode writeAccess : writeAccesses) {
if (writeAccess.getParent() instanceof Assignment) {
Expression rhs = ((Assignment) writeAccess.getParent()).getRightHandSide();
if (rhs instanceof NullLiteral)
continue;
result.addAll(getExpressionWithMostSpecificTypeForExpression(rhs, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor));
}
}
// need to see if the variable is a parameter and look for method calls
if (iVariableBinding.isParameter()) {
MethodDeclaration methodDeclaration = (MethodDeclaration) ASTUtils.getAncestorOfType(variable, ASTNode.METHOD_DECLARATION);
SingleVariableDeclaration singleVariableDeclaration = null;
for (Object parameterObject : methodDeclaration.parameters()) {
SingleVariableDeclaration parameterVariableDeclaration = (SingleVariableDeclaration) parameterObject;
if (iVariableBinding.isEqualTo(parameterVariableDeclaration.resolveBinding())) {
singleVariableDeclaration = parameterVariableDeclaration;
break;
}
}
int index = methodDeclaration.parameters().indexOf(singleVariableDeclaration);
IMethodBinding iMethodBinding = methodDeclaration.resolveBinding();
for (ASTNode methodInvocationName : ASTUtils.findMathodInvocations(variableCompilationUnit, methodDeclaration, iMethodBinding)) {
ASTNode methodInvocationNode = methodInvocationName.getParent();
Expression argument = null;
switch (methodInvocationNode.getNodeType()) {
case ASTNode.METHOD_INVOCATION:
argument = (Expression) ((MethodInvocation) methodInvocationNode).arguments().get(index);
break;
case ASTNode.CLASS_INSTANCE_CREATION:
argument = (Expression) ((ClassInstanceCreation) methodInvocationNode).arguments().get(index);
break;
}
result.addAll(getExpressionWithMostSpecificTypeForExpression(argument, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor));
}
}
else {
// get initializer
Expression initializer = ASTUtils.getInitializer(variableCompilationUnit, iVariableBinding);
if (initializer != null && initializer.getNodeType() != ASTNode.NULL_LITERAL)
result.addAll(getExpressionWithMostSpecificTypeForExpression(initializer, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor));
}
if (result.isEmpty()) {
result.add(variable);
return result;
}
return getExpressionWithMostSpecificType(result);
}
public static ArrayList<Expression> getExpressionWithMostSpecificTypeForVariable(QualifiedName variable, final IType contextIType, ArrayList<MethodDeclaration> visitedMethodDeclarations, final IAnalysisManagers analysisManagers, final IProgressMonitor progressMonitor) {
IVariableBinding iVariableBinding;
if (variable.resolveBinding() instanceof IVariableBinding)
iVariableBinding = (IVariableBinding) variable.resolveBinding();
else
throw new IllegalArgumentException("Parameter 'variable' must be an identfier of a variable");
IVariableBinding iVariableDeclarationBinding = iVariableBinding.getVariableDeclaration();
IJavaElement iJavaElement = iVariableDeclarationBinding.getJavaElement();
IJavaASTManager astManager = analysisManagers.getJavaASTManager();
CompilationUnit variableCompilationUnit = astManager.getCompilationUnit(iJavaElement);
VariableDeclarationFragment variableDeclaration = ASTUtils.getVariableDeclarationFragmentNode(iVariableDeclarationBinding, variableCompilationUnit);
TypeDeclaration typeDeclaration = ASTUtils.getDeclaringType(variableDeclaration);
IType newContextIType = (IType) typeDeclaration.resolveBinding().getJavaElement();
if (newContextIType != null)
return getExpressionWithMostSpecificTypeForVariable(variableDeclaration.getName(), newContextIType, visitedMethodDeclarations, analysisManagers, progressMonitor);
return newArrayList(variable);
}
public static ArrayList<Expression> getExpressionWithMostSpecificTypeForMethodInvocation(Expression expression, IType contextIType, ArrayList<MethodDeclaration> visitedMethodDeclarations, final IAnalysisManagers analysisManagers, final IProgressMonitor progressMonitor) {
final IJavaASTManager javaAstManager = analysisManagers.getJavaASTManager();
final IHierarchicalCallGraphManager callGraphManager = analysisManagers.getHierarchicalCallGraphManager();
// make sure the contextIType is in the call graph
callGraphManager.addCompilationUnit(JavaModelUtils.getTypeRoot(contextIType));
IMethod targetIMethod = null;
IMethod executingIMethod = null;
switch (expression.getNodeType()) {
case ASTNode.METHOD_INVOCATION:
MethodInvocation methodInvocation = (MethodInvocation) expression;
IMethodBinding iMethodBinding = methodInvocation.resolveMethodBinding();
if (iMethodBinding == null)
return newEmptyArrayList();
targetIMethod = (IMethod) iMethodBinding.getJavaElement();
if (methodInvocation.getExpression() == null || methodInvocation.getExpression().getNodeType() == ASTNode.THIS_EXPRESSION)
// a call in the hierarchy of the context type
executingIMethod = callGraphManager.getExecutingMethod(contextIType, targetIMethod);
// context remains the same
else {
try {
List<IMethod> implementations = callGraphManager.findImplementations(JavaModelUtils.getDeclaringType(targetIMethod), targetIMethod);
if (implementations.size() == 1)
executingIMethod = implementations.get(0);
else
executingIMethod = targetIMethod;
contextIType = JavaModelUtils.getDeclaringType(executingIMethod != null ? executingIMethod : targetIMethod);
} catch (JavaModelException e) {
e.printStackTrace();
}
}
if (executingIMethod == null || !executingIMethod.exists())
// perhaps outside the hierarchy, proceed with the statically known method
executingIMethod = targetIMethod;
break;
case ASTNode.SUPER_METHOD_INVOCATION:
SuperMethodInvocation superMethodInvocation = (SuperMethodInvocation) expression;
iMethodBinding = superMethodInvocation.resolveMethodBinding();
if (iMethodBinding == null)
return newEmptyArrayList();
targetIMethod = (IMethod) iMethodBinding.getJavaElement();
executingIMethod = targetIMethod;
break;
default:
throw new IllegalArgumentException("getExpressionWithMostSpecificTypeForMethodInvocation(): Parameter 'expression' must be either a MethodInvocation or a SuperMethodInvocation");
}
CompilationUnit executingMethodCU = javaAstManager.getCompilationUnit(executingIMethod);
if (executingMethodCU == null) {
System.err.println("getExpressionWithMostSpecificTypeForMethodInvocation(): Cannot get a compilation unit for: " + executingIMethod);
// cannot follow the call, have to return the invocation expression
return newArrayList(expression);
}
MethodDeclaration executingMethodDeclaration = ASTUtils.getMethodDeclarationNode(executingIMethod, executingMethodCU);
ArrayList<Expression> expressions = getExpressionWithMostSpecificTypeForMethodDeclaration(executingMethodDeclaration, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor);
if (expressions == null)
// the method declaration has been already analysed.
return new ArrayList<Expression>();
if (expressions.isEmpty())
return newArrayList(expression);
return getExpressionWithMostSpecificType(expressions);
}
/**
* @param methodDeclaration
* @param contextIType
* @param visitedMethodDeclarations
* @param analysisManagers
* @param progressMonitor
* @return expressions that have the most specific types of return expressions.
* Returns null if the method was previously analysed.
*/
public static ArrayList<Expression> getExpressionWithMostSpecificTypeForMethodDeclaration(MethodDeclaration methodDeclaration, final IType contextIType, final ArrayList<MethodDeclaration> visitedMethodDeclarations, final IAnalysisManagers analysisManagers, final IProgressMonitor progressMonitor) {
final ArrayList<Expression> result = new ArrayList<Expression>();
// prevent infinite recursion
if (visitedMethodDeclarations.contains(methodDeclaration))
return null;
else
visitedMethodDeclarations.add(methodDeclaration);
methodDeclaration.accept(new ASTVisitor() {
@Override
public boolean visit(ReturnStatement returnStatement) {
Expression expression = returnStatement.getExpression();
if (expression instanceof NullLiteral)
return false;
result.addAll(getExpressionWithMostSpecificTypeForExpression(expression, contextIType, visitedMethodDeclarations, analysisManagers, progressMonitor));
return false;
}
@Override
public boolean visit(ClassInstanceCreation node) {
// prevent the analysis of the contents of the anonymous subclasses
return false;
}
});
return result;
}
}