Loading sdg-core/src/main/java/tfm/graphs/CallGraph.java 0 → 100644 +133 −0 Original line number Diff line number Diff line package tfm.graphs; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.InitializerDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.io.DOTExporter; import java.util.Deque; import java.util.LinkedList; /** * A directed graph which displays the available method declarations as nodes and their * invocations as edges (caller to callee). * <br/> * Method declarations include both {@link ConstructorDeclaration constructors} * and {@link MethodDeclaration method declarations}. * In the future, {@link InitializerDeclaration static initializer blocks} and field initializers will be included. * <br/> * Method calls include only direct method calls, from {@link MethodCallExpr normal call}, * to {@link ObjectCreationExpr object creation} and {@link ExplicitConstructorInvocationStmt * explicit constructor invokation} ({@code this()}, {@code super()}). */ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> { private boolean built = false; public CallGraph() { super(null, null, false); } @Override public void build(NodeList<CompilationUnit> arg) { buildVertices(arg); buildEdges(arg); built = true; } @Override public boolean isBuilt() { return built; } protected void buildVertices(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { @Override public void visit(MethodDeclaration n, Void arg) { addVertex(n); super.visit(n, arg); } @Override public void visit(ConstructorDeclaration n, Void arg) { addVertex(n); super.visit(n, arg); } }, null); } protected void buildEdges(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { private final Deque<CallableDeclaration<?>> declStack = new LinkedList<>(); // ============ Method declarations =========== // There are some locations not considered, which may lead to an error in the stack. // 1. Method calls in non-static field initializations are assigned to all constructors of that class // 2. Method calls in static field initializations are assigned to the static block of that class @Override public void visit(MethodDeclaration n, Void arg) { declStack.push(n); super.visit(n, arg); declStack.pop(); } @Override public void visit(ConstructorDeclaration n, Void arg) { declStack.push(n); super.visit(n, arg); declStack.pop(); } // =============== Method calls =============== @Override public void visit(MethodCallExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } @Override public void visit(ObjectCreationExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } @Override public void visit(ExplicitConstructorInvocationStmt n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } }, null); } public DOTExporter<CallableDeclaration<?>, Edge<?>> getDOTExporter() { int[] id = new int[]{0}; return new DOTExporter<>( decl -> id[0]++ + "", decl -> decl.getDeclarationAsString(false, false, false), e -> e.getCallExpr().toString() ); } public static class Edge<T extends Resolvable<? extends ResolvedMethodLikeDeclaration>> extends DefaultEdge { protected T callExpr; public Edge(T callExpr) { this.callExpr = callExpr; } public T getCallExpr() { return callExpr; } } } sdg-core/src/main/java/tfm/graphs/sdg/sumarcs/AnalysisSummaryArcsBuilder.java 0 → 100644 +130 −0 Original line number Diff line number Diff line package tfm.graphs.sdg.sumarcs; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import tfm.arcs.Arc; import tfm.arcs.sdg.CallArc; import tfm.graphs.CallGraph; import tfm.graphs.sdg.SDG; import tfm.nodes.ActualIONode; import tfm.nodes.FormalIONode; import tfm.nodes.GraphNode; import tfm.nodes.type.NodeType; import tfm.utils.Utils; import java.util.*; import java.util.stream.Collectors; public abstract class AnalysisSummaryArcsBuilder extends SummaryArcsBuilder { private static class SummaryArcPair { FormalIONode in; FormalIONode out; } private final SDG sdg; private final CallGraph callGraph; protected Map<CallableDeclaration<?>, Set<SummaryArcPair>> vertexDataMap = new HashMap<>(); protected boolean built = false; public AnalysisSummaryArcsBuilder(SDG sdg) { super(sdg); CallGraph callGraph = new CallGraph(); callGraph.build(sdg.getCompilationUnits()); this.sdg = sdg; this.callGraph = callGraph; } public AnalysisSummaryArcsBuilder(SDG sdg, CallGraph callGraph) { super(sdg); this.sdg = sdg; this.callGraph = callGraph; } public Set<SummaryArcPair> getResult(MethodDeclaration vertex) { return vertexDataMap.get(vertex); } // Al acabar, los summary edges estan colocados @Override public void visit() { assert !built; List<CallableDeclaration<?>> workList = new LinkedList<>(callGraph.vertexSet()); callGraph.vertexSet().forEach(v -> vertexDataMap.put(v, computeSummaryArcs(v))); while (!workList.isEmpty()) { List<CallableDeclaration<?>> newWorkList = new LinkedList<>(); for (CallableDeclaration<?> vertex : workList) { updateVertex(vertex); Set<SummaryArcPair> newValue = computeSummaryArcs(vertex); // now with new arcs!!! if (!Objects.equals(vertexDataMap.get(vertex), newValue)) { vertexDataMap.put(vertex, newValue); newWorkList.addAll(callGraph.incomingEdgesOf(vertex).stream() .map(callGraph::getEdgeSource).collect(Collectors.toSet())); } } workList = newWorkList; } vertexDataMap = Collections.unmodifiableMap(vertexDataMap); built = true; } protected void updateVertex(CallableDeclaration<?> declaration) { if (!declaration.isMethodDeclaration()) { return; // Parse only method declarations } Optional<GraphNode<MethodDeclaration>> optionalMethodDeclarationNode = sdg.findNodeByASTNode(declaration.asMethodDeclaration()); if (optionalMethodDeclarationNode.isEmpty()) { return; } GraphNode<MethodDeclaration> methodDeclarationNode = optionalMethodDeclarationNode.get(); // Copiar los summaries que hay en 'vertexDataMap' a todas las llamadas a metodo contenidas en este vertices (ver outgoingEdges) // Get call arcs from this declaration Set<CallArc> methodCallExprNodes = sdg.incomingEdgesOf(methodDeclarationNode).stream() .filter(Arc::isCallArc) .map(Arc::asCallArc) .collect(Collectors.toSet()); for (CallArc callArc : methodCallExprNodes) { GraphNode<?> methodCallNode = sdg.getEdgeSource(callArc); for (SummaryArcPair summaryArcPair : vertexDataMap.getOrDefault(declaration, Utils.emptySet())) { FormalIONode inFormalNode = summaryArcPair.in; FormalIONode outFormalNode = summaryArcPair.out; Optional<ActualIONode> optionalIn = sdg.outgoingEdgesOf(methodCallNode).stream() .filter(arc -> (sdg.getEdgeTarget(arc)).getNodeType() == NodeType.ACTUAL_IN) .map(arc -> (ActualIONode) sdg.getEdgeTarget(arc)) .filter(actualIONode -> actualIONode.matchesFormalIO(inFormalNode)) .findFirst(); Optional<ActualIONode> optionalOut = sdg.outgoingEdgesOf(methodCallNode).stream() .filter(arc -> (sdg.getEdgeTarget(arc)).getNodeType() == NodeType.ACTUAL_OUT) .map(arc -> (ActualIONode) sdg.getEdgeTarget(arc)) .filter(actualIONode -> actualIONode.matchesFormalIO(outFormalNode)) .findFirst(); if (optionalIn.isEmpty() || optionalOut.isEmpty()) { continue; } sdg.addSummaryArc(optionalIn.get(), optionalOut.get()); } } } protected Set<SummaryArcPair> computeSummaryArcs(CallableDeclaration<?> declaration) { // Para cada formal-out, hacer slice intraprocedural. Anotar los formal-in incluidos. IMPORTANTE, SUMMARY SON INTRAPROC. // TODO return null; } } Loading
sdg-core/src/main/java/tfm/graphs/CallGraph.java 0 → 100644 +133 −0 Original line number Diff line number Diff line package tfm.graphs; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.InitializerDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.io.DOTExporter; import java.util.Deque; import java.util.LinkedList; /** * A directed graph which displays the available method declarations as nodes and their * invocations as edges (caller to callee). * <br/> * Method declarations include both {@link ConstructorDeclaration constructors} * and {@link MethodDeclaration method declarations}. * In the future, {@link InitializerDeclaration static initializer blocks} and field initializers will be included. * <br/> * Method calls include only direct method calls, from {@link MethodCallExpr normal call}, * to {@link ObjectCreationExpr object creation} and {@link ExplicitConstructorInvocationStmt * explicit constructor invokation} ({@code this()}, {@code super()}). */ public class CallGraph extends DirectedPseudograph<CallableDeclaration<?>, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> { private boolean built = false; public CallGraph() { super(null, null, false); } @Override public void build(NodeList<CompilationUnit> arg) { buildVertices(arg); buildEdges(arg); built = true; } @Override public boolean isBuilt() { return built; } protected void buildVertices(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { @Override public void visit(MethodDeclaration n, Void arg) { addVertex(n); super.visit(n, arg); } @Override public void visit(ConstructorDeclaration n, Void arg) { addVertex(n); super.visit(n, arg); } }, null); } protected void buildEdges(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { private final Deque<CallableDeclaration<?>> declStack = new LinkedList<>(); // ============ Method declarations =========== // There are some locations not considered, which may lead to an error in the stack. // 1. Method calls in non-static field initializations are assigned to all constructors of that class // 2. Method calls in static field initializations are assigned to the static block of that class @Override public void visit(MethodDeclaration n, Void arg) { declStack.push(n); super.visit(n, arg); declStack.pop(); } @Override public void visit(ConstructorDeclaration n, Void arg) { declStack.push(n); super.visit(n, arg); declStack.pop(); } // =============== Method calls =============== @Override public void visit(MethodCallExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } @Override public void visit(ObjectCreationExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } @Override public void visit(ExplicitConstructorInvocationStmt n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, new Edge<>(n))); super.visit(n, arg); } }, null); } public DOTExporter<CallableDeclaration<?>, Edge<?>> getDOTExporter() { int[] id = new int[]{0}; return new DOTExporter<>( decl -> id[0]++ + "", decl -> decl.getDeclarationAsString(false, false, false), e -> e.getCallExpr().toString() ); } public static class Edge<T extends Resolvable<? extends ResolvedMethodLikeDeclaration>> extends DefaultEdge { protected T callExpr; public Edge(T callExpr) { this.callExpr = callExpr; } public T getCallExpr() { return callExpr; } } }
sdg-core/src/main/java/tfm/graphs/sdg/sumarcs/AnalysisSummaryArcsBuilder.java 0 → 100644 +130 −0 Original line number Diff line number Diff line package tfm.graphs.sdg.sumarcs; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import tfm.arcs.Arc; import tfm.arcs.sdg.CallArc; import tfm.graphs.CallGraph; import tfm.graphs.sdg.SDG; import tfm.nodes.ActualIONode; import tfm.nodes.FormalIONode; import tfm.nodes.GraphNode; import tfm.nodes.type.NodeType; import tfm.utils.Utils; import java.util.*; import java.util.stream.Collectors; public abstract class AnalysisSummaryArcsBuilder extends SummaryArcsBuilder { private static class SummaryArcPair { FormalIONode in; FormalIONode out; } private final SDG sdg; private final CallGraph callGraph; protected Map<CallableDeclaration<?>, Set<SummaryArcPair>> vertexDataMap = new HashMap<>(); protected boolean built = false; public AnalysisSummaryArcsBuilder(SDG sdg) { super(sdg); CallGraph callGraph = new CallGraph(); callGraph.build(sdg.getCompilationUnits()); this.sdg = sdg; this.callGraph = callGraph; } public AnalysisSummaryArcsBuilder(SDG sdg, CallGraph callGraph) { super(sdg); this.sdg = sdg; this.callGraph = callGraph; } public Set<SummaryArcPair> getResult(MethodDeclaration vertex) { return vertexDataMap.get(vertex); } // Al acabar, los summary edges estan colocados @Override public void visit() { assert !built; List<CallableDeclaration<?>> workList = new LinkedList<>(callGraph.vertexSet()); callGraph.vertexSet().forEach(v -> vertexDataMap.put(v, computeSummaryArcs(v))); while (!workList.isEmpty()) { List<CallableDeclaration<?>> newWorkList = new LinkedList<>(); for (CallableDeclaration<?> vertex : workList) { updateVertex(vertex); Set<SummaryArcPair> newValue = computeSummaryArcs(vertex); // now with new arcs!!! if (!Objects.equals(vertexDataMap.get(vertex), newValue)) { vertexDataMap.put(vertex, newValue); newWorkList.addAll(callGraph.incomingEdgesOf(vertex).stream() .map(callGraph::getEdgeSource).collect(Collectors.toSet())); } } workList = newWorkList; } vertexDataMap = Collections.unmodifiableMap(vertexDataMap); built = true; } protected void updateVertex(CallableDeclaration<?> declaration) { if (!declaration.isMethodDeclaration()) { return; // Parse only method declarations } Optional<GraphNode<MethodDeclaration>> optionalMethodDeclarationNode = sdg.findNodeByASTNode(declaration.asMethodDeclaration()); if (optionalMethodDeclarationNode.isEmpty()) { return; } GraphNode<MethodDeclaration> methodDeclarationNode = optionalMethodDeclarationNode.get(); // Copiar los summaries que hay en 'vertexDataMap' a todas las llamadas a metodo contenidas en este vertices (ver outgoingEdges) // Get call arcs from this declaration Set<CallArc> methodCallExprNodes = sdg.incomingEdgesOf(methodDeclarationNode).stream() .filter(Arc::isCallArc) .map(Arc::asCallArc) .collect(Collectors.toSet()); for (CallArc callArc : methodCallExprNodes) { GraphNode<?> methodCallNode = sdg.getEdgeSource(callArc); for (SummaryArcPair summaryArcPair : vertexDataMap.getOrDefault(declaration, Utils.emptySet())) { FormalIONode inFormalNode = summaryArcPair.in; FormalIONode outFormalNode = summaryArcPair.out; Optional<ActualIONode> optionalIn = sdg.outgoingEdgesOf(methodCallNode).stream() .filter(arc -> (sdg.getEdgeTarget(arc)).getNodeType() == NodeType.ACTUAL_IN) .map(arc -> (ActualIONode) sdg.getEdgeTarget(arc)) .filter(actualIONode -> actualIONode.matchesFormalIO(inFormalNode)) .findFirst(); Optional<ActualIONode> optionalOut = sdg.outgoingEdgesOf(methodCallNode).stream() .filter(arc -> (sdg.getEdgeTarget(arc)).getNodeType() == NodeType.ACTUAL_OUT) .map(arc -> (ActualIONode) sdg.getEdgeTarget(arc)) .filter(actualIONode -> actualIONode.matchesFormalIO(outFormalNode)) .findFirst(); if (optionalIn.isEmpty() || optionalOut.isEmpty()) { continue; } sdg.addSummaryArc(optionalIn.get(), optionalOut.get()); } } } protected Set<SummaryArcPair> computeSummaryArcs(CallableDeclaration<?> declaration) { // Para cada formal-out, hacer slice intraprocedural. Anotar los formal-in incluidos. IMPORTANTE, SUMMARY SON INTRAPROC. // TODO return null; } }