Skip to content
PDGGraph.java 6.56 KiB
Newer Older
Javier Costa's avatar
Javier Costa committed
package tfm.graphs;
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
import com.github.javaparser.ast.Node;
Javier Costa's avatar
Javier Costa committed
import com.github.javaparser.ast.body.MethodDeclaration;
import edg.graphlib.Arrow;
Javier Costa's avatar
Javier Costa committed
import org.jetbrains.annotations.NotNull;
Javier Costa's avatar
Javier Costa committed
import tfm.arcs.Arc;
import tfm.arcs.data.ArcData;
Javier Costa's avatar
Javier Costa committed
import tfm.arcs.pdg.ControlDependencyArc;
import tfm.arcs.pdg.DataDependencyArc;
Javier Costa's avatar
Javier Costa committed
import tfm.nodes.GraphNode;
Javier Costa's avatar
Javier Costa committed
import tfm.slicing.SlicingCriterion;
Javier Costa's avatar
Javier Costa committed
import tfm.utils.ASTUtils;
Javier Costa's avatar
Javier Costa committed
import tfm.utils.NodeNotFoundException;
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
import java.util.Comparator;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
Javier Costa's avatar
Javier Costa committed
import java.util.stream.Collectors;

/**
 * The <b>Program Dependence Graph</b> represents the statements of a method in
 * a graph, connecting statements according to their {@link ControlDependencyArc control}
 * and {@link DataDependencyArc data} relationships. You can build one manually or use
 * the {@link tfm.visitors.pdg.PDGBuilder PDGBuilder}.
 * @see tfm.exec.Config Config (for the available variations of the PDG)
 */
Javier Costa's avatar
Javier Costa committed
public class PDGGraph extends Graph {
    public static boolean isRanked = false, isSorted = false;
Javier Costa's avatar
Javier Costa committed

Javier Costa's avatar
Javier Costa committed
    private CFGGraph cfgGraph;

Javier Costa's avatar
Javier Costa committed
    public PDGGraph() {
Javier Costa's avatar
Javier Costa committed
        setRootVertex(new GraphNode<>(getNextVertexId(), getRootNodeData(), new MethodDeclaration()));
Javier Costa's avatar
Javier Costa committed
    }

Javier Costa's avatar
Javier Costa committed
    public PDGGraph(CFGGraph cfgGraph) {
        this();
        this.cfgGraph = cfgGraph;
    }

    protected String getRootNodeData() {
        return "Entry";
    }
Javier Costa's avatar
Javier Costa committed

    @Override
Javier Costa's avatar
Javier Costa committed
    public <ASTNode extends Node> GraphNode<ASTNode> addNode(String instruction, ASTNode node) {
Javier Costa's avatar
Javier Costa committed
        return addNode(getNextVertexId(), instruction, node);
Javier Costa's avatar
Javier Costa committed
    }

Javier Costa's avatar
Javier Costa committed
    public <ASTNode extends Node> GraphNode<ASTNode> addNode(int id, String instruction, ASTNode node) {
        GraphNode<ASTNode> vertex = new GraphNode<>(id, instruction, node);
Javier Costa's avatar
Javier Costa committed
        super.addVertex(vertex);

        return vertex;
    }

    @SuppressWarnings("unchecked")
    private void addArc(Arc<? extends ArcData> arc) {
        super.addEdge((Arrow<String, ArcData>) arc);
Javier Costa's avatar
Javier Costa committed
    }

    public void addControlDependencyArc(GraphNode<?> from, GraphNode<?> to) {
Javier Costa's avatar
Javier Costa committed
        ControlDependencyArc controlDependencyArc = new ControlDependencyArc(from, to);

        this.addArc(controlDependencyArc);
    }

    public void addDataDependencyArc(GraphNode<?> from, GraphNode<?> to, String variable) {
Javier Costa's avatar
Javier Costa committed
        DataDependencyArc dataDataDependencyArc = new DataDependencyArc(from, to, variable);

        this.addArc(dataDataDependencyArc);
    }

    public Set<GraphNode<?>> getNodesAtLevel(int level) {
Javier Costa's avatar
Javier Costa committed
        return getVerticies().stream()
                .map(vertex -> (GraphNode<?>) vertex)
Javier Costa's avatar
Javier Costa committed
                .filter(node -> getLevelOf(node) == level)
Javier Costa's avatar
Javier Costa committed
                .collect(Collectors.toSet());
Javier Costa's avatar
Javier Costa committed
    }

    public int getLevels() {
        return getVerticies().stream()
                .map(vertex -> (GraphNode<?>) vertex)
Javier Costa's avatar
Javier Costa committed
                .max(Comparator.comparingInt(this::getLevelOf))
                .map(node -> getLevelOf(node) + 1)
                .orElse(0);
    }

    public int getLevelOf(int nodeId) {
        return findNodeById(nodeId)
                .map(this::getLevelOf)
                .orElseThrow(() -> new NodeNotFoundException("Node with id " + nodeId + " not found in PDG graph"));
    }

    public int getLevelOf(@NotNull GraphNode<?> node) {
        Optional<ControlDependencyArc> optionalControlDependencyArc = node.getIncomingArcs().stream()
                .filter(Arc::isControlDependencyArrow)
                .filter(a -> a.getFromNode().getId() < node.getId())
Javier Costa's avatar
Javier Costa committed
                .findFirst()
                .map(arc -> (ControlDependencyArc) arc);

        if (!optionalControlDependencyArc.isPresent()) {
            return 0;
        }

        GraphNode<?> parent = optionalControlDependencyArc.get().getFromNode();

        return 1 + getLevelOf(parent);
Javier Costa's avatar
Javier Costa committed
    public void setCfgGraph(CFGGraph cfgGraph) {
        this.cfgGraph = cfgGraph;
    }

Javier Costa's avatar
Javier Costa committed
    @Override
    public String toGraphvizRepresentation() {
        String lineSep = System.lineSeparator();

Javier Costa's avatar
Javier Costa committed
        String nodesDeclaration = getNodes().stream()
Javier Costa's avatar
Javier Costa committed
                .sorted(Comparator.comparingInt(GraphNode::getId))
                .map(GraphNode::toGraphvizRepresentation)
Javier Costa's avatar
Javier Costa committed
                .collect(Collectors.joining(lineSep));

Javier Costa's avatar
Javier Costa committed
        StringBuilder rankedNodes = new StringBuilder();

        // No level 0 is needed (only one node)
        for (int i = 0; i < getLevels(); i++) {
            Set<GraphNode<?>> levelNodes = getNodesAtLevel(i);
Javier Costa's avatar
Javier Costa committed

            if (levelNodes.size() <= 1) {
                continue;
            }

            // rank same
            if (isRanked) {
                rankedNodes.append("{ rank = same; ")
                        .append(levelNodes.stream()
                                .map(node -> String.valueOf(node.getId()))
                                .collect(Collectors.joining(";")))
                        .append(" }")
                        .append(lineSep);
            }
Javier Costa's avatar
Javier Costa committed

            // invisible arrows for ordering
            if (isSorted) {
                rankedNodes.append(levelNodes.stream()
Javier Costa's avatar
Javier Costa committed
                        .sorted(Comparator.comparingInt(GraphNode::getId))
Javier Costa's avatar
Javier Costa committed
                        .map(node -> String.valueOf(node.getId()))
                        .collect(Collectors.joining(" -> ")))
                        .append("[style = invis];")
                        .append(lineSep);
            }
Javier Costa's avatar
Javier Costa committed
        String arrows =
Javier Costa's avatar
Javier Costa committed
                getArcs().stream()
                        .sorted(Comparator.comparingInt(arrow -> ((GraphNode<?>) arrow.getFrom()).getId()))
Javier Costa's avatar
Javier Costa committed
                        .map(Arc::toGraphvizRepresentation)
Javier Costa's avatar
Javier Costa committed
                        .collect(Collectors.joining(lineSep));


        return "digraph g{" + lineSep +
                "splines=true;" + lineSep +
                nodesDeclaration + lineSep +
                arrows + lineSep +
                rankedNodes.toString() +
Javier Costa's avatar
Javier Costa committed
                "}";
    }
    public Set<Integer> slice(SlicingCriterion slicingCriterion) {
        Optional<GraphNode<?>> node = slicingCriterion.findNode(this);
        if (!node.isPresent())
Javier Costa's avatar
Javier Costa committed
            throw new NodeNotFoundException(slicingCriterion);
        Set<Integer> visited = new HashSet<>();
        getSliceNodes(visited, node.get());
        return visited;
    protected void getSliceNodes(Set<Integer> visited, GraphNode<?> node) {
        visited.add(node.getId());
Javier Costa's avatar
Javier Costa committed

        for (Arc<ArcData> arc : node.getIncomingArcs()) {
            GraphNode<?> from = arc.getFromNode();
            if (visited.contains(from.getId()))
                continue;

            getSliceNodes(visited, from);
        }
    }
Javier Costa's avatar
Javier Costa committed

    public CFGGraph getCfgGraph() {
        return cfgGraph;
    }
Javier Costa's avatar
Javier Costa committed
}