Newer
Older
package tfm.graphs.sdg;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import tfm.nodes.GraphNode;
import tfm.utils.Context;
import tfm.utils.Logger;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> {
private SDG sdg;
private GraphNode<ExpressionStmt> methodCallNode;
public MethodCallReplacerVisitor(SDG sdg) {
this.sdg = sdg;
}
@Override
public void visit(ExpressionStmt n, Context arg) {
Optional<GraphNode<ExpressionStmt>> optionalNode = sdg.findNodeByASTNode(n);
assert optionalNode.isPresent();
methodCallNode = optionalNode.get();
super.visit(n, arg);
}
@Override
public void visit(MethodCallExpr methodCallExpr, Context context) {
Logger.log("MethodCallReplacerVisitor", context);
Optional<MethodDeclaration> optionalCallingMethod = methodCallExpr.getScope().isPresent()
? shouldMakeCallWithScope(methodCallExpr, context)
: shouldMakeCallWithNoScope(methodCallExpr, context);
if (!optionalCallingMethod.isPresent()) {
Logger.log("Discarding: " + methodCallExpr);
Logger.log(String.format("%s | Method '%s' called", methodCallExpr, optionalCallingMethod.get().getNameAsString()));
}
private Optional<MethodDeclaration> shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) {
assert methodCallExpr.getScope().isPresent();
String scopeName = methodCallExpr.getScope().get().toString();
if (!context.getCurrentClass().isPresent()) {
return Optional.empty();
}
ClassOrInterfaceDeclaration currentClass = context.getCurrentClass().get();
// Check if it's a static method call of current class
if (!Objects.equals(scopeName, currentClass.getNameAsString())) {
// Check if 'scopeName' is a variable
List<GraphNode<?>> declarations = sdg.findDeclarationsOfVariable(scopeName, methodCallNode);
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
if (declarations.isEmpty()) {
// It is a static method call of another class. We do nothing
return Optional.empty();
}
/*
It's a variable since it has declarations. We now have to check if the class name
is the same as the current class (the object is an instance of our class)
*/
GraphNode<?> declarationNode = declarations.get(declarations.size() - 1);
ExpressionStmt declarationExpr = (ExpressionStmt) declarationNode.getAstNode();
VariableDeclarationExpr variableDeclarationExpr = declarationExpr.getExpression().asVariableDeclarationExpr();
Optional<VariableDeclarator> optionalVariableDeclarator = variableDeclarationExpr.getVariables().stream()
.filter(variableDeclarator -> Objects.equals(variableDeclarator.getNameAsString(), scopeName))
.findFirst();
if (!optionalVariableDeclarator.isPresent()) {
// should not happen
return Optional.empty();
}
Type variableType = optionalVariableDeclarator.get().getType();
if (!variableType.isClassOrInterfaceType()) {
// Not class type
return Optional.empty();
}
if (!Objects.equals(variableType.asClassOrInterfaceType().getNameAsString(), currentClass.getNameAsString())) {
// object is not instance of our class
return Optional.empty();
}
// if we got here, the object is instance of our class
}
// It's a static method call to a method of the current class
return findMethodInClass(methodCallExpr, currentClass);
}
private Optional<MethodDeclaration> shouldMakeCallWithNoScope(MethodCallExpr methodCallExpr, Context context) {
assert !methodCallExpr.getScope().isPresent();
/*
May be a call to a method of the current class or a call to an imported static method.
In the first case, we make the call. Otherwise, not.
*/
if (!context.getCurrentClass().isPresent()) {
return Optional.empty();
}
// We get the current class and search along their methods to find the one we're looking for...
ClassOrInterfaceDeclaration currentClass = context.getCurrentClass().get();
return findMethodInClass(methodCallExpr, currentClass);
}
private Optional<MethodDeclaration> findMethodInClass(MethodCallExpr methodCallExpr, ClassOrInterfaceDeclaration klass) {
String[] typeParameters = methodCallExpr.getTypeArguments()
.map(types -> types.stream()
.map(Node::toString)
.collect(Collectors.toList())
.toArray(new String[types.size()])
).orElse(new String[]{});
List<MethodDeclaration> classMethods =
klass.getMethodsBySignature(methodCallExpr.getNameAsString(), typeParameters);
if (classMethods.isEmpty()) {
return Optional.empty(); // The method called is not inside the current class
}
// The current method is inside the current class, so we make the call
return Optional.of(classMethods.get(0));
}
}