Commit efad178c authored by Carlos Galindo's avatar Carlos Galindo
Browse files

fix: give condensations ids to enable modification.



Previously:
- Each condensation (generated with KosarajuStrongConnectivityInspector) was a Graph of Graphs
- The subgraphs could not be modified, as JGraphT relies on hashCode being constant for storage in hash maps and sets.
- We modified the subgraphs, leading to the subgraphs no longer being a part of the condensation graph (cSCRs and intraSCRs).

After the fix:
- Each subgraph has a unique id, in which hashCode and equals rely. Thus, they can be freely modified without issue.
- Previously complex types have been simplified to IntraSCRGraph (condensation of ICFG), IntraSCR (a node of that condensation), CallSCRGraph (condensation of CallGraph), CallSCR (a node of that condensation).

Co-authored-by: default avatarJonathan Andrade <jandbur@upv.edu.es>
parent 9005b203
Loading
Loading
Loading
Loading
Loading
+40 −35
Original line number Diff line number Diff line
@@ -14,6 +14,10 @@ import es.upv.mist.slicing.graphs.CallGraph;
import es.upv.mist.slicing.graphs.ClassGraph;
import es.upv.mist.slicing.graphs.augmented.ACFG;
import es.upv.mist.slicing.graphs.cfg.CFG;
import es.upv.mist.slicing.graphs.scrs.CallSCR;
import es.upv.mist.slicing.graphs.scrs.CallSCRGraph;
import es.upv.mist.slicing.graphs.scrs.IntraSCR;
import es.upv.mist.slicing.graphs.scrs.IntraSCRGraph;
import es.upv.mist.slicing.graphs.sdg.InterproceduralDefinitionFinder;
import es.upv.mist.slicing.graphs.sdg.InterproceduralUsageFinder;
import es.upv.mist.slicing.nodes.GraphNode;
@@ -24,7 +28,6 @@ import es.upv.mist.slicing.nodes.io.MethodExitNode;
import es.upv.mist.slicing.utils.ASTUtils;
import org.jgrapht.Graph;
import org.jgrapht.Graphs;
import org.jgrapht.alg.connectivity.KosarajuStrongConnectivityInspector;
import org.jgrapht.alg.util.Triple;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
@@ -77,15 +80,15 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
        /** A map to locate interprocedural {@link ControlFlowArc}s that correspond to a given {@link CallGraph}'s edge. */
        protected final Map<CallGraph.Edge<?>, List<ControlFlowArc>> callGraphEdge2ICFGArcMap = new HashMap<>();
        /** The strongly connected components of the {@link CallGraph}. */
        protected Graph<Graph<CallGraph.Vertex, CallGraph.Edge<?>>, DefaultEdge> cSCRs = new DefaultDirectedGraph<>(null, null, false);
        protected CallSCRGraph cSCRs;
        /** A map to locate declarations in the {@link #cSCRs}. */
        protected Map<CallableDeclaration<?>, Graph<CallGraph.Vertex, CallGraph.Edge<?>>> cSCRsMap;
        protected Map<CallableDeclaration<?>, CallSCR> cSCRsMap;
        /** A map to locate the set of {@link #intraSCRs} nodes that correspond (transitively) to a given {@link #cSCRs} node. */
        protected final Map<Graph<CallGraph.Vertex, CallGraph.Edge<?>>, Set<Graph<GraphNode<?>, Arc>>> transitiveMap = new HashMap<>();
        protected final Map<CallSCR, Set<IntraSCR>> transitiveMap = new HashMap<>();
        /** The strongly connected components of the {@link ICFG}, computed while ignoring
         *  interprocedural edges that connect {@link #cSCRs} nodes. <br>
         *  Fulfils steps 2-3 of the Nanda-Ramesh topological numbers algorithm. */
        protected Graph<Graph<GraphNode<?>, Arc>, DefaultEdge> intraSCRs = new DefaultDirectedGraph<>(null, null, false);
        protected IntraSCRGraph intraSCRs;
        /** Non-Recursive interprocedural Arcs from {@link  #intraSCRs} */
        protected Set<Triple<GraphNode<?>, GraphNode<?>, ControlFlowArc>> interprocNonRecArcs = new HashSet<>();

@@ -113,8 +116,8 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<

        private void addEdgesToIntraSCRs(Set<Triple<GraphNode<?>, GraphNode<?>, ControlFlowArc>> deletedArcs) {
            for (Triple<GraphNode<?>, GraphNode<?>, ControlFlowArc> arc : deletedArcs) {
                for (Graph<GraphNode<?>, Arc> srcSCR : intraSCRs.vertexSet()) {
                    for (Graph<GraphNode<?>, Arc> tgtSCR : intraSCRs.vertexSet()) {
                for (IntraSCR srcSCR : intraSCRs.vertexSet()) {
                    for (IntraSCR tgtSCR : intraSCRs.vertexSet()) {
                        if (srcSCR.containsVertex(arc.getFirst()) && tgtSCR.containsVertex(arc.getSecond())) {
                            if (srcSCR == tgtSCR)
                                srcSCR.addEdge(arc.getFirst(), arc.getSecond(), arc.getThird());
@@ -128,12 +131,12 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
        }

        private void buildCacheTransitive() {
            Set<Graph<CallGraph.Vertex, CallGraph.Edge<?>>> processedCSCRNode = new HashSet<>();
            Set<CallSCR> processedCSCRNode = new HashSet<>();

            // startFromMain
            // getMain vertex
            Graph<CallGraph.Vertex, CallGraph.Edge<?>> mainNode = null;
            for (Graph<CallGraph.Vertex, CallGraph.Edge<?>> cSCRNode : cSCRs.vertexSet()) {
            CallSCR mainNode = null;
            for (CallSCR cSCRNode : cSCRs.vertexSet()) {
                if (cSCRs.inDegreeOf(cSCRNode) == 0) {
                    mainNode = cSCRNode;
                    break;
@@ -143,27 +146,27 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
            cacheTransitive(mainNode, processedCSCRNode);
        }

        private void cacheTransitive(Graph<CallGraph.Vertex, CallGraph.Edge<?>> cSCRNode, Set<Graph<CallGraph.Vertex, CallGraph.Edge<?>>> processedCSCRs) {
        private void cacheTransitive(CallSCR cSCRNode, Set<CallSCR> processedCSCRs) {
            if (processedCSCRs.contains(cSCRNode)) {
                return;
            }
            processedCSCRs.add(cSCRNode);

            Set<Graph<GraphNode<?>, Arc>> intraSCRNodes = getEnterNodesRelatedToProcess(cSCRNode);
            Set<Graph<GraphNode<?>, Arc>> SCRNodes = new HashSet<>();
            Set<IntraSCR> intraSCRNodes = getEnterNodesRelatedToProcess(cSCRNode);
            Set<IntraSCR> SCRNodes = new HashSet<>();
            getAllIntraSCRNodes(intraSCRNodes, SCRNodes);
            transitiveMap.computeIfAbsent(cSCRNode, k -> new HashSet<>()).addAll(SCRNodes);

            for (Graph<CallGraph.Vertex, CallGraph.Edge<?>> successor : Graphs.successorListOf(cSCRs, cSCRNode)) {
            for (CallSCR successor : Graphs.successorListOf(cSCRs, cSCRNode)) {
                cacheTransitive(successor, processedCSCRs);
                transitiveMap.get(cSCRNode).addAll(transitiveMap.get(successor));
            }
        }

        private Set<Graph<GraphNode<?>, Arc>> getEnterNodesRelatedToProcess(Graph<CallGraph.Vertex, CallGraph.Edge<?>> cSCRNode) {
            Set<Graph<GraphNode<?>, Arc>> intraSCRNodes = new HashSet<>();
        private Set<IntraSCR> getEnterNodesRelatedToProcess(CallSCR cSCRNode) {
            Set<IntraSCR> intraSCRNodes = new HashSet<>();
            for (CallGraph.Vertex process : cSCRNode.vertexSet()) {
                for (Graph<GraphNode<?>, Arc> intraSCR : intraSCRs.vertexSet()) {
                for (IntraSCR intraSCR : intraSCRs.vertexSet()) {
                    if (intraSCRs.inDegreeOf(intraSCR) == 0)
                        for (GraphNode<?> graphNode : intraSCR.vertexSet()) {
                            if (graphNode.getAstNode() instanceof MethodDeclaration
@@ -176,10 +179,10 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
            return intraSCRNodes;
        }

        private void getAllIntraSCRNodes(Set<Graph<GraphNode<?>, Arc>> intraSCRNodes, Set<Graph<GraphNode<?>, Arc>> SCRNodes) {
            for (Graph<GraphNode<?>, Arc> intraSCRNode : intraSCRNodes) {
        private void getAllIntraSCRNodes(Set<IntraSCR> intraSCRNodes, Set<IntraSCR> SCRNodes) {
            for (IntraSCR intraSCRNode : intraSCRNodes) {
                SCRNodes.add(intraSCRNode);
                Iterator<Graph<GraphNode<?>, Arc>> iterator = new DepthFirstIterator<>(intraSCRs,intraSCRNode);
                Iterator<IntraSCR> iterator = new DepthFirstIterator<>(intraSCRs,intraSCRNode);
                while (iterator.hasNext()) {
                    SCRNodes.add(iterator.next());
                }
@@ -187,17 +190,17 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
        }

        private void buildISCR() {
            List<Graph<GraphNode<?>, Arc>> processedIntraSCRs = new ArrayList<>();
            List<IntraSCR> processedIntraSCRs = new ArrayList<>();
            // startFromMain
            // getMain vertex
            Graph<GraphNode<?>, Arc> mainIntraSCR = null;
            List<Graph<GraphNode<?>, Arc>> listIntraSCR = new ArrayList<>();
            for (Graph<GraphNode<?>, Arc> intraSCR : intraSCRs.vertexSet()) {
            IntraSCR mainIntraSCR = null;
            List<IntraSCR> listIntraSCR = new ArrayList<>();
            for (IntraSCR intraSCR : intraSCRs.vertexSet()) {
                if(intraSCRs.incomingEdgesOf(intraSCR).isEmpty()) {
                    listIntraSCR.add(intraSCR);
                }
            }
            for (Graph<GraphNode<?>, Arc> intraSCRNode : listIntraSCR) {
            for (IntraSCR intraSCRNode : listIntraSCR) {
                for (GraphNode<?> graphNode : intraSCRNode.vertexSet()) {
                    MethodDeclaration declaration = (MethodDeclaration) graphNode.getAstNode();
                    if(declaration.isPublic() && declaration.isStatic() && declaration.getType().isVoidType()) {
@@ -208,7 +211,7 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
            buildISCRGraph(mainIntraSCR, processedIntraSCRs);
        }

        private static void mergeGraphs(Graph<GraphNode<?>, Arc> target, Graph<GraphNode<?>, Arc> src) {
        private static void mergeGraphs(IntraSCR target, IntraSCR src) {
            for (GraphNode<?> n : src.vertexSet())
                if (!target.addVertex(n))
                    throw new RuntimeException();
@@ -217,7 +220,7 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
                    throw new RuntimeException();
        }

        private void buildISCRGraph(Graph<GraphNode<?>, Arc> intraSCR, List<Graph<GraphNode<?>, Arc>> processedIntraSCRs) {
        private void buildISCRGraph(IntraSCR intraSCR, List<IntraSCR> processedIntraSCRs) {
            if (processedIntraSCRs.contains(intraSCR)) {
                return;
            }
@@ -225,22 +228,24 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
            Set<GraphNode<?>> graphNodes = intraSCR.vertexSet();
            if (graphNodes.size() > 1) {
                //multi
                Set<IntraSCR> toBeMerged = new HashSet<>();
                for (GraphNode<?> graphNode : graphNodes) {
                    if (graphNode instanceof CallNode) {
                        for (Triple<GraphNode<?>, GraphNode<?>, ControlFlowArc> arc : interprocNonRecArcs) {
                            if (graphNode.equals(arc.getFirst())) {
                                GraphNode<CallableDeclaration<?>> enterNode = (GraphNode<CallableDeclaration<?>>) arc.getSecond();
                                for (Graph<GraphNode<?>, Arc> graph : transitiveMap.get(cSCRsMap.get(enterNode.getAstNode()))) {
                                    mergeGraphs(intraSCR, graph);
                                }
                                Graph<GraphNode<?>, Arc> iSCRtarget = intraSCRs.vertexSet().stream()
                                toBeMerged.addAll(transitiveMap.get(cSCRsMap.get(enterNode.getAstNode())));
                                IntraSCR iSCRtarget = intraSCRs.vertexSet().stream()
                                        .filter(iSCR -> iSCR.containsVertex(enterNode))
                                        .findFirst().orElseThrow();
                                intraSCRs.removeEdge(intraSCR, iSCRtarget);
                                break;
                            }
                        }
                    }
                }
                for (IntraSCR graph : toBeMerged)
                    mergeGraphs(intraSCR, graph);
            } else if (graphNodes.size() == 1) {
                //single

@@ -248,7 +253,7 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
                throw new IllegalStateException("The intraSCRs is empty");
            }

            for (Graph<GraphNode<?>, Arc> successor : Graphs.successorListOf(intraSCRs, intraSCR)) {
            for (IntraSCR successor : Graphs.successorListOf(intraSCRs, intraSCR)) {
                buildISCRGraph(successor, processedIntraSCRs);
            }
        }
@@ -299,7 +304,7 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
                        throw new IllegalStateException("The creation of intraSCRs missed a call or a return arc");
                }
            // 4. Generar los SCR del simpleICFG, para producir los intraSCRs
            intraSCRs = new KosarajuStrongConnectivityInspector<>(simpleICFG).getCondensation();
            intraSCRs = new IntraSCRGraph(simpleICFG);
            // buildCache algorithm
            buildCacheTransitive();
            // Only now can interprocedural edges be re-added to intraSCRs, cache building required
@@ -307,9 +312,9 @@ public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<
        }

        protected void computeCallSCRs() {
            cSCRs = new KosarajuStrongConnectivityInspector<>(deleteDuplicatedEdges(callGraph)).getCondensation();
            cSCRs = new CallSCRGraph(deleteDuplicatedEdges(callGraph));
            cSCRsMap = new HashMap<>(callGraph.vertexSet().size());
            for (Graph<CallGraph.Vertex, CallGraph.Edge<?>> cSCR : cSCRs.vertexSet())
            for (CallSCR cSCR : cSCRs.vertexSet())
                for (CallGraph.Vertex vertex : cSCR.vertexSet())
                    cSCRsMap.put(vertex.getDeclaration(), cSCR);
        }
+37 −0
Original line number Diff line number Diff line
package es.upv.mist.slicing.graphs.scrs;

import org.jgrapht.Graph;
import org.jgrapht.graph.AsSubgraph;

import java.util.Objects;
import java.util.Set;

/**
 * An abstract Strongly Connected Region (or Component) of a graph g, implemented as a subgraph of g.
 * This SCR can be freely modified (edges, vertices added and removed), and it will remain a part of the condensation.
 * @param <V> The type of vertices in the original graph.
 * @param <E> The type of edges in the original graph.
 * @see AbstractSCRAlgorithm
 */
public abstract class AbstractSCR<V, E> extends AsSubgraph<V, E> {
    /** A unique id to */
    protected int id;

    public AbstractSCR(Graph<V, E> base, Set<? extends V> vertexSubset, Set<? extends E> edgeSubset) {
        super(base, vertexSubset, edgeSubset);
    }

    public int getId() {
        return id;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(id);
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof AbstractSCR scr && this.id == scr.id;
    }
}
+64 −0
Original line number Diff line number Diff line
package es.upv.mist.slicing.graphs.scrs;

import org.jgrapht.Graph;
import org.jgrapht.alg.connectivity.KosarajuStrongConnectivityInspector;
import org.jgrapht.graph.DefaultEdge;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A wrapper for a Strong Connectivity Algorithm, to modify the generated condensation and enable the modification
 * of such condensations, by using {@link AbstractSCR}.<br>
 * A condensation of the strongly connected regions of a {@code Graph<A, B>} with vertices of type {@code A}
 * and edges of type {@code B} is {@code Graph<Graph<A, B>, DefaultEdge>}, a new graph whose vertices are subgraphs
 * (strongly connected regions) of the initial graph, connected by {@link DefaultEdge}.<br>
 * This class uses a more specific type for the vertices of the condensation, yielding {@code Graph<R, DefaultEdge>}
 * for the condensation. This type is a wrapper on the subgraph, with implementations of {@code hashCode} and {@code equals}
 * that do not change when the graph changes, therefore allowing modifications.
 * @param <V> The type of vertices on the original graph.
 * @param <E> The type of edges on the original graph.
 * @param <R> The type of vertices on the condensed graph.
 * @see org.jgrapht.alg.interfaces.StrongConnectivityAlgorithm
 * @see AbstractSCR
 */
public abstract class AbstractSCRAlgorithm<V, E, R extends AbstractSCR<V, E>> extends KosarajuStrongConnectivityInspector<V, E> {
    public AbstractSCRAlgorithm(Graph<V, E> graph) {
        super(graph);
    }

    public abstract R newSCR(Graph<V, E> graph, Set<V> nodeSet, Set<E> edgeSet);

    /**
     * Reimplementation of {@link org.jgrapht.alg.connectivity.AbstractStrongConnectivityInspector#getCondensation()} to wrap each strongly
     * connected component into a class with constant {@code hashCode} and {@code equals} implementation.
     */
    public void copySCRs(Graph<R, DefaultEdge> condensation) {
        List<Set<V>> sets = stronglyConnectedSets();

        Map<V, R> vertexToComponent = new HashMap<>();

        for (Set<V> set : sets) {
            R component = newSCR(graph, set, null);
            condensation.addVertex(component);
            for (V v : set) {
                vertexToComponent.put(v, component);
            }
        }

        for (E e : graph.edgeSet()) {
            V s = graph.getEdgeSource(e);
            R sComponent = vertexToComponent.get(s);

            V t = graph.getEdgeTarget(e);
            R tComponent = vertexToComponent.get(t);

            if (sComponent != tComponent) { // reference equal on purpose
                // TODO: instead of DefaultEdges, use a grouping class to hold multiple data edges (Arc, CallGraph.Edge)
                condensation.addEdge(sComponent, tComponent);
            }
        }
    }
}
+19 −0
Original line number Diff line number Diff line
package es.upv.mist.slicing.graphs.scrs;

import es.upv.mist.slicing.graphs.CallGraph;
import org.jgrapht.Graph;

import java.util.Set;

/**
 * A strongly connected region (or component) on a {@link CallGraph}.
 * @see AbstractSCR
 */
public class CallSCR extends AbstractSCR<CallGraph.Vertex, CallGraph.Edge<?>> {
    private static int nextId = 1;

    public CallSCR(Graph<CallGraph.Vertex, CallGraph.Edge<?>> base, Set<? extends CallGraph.Vertex> vertexSubset, Set<? extends CallGraph.Edge<?>> edgeSubset) {
        super(base, vertexSubset, edgeSubset);
        this.id = nextId++;
    }
}
+25 −0
Original line number Diff line number Diff line
package es.upv.mist.slicing.graphs.scrs;

import es.upv.mist.slicing.graphs.CallGraph;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.SimpleDirectedGraph;

import java.util.Set;

/**
 * A condensation of the strongly connected regions of a {@link CallGraph}.
 * @see AbstractSCRAlgorithm
 * @see CallSCR CallSCR: The component members of this graph.
 */
public class CallSCRGraph extends SimpleDirectedGraph<CallSCR, DefaultEdge> {
    public CallSCRGraph(Graph<CallGraph.Vertex, CallGraph.Edge<?>> graph) {
        super(DefaultEdge.class);
        new AbstractSCRAlgorithm<CallGraph.Vertex, CallGraph.Edge<?>, CallSCR>(graph) {
            @Override
            public CallSCR newSCR(Graph<CallGraph.Vertex, CallGraph.Edge<?>> graph, Set<CallGraph.Vertex> nodeSet, Set<CallGraph.Edge<?>> edgeSet) {
                return new CallSCR(graph, nodeSet, edgeSet);
            }
        }.copySCRs(this);
    }
}
Loading