Loading src/main/java/tfm/graphs/sdg/MethodCallReplacer.java +4 −14 Original line number Diff line number Diff line Loading @@ -16,19 +16,9 @@ class MethodCallReplacer { this.sdg = sdg; } public void replace() { this.sdg.getContexts().stream() .filter(context -> context.getCurrentMethod().isPresent()) .forEach(context -> { Logger.log("MethodCallReplacer", context); Optional<GraphNode<MethodDeclaration>> optionalRootNode = this.sdg.getRootNode(context); if (!optionalRootNode.isPresent()) { return; // We don't have visited the code (e.g. the MethodDeclaration for a method call) public void replace(Context context) { for (MethodDeclaration methodDeclaration : this.sdg.getMethodDeclarations()) { methodDeclaration.accept(new MethodCallReplacerVisitor(sdg), context); } optionalRootNode.get().getAstNode().accept(new MethodCallReplacerVisitor(sdg), context); }); } } src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +2 −190 Original line number Diff line number Diff line Loading @@ -145,6 +145,8 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> { GraphNode<ExpressionStmt> argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_IN)); sdg.addControlDependencyArc(methodCallNode, argumentInNode); // Out expression VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( new VariableDeclarator( Loading Loading @@ -179,194 +181,4 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> { return Optional.empty(); } } /** * Handles method calls with scope. Examples: * - System.out.println() -> println() is a method call with scope System.out * - new A().getB() -> getB() is a method call with scope new A() */ 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); 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) { 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(); 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()) { // No methods in class with that name and parameter count return Optional.empty(); } 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(); } } src/main/java/tfm/graphs/sdg/SDG.java +10 −35 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ import tfm.arcs.sdg.CallArc; import tfm.arcs.sdg.ParameterInOutArc; import tfm.graphs.Buildable; import tfm.graphs.Graph; import tfm.graphs.cfg.CFG; import tfm.graphs.pdg.PDG; import tfm.nodes.*; import tfm.slicing.Slice; Loading @@ -25,10 +26,10 @@ import java.util.stream.Collectors; public class SDG extends Graph implements Sliceable, Buildable<NodeList<CompilationUnit>> { private boolean built = false; private Map<Context, Long> contextToMethodRoot; private Map<MethodDeclaration, CFG> methodCFGMap; public SDG() { this.contextToMethodRoot = new HashMap<>(); this.methodCFGMap = new HashMap<>(); } @Override Loading @@ -46,46 +47,20 @@ public class SDG extends Graph implements Sliceable, Buildable<NodeList<Compilat return built; } public Set<Context> getContexts() { return contextToMethodRoot.keySet(); public Set<MethodDeclaration> getMethodDeclarations() { return this.methodCFGMap.keySet(); } @SuppressWarnings("unchecked") public List<GraphNode<MethodDeclaration>> getMethodRoots() { return contextToMethodRoot.values().stream() .map(id -> findNodeById(id)) .filter(Optional::isPresent) .map(optional -> (GraphNode<MethodDeclaration>) optional.get()) .collect(Collectors.toList()); public void setMethodCFG(MethodDeclaration methodDeclaration, CFG cfg) { this.methodCFGMap.put(methodDeclaration, cfg); } @SuppressWarnings("unchecked") public Optional<GraphNode<MethodDeclaration>> getRootNode(Context context) { Long id = this.contextToMethodRoot.get(context); if (id == null) { public Optional<CFG> getMethodCFG(MethodDeclaration methodDeclaration) { if (!this.methodCFGMap.containsKey(methodDeclaration)) { return Optional.empty(); } return findNodeById(id).map(node -> (GraphNode<MethodDeclaration>) node); } public void addRootNode(Context context, long id) { if (!findNodeById(id).isPresent()) throw new IllegalArgumentException("Root node with id " + id + " is not contained in graph!"); this.contextToMethodRoot.put(new Context(context), id); } public void addRootNode(Context context, GraphNode<MethodDeclaration> node) { addRootNode(context, node.getId()); } public Optional<Context> getContext(long id) { return contextToMethodRoot.entrySet().stream() .filter(entry -> Objects.equals(entry.getValue(), id)) .findFirst() .map(Map.Entry::getKey); return Optional.of(this.methodCFGMap.get(methodDeclaration)); } public void addControlDependencyArc(GraphNode<?> from, GraphNode<?> to) { Loading src/main/java/tfm/graphs/sdg/SDGBuilder.java +3 −3 Original line number Diff line number Diff line Loading @@ -51,8 +51,8 @@ class SDGBuilder extends VoidVisitorAdapter<Context> { GraphNode<MethodDeclaration> methodDeclarationNode = pdg.getRootNode().get(); // Add root node from PDG sdg.addRootNode(context, methodDeclarationNode.getId()); // Add CFG sdg.setMethodCFG(methodDeclaration, pdg.getCfg()); } @Override Loading @@ -72,7 +72,7 @@ class SDGBuilder extends VoidVisitorAdapter<Context> { // Once every PDG is built, expand method call nodes of each one // and link them to the corresponding method declaration node MethodCallReplacer methodCallReplacer = new MethodCallReplacer(sdg); methodCallReplacer.replace(); methodCallReplacer.replace(context); Loading Loading
src/main/java/tfm/graphs/sdg/MethodCallReplacer.java +4 −14 Original line number Diff line number Diff line Loading @@ -16,19 +16,9 @@ class MethodCallReplacer { this.sdg = sdg; } public void replace() { this.sdg.getContexts().stream() .filter(context -> context.getCurrentMethod().isPresent()) .forEach(context -> { Logger.log("MethodCallReplacer", context); Optional<GraphNode<MethodDeclaration>> optionalRootNode = this.sdg.getRootNode(context); if (!optionalRootNode.isPresent()) { return; // We don't have visited the code (e.g. the MethodDeclaration for a method call) public void replace(Context context) { for (MethodDeclaration methodDeclaration : this.sdg.getMethodDeclarations()) { methodDeclaration.accept(new MethodCallReplacerVisitor(sdg), context); } optionalRootNode.get().getAstNode().accept(new MethodCallReplacerVisitor(sdg), context); }); } }
src/main/java/tfm/graphs/sdg/MethodCallReplacerVisitor.java +2 −190 Original line number Diff line number Diff line Loading @@ -145,6 +145,8 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> { GraphNode<ExpressionStmt> argumentInNode = sdg.addNode(inExprStmt.toString(), inExprStmt, TypeNodeFactory.fromType(NodeType.VARIABLE_IN)); sdg.addControlDependencyArc(methodCallNode, argumentInNode); // Out expression VariableDeclarationExpr outVariableDeclarationExpr = new VariableDeclarationExpr( new VariableDeclarator( Loading Loading @@ -179,194 +181,4 @@ class MethodCallReplacerVisitor extends VoidVisitorAdapter<Context> { return Optional.empty(); } } /** * Handles method calls with scope. Examples: * - System.out.println() -> println() is a method call with scope System.out * - new A().getB() -> getB() is a method call with scope new A() */ 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); 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) { 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(); 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()) { // No methods in class with that name and parameter count return Optional.empty(); } 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(); } }
src/main/java/tfm/graphs/sdg/SDG.java +10 −35 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ import tfm.arcs.sdg.CallArc; import tfm.arcs.sdg.ParameterInOutArc; import tfm.graphs.Buildable; import tfm.graphs.Graph; import tfm.graphs.cfg.CFG; import tfm.graphs.pdg.PDG; import tfm.nodes.*; import tfm.slicing.Slice; Loading @@ -25,10 +26,10 @@ import java.util.stream.Collectors; public class SDG extends Graph implements Sliceable, Buildable<NodeList<CompilationUnit>> { private boolean built = false; private Map<Context, Long> contextToMethodRoot; private Map<MethodDeclaration, CFG> methodCFGMap; public SDG() { this.contextToMethodRoot = new HashMap<>(); this.methodCFGMap = new HashMap<>(); } @Override Loading @@ -46,46 +47,20 @@ public class SDG extends Graph implements Sliceable, Buildable<NodeList<Compilat return built; } public Set<Context> getContexts() { return contextToMethodRoot.keySet(); public Set<MethodDeclaration> getMethodDeclarations() { return this.methodCFGMap.keySet(); } @SuppressWarnings("unchecked") public List<GraphNode<MethodDeclaration>> getMethodRoots() { return contextToMethodRoot.values().stream() .map(id -> findNodeById(id)) .filter(Optional::isPresent) .map(optional -> (GraphNode<MethodDeclaration>) optional.get()) .collect(Collectors.toList()); public void setMethodCFG(MethodDeclaration methodDeclaration, CFG cfg) { this.methodCFGMap.put(methodDeclaration, cfg); } @SuppressWarnings("unchecked") public Optional<GraphNode<MethodDeclaration>> getRootNode(Context context) { Long id = this.contextToMethodRoot.get(context); if (id == null) { public Optional<CFG> getMethodCFG(MethodDeclaration methodDeclaration) { if (!this.methodCFGMap.containsKey(methodDeclaration)) { return Optional.empty(); } return findNodeById(id).map(node -> (GraphNode<MethodDeclaration>) node); } public void addRootNode(Context context, long id) { if (!findNodeById(id).isPresent()) throw new IllegalArgumentException("Root node with id " + id + " is not contained in graph!"); this.contextToMethodRoot.put(new Context(context), id); } public void addRootNode(Context context, GraphNode<MethodDeclaration> node) { addRootNode(context, node.getId()); } public Optional<Context> getContext(long id) { return contextToMethodRoot.entrySet().stream() .filter(entry -> Objects.equals(entry.getValue(), id)) .findFirst() .map(Map.Entry::getKey); return Optional.of(this.methodCFGMap.get(methodDeclaration)); } public void addControlDependencyArc(GraphNode<?> from, GraphNode<?> to) { Loading
src/main/java/tfm/graphs/sdg/SDGBuilder.java +3 −3 Original line number Diff line number Diff line Loading @@ -51,8 +51,8 @@ class SDGBuilder extends VoidVisitorAdapter<Context> { GraphNode<MethodDeclaration> methodDeclarationNode = pdg.getRootNode().get(); // Add root node from PDG sdg.addRootNode(context, methodDeclarationNode.getId()); // Add CFG sdg.setMethodCFG(methodDeclaration, pdg.getCfg()); } @Override Loading @@ -72,7 +72,7 @@ class SDGBuilder extends VoidVisitorAdapter<Context> { // Once every PDG is built, expand method call nodes of each one // and link them to the corresponding method declaration node MethodCallReplacer methodCallReplacer = new MethodCallReplacer(sdg); methodCallReplacer.replace(); methodCallReplacer.replace(context); Loading