Loading src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +162 −14 Original line number Diff line number Diff line package tfm.graphs.sdg; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.stmt.ExpressionStmt; import com.github.javaparser.ast.stmt.ReturnStmt; import com.github.javaparser.ast.stmt.Statement; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.nodes.GraphNode; import tfm.nodes.factories.InVariableNodeFactory; import tfm.nodes.factories.OutVariableNodeFactory; import tfm.utils.Context; import tfm.utils.Logger; Loading @@ -21,12 +29,25 @@ import java.util.stream.Collectors; class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> { private SDG sdg; private GraphNode<ExpressionStmt> methodCallNode; private GraphNode<?> methodCallNode; public MethodCallReplacerVisitor(SDG sdg) { this.sdg = sdg; } private void searchAndSetMethodCallNode(Node node) { Optional<GraphNode<Node>> optionalNode = sdg.findNodeByASTNode(node); assert optionalNode.isPresent(); methodCallNode = optionalNode.get(); } @Override public void visit(ReturnStmt n, Context arg) { searchAndSetMethodCallNode(n); super.visit(n, arg); } @Override public void visit(ExpressionStmt n, Context arg) { Optional<GraphNode<ExpressionStmt>> optionalNode = sdg.findNodeByASTNode(n); Loading @@ -51,8 +72,48 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> { return; } MethodDeclaration methodCalled = optionalCallingMethod.get(); Optional<GraphNode<MethodDeclaration>> optionalCalledMethodNode = sdg.findNodeByASTNode(methodCalled); assert optionalCalledMethodNode.isPresent(); GraphNode<MethodDeclaration> calledMethodNode = optionalCalledMethodNode.get(); sdg.addCallArc(methodCallNode, calledMethodNode); for (Parameter parameter : calledMethodNode.getAstNode().getParameters()) { // In expression VariableDeclarationExpr inVariableDeclarationExpr = new VariableDeclarationExpr( new VariableDeclarator( parameter.getType(), parameter.getNameAsString() + "_in", new NameExpr(parameter.getNameAsString()) ) ); ExpressionStmt inExprStmt = new ExpressionStmt(inVariableDeclarationExpr); GraphNode<ExpressionStmt> argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, new InVariableNodeFactory()); // Out expression VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( new VariableDeclarator( parameter.getType(), parameter.getNameAsString(), new NameExpr(parameter.getNameAsString() + "_out") ) ); ExpressionStmt outExprStmt = new ExpressionStmt(outVariableDeclarationExpr); GraphNode<ExpressionStmt> argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, new OutVariableNodeFactory()); } // todo make call Logger.log(String.format("%s | Method '%s' called", methodCallExpr, optionalCallingMethod.get().getNameAsString())); Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodCalled.getNameAsString())); } private Optional<MethodDeclaration> shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) { Loading Loading @@ -135,21 +196,108 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> { } 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[]{}); int argumentsCount = methodCallExpr.getArguments().size(); // Get methods with equal name and parameter count List<MethodDeclaration> classMethods = klass.getMethodsByName(methodCallExpr.getNameAsString()).stream() // Filter methods with equal or less (varargs) number of parameters .filter(methodDeclaration -> { NodeList<Parameter> parameters = methodDeclaration.getParameters(); List<MethodDeclaration> classMethods = klass.getMethodsBySignature(methodCallExpr.getNameAsString(), typeParameters); if (parameters.size() == argumentsCount) { return true; } if (parameters.isEmpty()) { return false; } // There are more arguments than parameters. May be OK if last parameter is varargs if (parameters.size() < argumentsCount) { return parameters.get(parameters.size() - 1).isVarArgs(); } // There are less arguments than parameters. May be OK if the last parameter is varargs // and it's omited return parameters.size() - 1 == argumentsCount && parameters.get(parameters.size() - 1).isVarArgs(); }) .collect(Collectors.toList()); if (classMethods.isEmpty()) { return Optional.empty(); // The method called is not inside the current class // No methods in class with that name and parameter count return Optional.empty(); } // The current method is inside the current class, so we make the call if (classMethods.size() == 1) { // We found the method! return Optional.of(classMethods.get(0)); } /* * Tricky one! We have to match argument and parameter types, so we have to: * - Differentiate arguments expressions: * - Easy: In case of CastExpr, get the type * - Easy: In case of ObjectCreationExpr, get the type * - Medium: In case of NameExpr, find the declaration and its type * - Medium: In case of LiteralExpr, get the type * - Hard: In case of MethodCallExpr, find MethodDeclaration and its type * - If there is a varargs parameter, check every argument corresponding to it has the same type * * Example: * At this point these three methods are considered as called: * private void foo(int a, int b) {} * private void foo(String a, String b) {} * private void foo(String a, int... bs) {} * * We have to match types to get the correct one * * */ return classMethods.stream().filter(methodDeclaration -> { boolean match = true; for (int i = 0; i < methodDeclaration.getParameters().size(); i++) { if (!match) { break; } if (argumentsCount < i) { return argumentsCount == i - 1 && methodDeclaration.getParameter(i).isVarArgs(); } // TODO - Convert into a visitor Expression argumentExpression = methodCallExpr.getArgument(i); Parameter parameter = methodDeclaration.getParameter(i); if (argumentExpression.isCastExpr()) { match = Objects.equals(argumentExpression.asCastExpr().getType(), parameter.getType()); } else if (argumentExpression.isObjectCreationExpr()) { match = Objects.equals(argumentExpression.asObjectCreationExpr().getType(), parameter.getType()); } else if (argumentExpression.isNameExpr()) { String variableName = argumentExpression.asNameExpr().getNameAsString(); List<GraphNode<?>> declarationsOfVariable = sdg.findDeclarationsOfVariable(argumentExpression.asNameExpr().getNameAsString(), methodCallNode); assert !declarationsOfVariable.isEmpty(); GraphNode<?> declarationNode = declarationsOfVariable.get(declarationsOfVariable.size() - 1); ExpressionStmt expressionStmt = (ExpressionStmt) declarationNode.getAstNode(); assert expressionStmt.getExpression().isVariableDeclarationExpr(); match = expressionStmt.getExpression().asVariableDeclarationExpr().getVariables().stream() .filter(variableDeclarator -> Objects.equals(variableDeclarator.getName().asString(), variableName)) .findFirst() .map(variableDeclarator -> Objects.equals(variableDeclarator.getType(), parameter.getType())) .orElse(false); } // TODO: More checks } return match; }).findFirst(); } } Loading
src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +162 −14 Original line number Diff line number Diff line package tfm.graphs.sdg; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.stmt.ExpressionStmt; import com.github.javaparser.ast.stmt.ReturnStmt; import com.github.javaparser.ast.stmt.Statement; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import tfm.nodes.GraphNode; import tfm.nodes.factories.InVariableNodeFactory; import tfm.nodes.factories.OutVariableNodeFactory; import tfm.utils.Context; import tfm.utils.Logger; Loading @@ -21,12 +29,25 @@ import java.util.stream.Collectors; class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> { private SDG sdg; private GraphNode<ExpressionStmt> methodCallNode; private GraphNode<?> methodCallNode; public MethodCallReplacerVisitor(SDG sdg) { this.sdg = sdg; } private void searchAndSetMethodCallNode(Node node) { Optional<GraphNode<Node>> optionalNode = sdg.findNodeByASTNode(node); assert optionalNode.isPresent(); methodCallNode = optionalNode.get(); } @Override public void visit(ReturnStmt n, Context arg) { searchAndSetMethodCallNode(n); super.visit(n, arg); } @Override public void visit(ExpressionStmt n, Context arg) { Optional<GraphNode<ExpressionStmt>> optionalNode = sdg.findNodeByASTNode(n); Loading @@ -51,8 +72,48 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> { return; } MethodDeclaration methodCalled = optionalCallingMethod.get(); Optional<GraphNode<MethodDeclaration>> optionalCalledMethodNode = sdg.findNodeByASTNode(methodCalled); assert optionalCalledMethodNode.isPresent(); GraphNode<MethodDeclaration> calledMethodNode = optionalCalledMethodNode.get(); sdg.addCallArc(methodCallNode, calledMethodNode); for (Parameter parameter : calledMethodNode.getAstNode().getParameters()) { // In expression VariableDeclarationExpr inVariableDeclarationExpr = new VariableDeclarationExpr( new VariableDeclarator( parameter.getType(), parameter.getNameAsString() + "_in", new NameExpr(parameter.getNameAsString()) ) ); ExpressionStmt inExprStmt = new ExpressionStmt(inVariableDeclarationExpr); GraphNode<ExpressionStmt> argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, new InVariableNodeFactory()); // Out expression VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( new VariableDeclarator( parameter.getType(), parameter.getNameAsString(), new NameExpr(parameter.getNameAsString() + "_out") ) ); ExpressionStmt outExprStmt = new ExpressionStmt(outVariableDeclarationExpr); GraphNode<ExpressionStmt> argumentOutNode = sdg.addNode(outExprStmt.toString(), outExprStmt, new OutVariableNodeFactory()); } // todo make call Logger.log(String.format("%s | Method '%s' called", methodCallExpr, optionalCallingMethod.get().getNameAsString())); Logger.log("MethodCallReplacerVisitor", String.format("%s | Method '%s' called", methodCallExpr, methodCalled.getNameAsString())); } private Optional<MethodDeclaration> shouldMakeCallWithScope(MethodCallExpr methodCallExpr, Context context) { Loading Loading @@ -135,21 +196,108 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> { } 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[]{}); int argumentsCount = methodCallExpr.getArguments().size(); // Get methods with equal name and parameter count List<MethodDeclaration> classMethods = klass.getMethodsByName(methodCallExpr.getNameAsString()).stream() // Filter methods with equal or less (varargs) number of parameters .filter(methodDeclaration -> { NodeList<Parameter> parameters = methodDeclaration.getParameters(); List<MethodDeclaration> classMethods = klass.getMethodsBySignature(methodCallExpr.getNameAsString(), typeParameters); if (parameters.size() == argumentsCount) { return true; } if (parameters.isEmpty()) { return false; } // There are more arguments than parameters. May be OK if last parameter is varargs if (parameters.size() < argumentsCount) { return parameters.get(parameters.size() - 1).isVarArgs(); } // There are less arguments than parameters. May be OK if the last parameter is varargs // and it's omited return parameters.size() - 1 == argumentsCount && parameters.get(parameters.size() - 1).isVarArgs(); }) .collect(Collectors.toList()); if (classMethods.isEmpty()) { return Optional.empty(); // The method called is not inside the current class // No methods in class with that name and parameter count return Optional.empty(); } // The current method is inside the current class, so we make the call if (classMethods.size() == 1) { // We found the method! return Optional.of(classMethods.get(0)); } /* * Tricky one! We have to match argument and parameter types, so we have to: * - Differentiate arguments expressions: * - Easy: In case of CastExpr, get the type * - Easy: In case of ObjectCreationExpr, get the type * - Medium: In case of NameExpr, find the declaration and its type * - Medium: In case of LiteralExpr, get the type * - Hard: In case of MethodCallExpr, find MethodDeclaration and its type * - If there is a varargs parameter, check every argument corresponding to it has the same type * * Example: * At this point these three methods are considered as called: * private void foo(int a, int b) {} * private void foo(String a, String b) {} * private void foo(String a, int... bs) {} * * We have to match types to get the correct one * * */ return classMethods.stream().filter(methodDeclaration -> { boolean match = true; for (int i = 0; i < methodDeclaration.getParameters().size(); i++) { if (!match) { break; } if (argumentsCount < i) { return argumentsCount == i - 1 && methodDeclaration.getParameter(i).isVarArgs(); } // TODO - Convert into a visitor Expression argumentExpression = methodCallExpr.getArgument(i); Parameter parameter = methodDeclaration.getParameter(i); if (argumentExpression.isCastExpr()) { match = Objects.equals(argumentExpression.asCastExpr().getType(), parameter.getType()); } else if (argumentExpression.isObjectCreationExpr()) { match = Objects.equals(argumentExpression.asObjectCreationExpr().getType(), parameter.getType()); } else if (argumentExpression.isNameExpr()) { String variableName = argumentExpression.asNameExpr().getNameAsString(); List<GraphNode<?>> declarationsOfVariable = sdg.findDeclarationsOfVariable(argumentExpression.asNameExpr().getNameAsString(), methodCallNode); assert !declarationsOfVariable.isEmpty(); GraphNode<?> declarationNode = declarationsOfVariable.get(declarationsOfVariable.size() - 1); ExpressionStmt expressionStmt = (ExpressionStmt) declarationNode.getAstNode(); assert expressionStmt.getExpression().isVariableDeclarationExpr(); match = expressionStmt.getExpression().asVariableDeclarationExpr().getVariables().stream() .filter(variableDeclarator -> Objects.equals(variableDeclarator.getName().asString(), variableName)) .findFirst() .map(variableDeclarator -> Objects.equals(variableDeclarator.getType(), parameter.getType())) .orElse(false); } // TODO: More checks } return match; }).findFirst(); } }