Loading src/main/java/tfm/graphs/Graph.java +2 −2 Original line number Diff line number Diff line Loading @@ -82,8 +82,8 @@ public abstract class Graph extends edg.graphlib.Graph<String, ArcData> { } @SuppressWarnings("unchecked") public GraphNode<?> getRootNode() { return (GraphNode<?>) super.getRootVertex(); public <ASTNode extends Node> GraphNode<ASTNode> getRootNode() { return (GraphNode<ASTNode>) super.getRootVertex(); } public abstract <ASTNode extends Node> GraphNode<ASTNode> addNode(String instruction, ASTNode node); Loading src/main/java/tfm/graphs/PDGGraph.java +2 −1 Original line number Diff line number Diff line package tfm.graphs; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.EmptyStmt; import edg.graphlib.Arrow; import org.jetbrains.annotations.NotNull; Loading @@ -25,7 +26,7 @@ public class PDGGraph extends Graph { private CFGGraph cfgGraph; public PDGGraph() { setRootVertex(new GraphNode<>(getNextVertexId(), getRootNodeData(), new EmptyStmt())); setRootVertex(new GraphNode<>(getNextVertexId(), getRootNodeData(), new MethodDeclaration())); } public PDGGraph(CFGGraph cfgGraph) { Loading src/main/java/tfm/graphs/SDGGraph.java +29 −30 Original line number Diff line number Diff line Loading @@ -4,23 +4,19 @@ import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.stmt.EmptyStmt; import edg.graphlib.Visitor; import tfm.arcs.data.ArcData; import tfm.nodes.GraphNode; import tfm.nodes.PDGNode; import tfm.slicing.SlicingCriterion; import tfm.utils.Context; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.*; import java.util.stream.Collectors; public class SDGGraph extends Graph { private List<PDGGraph> pdgGraphList; private Map<Context, PDGGraph> contextPDGGraphMap; public SDGGraph() { this.pdgGraphList = new ArrayList<>(); this.contextPDGGraphMap = new HashMap<>(); } @Override Loading @@ -33,7 +29,8 @@ public class SDGGraph extends Graph { @Override public String toGraphvizRepresentation() { return pdgGraphList.stream().map(PDGGraph::toGraphvizRepresentation).collect(Collectors.joining("\n")); return contextPDGGraphMap.values().stream() .map(PDGGraph::toGraphvizRepresentation).collect(Collectors.joining("\n")); } @Override Loading @@ -41,6 +38,26 @@ public class SDGGraph extends Graph { return this; } public Map<Context, PDGGraph> getContextPDGGraphMap() { return contextPDGGraphMap; } public Set<Context> getContexts() { return contextPDGGraphMap.keySet(); } public Set<MethodDeclaration> getMethods() { return getContexts().stream() .filter(context -> context.getCurrentMethod().isPresent()) .map(context -> context.getCurrentMethod().get()) .collect(Collectors.toSet()); } public Collection<PDGGraph> getPDGs() { return contextPDGGraphMap.values(); } @Deprecated public void addPDG(PDGGraph pdgGraph, MethodDeclaration methodDeclaration) { if (this.rootVertex == null) { this.setRootVertex(new GraphNode<>(getNextVertexId(), methodDeclaration.getNameAsString(), methodDeclaration)); Loading Loading @@ -74,31 +91,13 @@ public class SDGGraph extends Graph { } } public GraphNode<MethodDeclaration> addMethod(MethodDeclaration methodDeclaration, PDGGraph pdgGraph) { GraphNode<MethodDeclaration> node = new GraphNode<>( public void addMethod(MethodDeclaration methodDeclaration, PDGGraph pdgGraph) { GraphNode<MethodDeclaration> methodRootNode = new GraphNode<>( getNextVertexId(), "ENTER " + methodDeclaration.getDeclarationAsString(false, false, true), methodDeclaration ); pdgGraph.depthFirstSearch(pdgGraph.getRootNode(), (Visitor<String, ArcData>) (g, v) -> { if (Objects.equals(g.getRootVertex(), v)) { return; // We don't care about root node (entry node) } PDGNode<?> pdgNode = (PDGNode) v; GraphNode<?> sdgNode = new GraphNode<>( getNextVertexId(), pdgNode.getData(), pdgNode.getAstNode() ); }); super.addVertex(node); return node; super.addVertex(methodRootNode); } } src/main/java/tfm/visitors/pdg/PDGBuilder.java +4 −0 Original line number Diff line number Diff line package tfm.visitors.pdg; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.BlockStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; Loading Loading @@ -33,6 +34,9 @@ public class PDGBuilder extends VoidVisitorAdapter<GraphNode<?>> { if (!methodDeclaration.getBody().isPresent()) return; // Assign the method declaration to the root node of the PDG graph this.pdgGraph.getRootNode().setAstNode(methodDeclaration); BlockStmt methodBody = methodDeclaration.getBody().get(); // build CFG Loading src/main/java/tfm/visitors/sdg/MethodCallReplacer.java +11 −126 Original line number Diff line number Diff line package tfm.visitors.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.graphs.PDGGraph; import tfm.graphs.SDGGraph; import tfm.nodes.GraphNode; import tfm.utils.Context; import java.lang.reflect.Method; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; public class MethodCallReplacer { public class MethodCallReplacer extends VoidVisitorAdapter<Context> { SDGGraph sdgGraph; private SDGGraph sdgGraph; public MethodCallReplacer(SDGGraph sdgGraph) { this.sdgGraph = sdgGraph; } @Override public void visit(MethodCallExpr methodCallExpr, Context context) { Optional<MethodDeclaration> optionalCallingMethod = methodCallExpr.getScope().isPresent() ? shouldMakeCallWithScope(methodCallExpr, context) : shouldMakeCallWithNoScope(methodCallExpr, context); if (!optionalCallingMethod.isPresent()) { return; } // todo make call } 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 = sdgGraph.findDeclarationsOfVariable(scopeName); 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 public void replace() { this.sdgGraph.getContextPDGGraphMap() .forEach((context, pdgGraph) -> { if (!context.getCurrentMethod().isPresent()) { return; // Should NOT happen } // The current method is inside the current class, so we make the call return Optional.of(classMethods.get(0)); context.getCurrentMethod().get().accept(new MethodCallReplacerVisitor(pdgGraph), context); }); } } Loading
src/main/java/tfm/graphs/Graph.java +2 −2 Original line number Diff line number Diff line Loading @@ -82,8 +82,8 @@ public abstract class Graph extends edg.graphlib.Graph<String, ArcData> { } @SuppressWarnings("unchecked") public GraphNode<?> getRootNode() { return (GraphNode<?>) super.getRootVertex(); public <ASTNode extends Node> GraphNode<ASTNode> getRootNode() { return (GraphNode<ASTNode>) super.getRootVertex(); } public abstract <ASTNode extends Node> GraphNode<ASTNode> addNode(String instruction, ASTNode node); Loading
src/main/java/tfm/graphs/PDGGraph.java +2 −1 Original line number Diff line number Diff line package tfm.graphs; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.EmptyStmt; import edg.graphlib.Arrow; import org.jetbrains.annotations.NotNull; Loading @@ -25,7 +26,7 @@ public class PDGGraph extends Graph { private CFGGraph cfgGraph; public PDGGraph() { setRootVertex(new GraphNode<>(getNextVertexId(), getRootNodeData(), new EmptyStmt())); setRootVertex(new GraphNode<>(getNextVertexId(), getRootNodeData(), new MethodDeclaration())); } public PDGGraph(CFGGraph cfgGraph) { Loading
src/main/java/tfm/graphs/SDGGraph.java +29 −30 Original line number Diff line number Diff line Loading @@ -4,23 +4,19 @@ import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.stmt.EmptyStmt; import edg.graphlib.Visitor; import tfm.arcs.data.ArcData; import tfm.nodes.GraphNode; import tfm.nodes.PDGNode; import tfm.slicing.SlicingCriterion; import tfm.utils.Context; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.*; import java.util.stream.Collectors; public class SDGGraph extends Graph { private List<PDGGraph> pdgGraphList; private Map<Context, PDGGraph> contextPDGGraphMap; public SDGGraph() { this.pdgGraphList = new ArrayList<>(); this.contextPDGGraphMap = new HashMap<>(); } @Override Loading @@ -33,7 +29,8 @@ public class SDGGraph extends Graph { @Override public String toGraphvizRepresentation() { return pdgGraphList.stream().map(PDGGraph::toGraphvizRepresentation).collect(Collectors.joining("\n")); return contextPDGGraphMap.values().stream() .map(PDGGraph::toGraphvizRepresentation).collect(Collectors.joining("\n")); } @Override Loading @@ -41,6 +38,26 @@ public class SDGGraph extends Graph { return this; } public Map<Context, PDGGraph> getContextPDGGraphMap() { return contextPDGGraphMap; } public Set<Context> getContexts() { return contextPDGGraphMap.keySet(); } public Set<MethodDeclaration> getMethods() { return getContexts().stream() .filter(context -> context.getCurrentMethod().isPresent()) .map(context -> context.getCurrentMethod().get()) .collect(Collectors.toSet()); } public Collection<PDGGraph> getPDGs() { return contextPDGGraphMap.values(); } @Deprecated public void addPDG(PDGGraph pdgGraph, MethodDeclaration methodDeclaration) { if (this.rootVertex == null) { this.setRootVertex(new GraphNode<>(getNextVertexId(), methodDeclaration.getNameAsString(), methodDeclaration)); Loading Loading @@ -74,31 +91,13 @@ public class SDGGraph extends Graph { } } public GraphNode<MethodDeclaration> addMethod(MethodDeclaration methodDeclaration, PDGGraph pdgGraph) { GraphNode<MethodDeclaration> node = new GraphNode<>( public void addMethod(MethodDeclaration methodDeclaration, PDGGraph pdgGraph) { GraphNode<MethodDeclaration> methodRootNode = new GraphNode<>( getNextVertexId(), "ENTER " + methodDeclaration.getDeclarationAsString(false, false, true), methodDeclaration ); pdgGraph.depthFirstSearch(pdgGraph.getRootNode(), (Visitor<String, ArcData>) (g, v) -> { if (Objects.equals(g.getRootVertex(), v)) { return; // We don't care about root node (entry node) } PDGNode<?> pdgNode = (PDGNode) v; GraphNode<?> sdgNode = new GraphNode<>( getNextVertexId(), pdgNode.getData(), pdgNode.getAstNode() ); }); super.addVertex(node); return node; super.addVertex(methodRootNode); } }
src/main/java/tfm/visitors/pdg/PDGBuilder.java +4 −0 Original line number Diff line number Diff line package tfm.visitors.pdg; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.BlockStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; Loading Loading @@ -33,6 +34,9 @@ public class PDGBuilder extends VoidVisitorAdapter<GraphNode<?>> { if (!methodDeclaration.getBody().isPresent()) return; // Assign the method declaration to the root node of the PDG graph this.pdgGraph.getRootNode().setAstNode(methodDeclaration); BlockStmt methodBody = methodDeclaration.getBody().get(); // build CFG Loading
src/main/java/tfm/visitors/sdg/MethodCallReplacer.java +11 −126 Original line number Diff line number Diff line package tfm.visitors.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.graphs.PDGGraph; import tfm.graphs.SDGGraph; import tfm.nodes.GraphNode; import tfm.utils.Context; import java.lang.reflect.Method; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; public class MethodCallReplacer { public class MethodCallReplacer extends VoidVisitorAdapter<Context> { SDGGraph sdgGraph; private SDGGraph sdgGraph; public MethodCallReplacer(SDGGraph sdgGraph) { this.sdgGraph = sdgGraph; } @Override public void visit(MethodCallExpr methodCallExpr, Context context) { Optional<MethodDeclaration> optionalCallingMethod = methodCallExpr.getScope().isPresent() ? shouldMakeCallWithScope(methodCallExpr, context) : shouldMakeCallWithNoScope(methodCallExpr, context); if (!optionalCallingMethod.isPresent()) { return; } // todo make call } 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 = sdgGraph.findDeclarationsOfVariable(scopeName); 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 public void replace() { this.sdgGraph.getContextPDGGraphMap() .forEach((context, pdgGraph) -> { if (!context.getCurrentMethod().isPresent()) { return; // Should NOT happen } // The current method is inside the current class, so we make the call return Optional.of(classMethods.get(0)); context.getCurrentMethod().get().accept(new MethodCallReplacerVisitor(pdgGraph), context); }); } }