Commit 4aefcb42 authored by Carlos Galindo's avatar Carlos Galindo
Browse files

feat: steps 2 and 3 of the ISCR (section 5.1)

parent 8c03d7d6
Loading
Loading
Loading
Loading
Loading
+50 −29
Original line number Diff line number Diff line
@@ -12,7 +12,6 @@ import es.upv.mist.slicing.arcs.cfg.ControlFlowArc;
import es.upv.mist.slicing.graphs.Buildable;
import es.upv.mist.slicing.graphs.CallGraph;
import es.upv.mist.slicing.graphs.ClassGraph;
import es.upv.mist.slicing.graphs.Graph;
import es.upv.mist.slicing.graphs.augmented.ACFG;
import es.upv.mist.slicing.graphs.cfg.CFG;
import es.upv.mist.slicing.graphs.sdg.InterproceduralDefinitionFinder;
@@ -23,8 +22,10 @@ import es.upv.mist.slicing.nodes.io.ActualIONode;
import es.upv.mist.slicing.nodes.io.CallNode;
import es.upv.mist.slicing.nodes.io.MethodExitNode;
import es.upv.mist.slicing.utils.ASTUtils;
import org.jgrapht.Graph;
import org.jgrapht.alg.connectivity.KosarajuStrongConnectivityInspector;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;

import java.util.*;

@@ -33,13 +34,15 @@ import static es.upv.mist.slicing.util.SingletonCollector.toSingleton;
/**
 * An interprocedural CFG, whose component CFGs are built as ACFGs.
 */
public class ICFG extends Graph implements Buildable<NodeList<CompilationUnit>> {
public class ICFG extends es.upv.mist.slicing.graphs.Graph implements Buildable<NodeList<CompilationUnit>> {

    protected final Map<CallableDeclaration<?>, CFG> cfgMap = ASTUtils.newIdentityHashMap();
    protected boolean built = false;

    public void addControlFlowArc(GraphNode<?> from, GraphNode<?> to) {
        addEdge(from, to, new ControlFlowArc());
    public ControlFlowArc addControlFlowArc(GraphNode<?> from, GraphNode<?> to) {
        ControlFlowArc a = new ControlFlowArc();
        addEdge(from, to, a);
        return a;
    }

    public void addNonExecControlFlowArc(GraphNode<?> from, GraphNode<?> to) {
@@ -68,7 +71,9 @@ public class ICFG extends Graph implements Buildable<NodeList<CompilationUnit>>

    public class Builder {
        protected CallGraph callGraph;
        protected List<Set<Object>> scrs;
        protected final Map<CallGraph.Edge<?>, List<ControlFlowArc>> callGraphEdge2ICFGArcMap = new HashMap<>();
        protected Graph<Graph<GraphNode<?>, Arc>, DefaultEdge> intraSCRs = new DefaultDirectedGraph<>(null, null, false);


        public void build(NodeList<CompilationUnit> units) {
            createClassGraph(units);
@@ -87,36 +92,51 @@ public class ICFG extends Graph implements Buildable<NodeList<CompilationUnit>>
            copyCFGs();
            expandCalls();
            joinCFGs();
            getSCRs(callGraph);
            intraSCRs = computeIntraSCRs();
        }

        protected void getSCRs(CallGraph callGraph) {
            DefaultDirectedGraph<Object, CallGraph.Edge> convertedCallGraph = deleteDuplicatedEdges(callGraph);
            KosarajuStrongConnectivityInspector<Object, CallGraph.Edge> inspector =
                    new KosarajuStrongConnectivityInspector<>(convertedCallGraph);

            scrs = inspector.stronglyConnectedSets();
        protected Graph<Graph<GraphNode<?>, Arc>, DefaultEdge> computeIntraSCRs() {
            // 1. Copiar el ICFG a un nuevo grafo sin arcos duplicados. (opcional)
            var simpleICFG = deleteDuplicatedEdges(ICFG.this);
            // 2. Compute cSCR graph
            var cSCRs = computeCallSCRs();
            // 3. Borrar del ICFG todos los arcos que aparecen en cSCRs
            for (DefaultEdge e : cSCRs.edgeSet()) {
                int callsDeleted = 0;
                for (CallGraph.Vertex src : cSCRs.getEdgeSource(e).vertexSet()) {
                    for (CallGraph.Vertex tgt : cSCRs.getEdgeTarget(e).vertexSet()) {
                        // 3a. Buscar el arco(s) correspondiente en el callgraph
                        for (CallGraph.Edge<?> callEdge : callGraph.getAllEdges(src, tgt)) {
                            // 3b. Buscar el arco(s) correspondientes en el simpleICFG y borrarlos
                            for (ControlFlowArc controlFlowArc : callGraphEdge2ICFGArcMap.get(callEdge))
                                if (simpleICFG.removeEdge(controlFlowArc))
                                    callsDeleted++;
                        }

        public static DefaultDirectedGraph<Object, CallGraph.Edge> deleteDuplicatedEdges(CallGraph callGraph) {
            DefaultDirectedGraph<Object, CallGraph.Edge> simpleGraph = new DefaultDirectedGraph<>(CallGraph.Edge.class);

            for (CallGraph.Vertex v : callGraph.vertexSet()) {
                simpleGraph.addVertex(v);
                    }
                }
                if (callsDeleted < 2)
                    throw new IllegalStateException("The creation of intraSCRs did not delete the minimum amount of call/return arcs");
                if (callsDeleted % 2 == 1)
                    throw new IllegalStateException("The creation of intraSCRs missed a call or a return arc");
            }
            // 4. Generar los SCR del simpleICFG, para producir los intraSCRs
            return new KosarajuStrongConnectivityInspector<>(simpleICFG).getCondensation();
        }

            Map<String, CallGraph.Edge<?>> edgeMap = new HashMap<>();

            for (CallGraph.Edge<?> edge : callGraph.edgeSet()) {
                CallGraph.Vertex src = callGraph.getEdgeSource(edge);
                CallGraph.Vertex tgt = callGraph.getEdgeTarget(edge);
        protected Graph<Graph<CallGraph.Vertex, CallGraph.Edge<?>>, DefaultEdge> computeCallSCRs() {
            return new KosarajuStrongConnectivityInspector<>(deleteDuplicatedEdges(callGraph)).getCondensation();
        }

                String key = src.getDeclaration().getDeclarationAsString() + "->" + tgt.getDeclaration().getDeclarationAsString();
        public static <V, E> DefaultDirectedGraph<V, E> deleteDuplicatedEdges(Graph<V, E> baseGraph) {
            DefaultDirectedGraph<V, E> simpleGraph = new DefaultDirectedGraph<>(null, null, false);

                if (!edgeMap.containsKey(key)) {
            for (V v : baseGraph.vertexSet())
                simpleGraph.addVertex(v);
            for (E edge : baseGraph.edgeSet()) {
                V src = baseGraph.getEdgeSource(edge);
                V tgt = baseGraph.getEdgeTarget(edge);
                if (!simpleGraph.containsEdge(src, tgt))
                    simpleGraph.addEdge(src, tgt, edge);
                    edgeMap.put(key, edge);
                }
            }

            return simpleGraph;
@@ -358,8 +378,9 @@ public class ICFG extends Graph implements Buildable<NodeList<CompilationUnit>>
                GraphNode<?> enterNode = cfgMap.get(callGraph.getEdgeTarget(call).getDeclaration()).getRootNode();
                GraphNode<?> exitNode  = cfgMap.get(callGraph.getEdgeTarget(call).getDeclaration()).getExitNode();
                // Connections
                addControlFlowArc(callNode, enterNode);
                addControlFlowArc(exitNode, returnNode);
                ControlFlowArc call2enter = addControlFlowArc(callNode, enterNode);
                ControlFlowArc exit2return = addControlFlowArc(exitNode, returnNode);
                callGraphEdge2ICFGArcMap.put(call, List.of(call2enter, exit2return));
            }
        }

+153 −0
Original line number Diff line number Diff line
package es.upv.mist.slicing.graphs.icfg;

import es.upv.mist.slicing.arcs.Arc;
import es.upv.mist.slicing.cli.DOTAttributes;
import es.upv.mist.slicing.nodes.GraphNode;
import es.upv.mist.slicing.utils.Logger;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.nio.dot.DOTExporter;

import java.awt.*;
import java.io.*;
import java.util.stream.Collectors;

public abstract class GraphLogIntraSCR<G extends Graph<Graph<GraphNode<?>, Arc>, DefaultEdge>> {

    protected G graph;

    protected String imageName;
    protected String format;
    protected boolean generated = false;
    protected File outputDir = new File("./out/");

    public GraphLogIntraSCR() {
        this(null);
    }

    public GraphLogIntraSCR(G graph) {
        this.graph = graph;
    }

    public void setDirectory(File outputDir) {
        this.outputDir = outputDir;
    }

    public void log() throws IOException {
        Logger.log(
                "****************************\n" +
                        "*           GRAPH          *\n" +
                        "****************************"
        );
        Logger.log(graph);
        Logger.log(
                "****************************\n" +
                        "*         GRAPHVIZ         *\n" +
                        "****************************"
        );
        try (StringWriter stringWriter = new StringWriter()) {
            getDOTExporter().exportGraph(graph, stringWriter);
            stringWriter.append('\n');
            Logger.log(stringWriter.toString());
        }
    }

    public void generateImages() throws IOException {
        generateImages("graph");
    }

    public void generateImages(String imageName) throws IOException {
        generateImages(imageName, "pdf");
    }

    public void generateImages(String imageName, String format) throws IOException {
        this.format = format;
        generated = true;
        File tmpDot = getDotFile(imageName);
        // Graph -> DOT -> file
        try (Writer w = new FileWriter(tmpDot)) {
            getDOTExporter().exportGraph(graph, w);
        }
        // Execute dot
        ProcessBuilder pb = new ProcessBuilder("dot",
                tmpDot.getAbsolutePath(), "-T" + format,
                "-o", getImageFile().getAbsolutePath());
        try {
            int result = pb.start().waitFor();
            if (result == 0)
                tmpDot.deleteOnExit();
            else
                Logger.log("Image generation failed, try running \"" + pb.toString() + "\" on your terminal.");
        } catch (InterruptedException e) {
            Logger.log("Image generation failed\n" + e.getMessage());
        }
    }

    public File getDotFile(String imageName) throws IOException {
        this.imageName = imageName + "-" + graph.getClass().getSimpleName();
        File tmpDot = File.createTempFile("graph-source-", ".dot");
        tmpDot.getParentFile().mkdirs();
        getImageFile().getParentFile().mkdirs();

        // Graph -> DOT -> file
        try (Writer w = new FileWriter(tmpDot)) {
            getDOTExporter().exportGraph(graph, w);
        }

        return tmpDot;
    }

    public void openVisualRepresentation() throws IOException {
        if (!generated) generateImages();
        openFileForUser(getImageFile());
    }

    protected static void openFileForUser(File file) throws IOException {
        if (Desktop.isDesktopSupported()) {
            Desktop.getDesktop().open(file);
            return;
        }
        // Alternative manual opening of the file
        String os = System.getProperty("os.name").toLowerCase();
        String cmd = null;
        if (os.contains("win")) {
            cmd = "";
        } else if (os.contains("mac")) {
            cmd = "open";
        } else if (os.contains("nix") || os.contains("nux") || os.contains("aix")) {
            cmd = "xdg-open";
        }

        if (cmd != null) {
            new ProcessBuilder(cmd, file.getAbsolutePath()).start();
        } else {
            Logger.format("Warning: cannot open file %s in your system (%s)",
                    file.getName(), os);
        }
    }

    public File getImageFile() {
        return new File(outputDir, imageName + "." + format);
    }

    protected DOTExporter<Graph<GraphNode<?>, Arc>, DefaultEdge> getDOTExporter() {
        DOTExporter<Graph<GraphNode<?>, Arc>, DefaultEdge> exporter = new DOTExporter<>();
        exporter.setVertexIdProvider(node -> String.valueOf(node.vertexSet().stream().map(GraphNode::getId).findFirst().orElse(-1L)));
        exporter.setVertexAttributeProvider(v -> vertexAttributes(v).build());
        exporter.setEdgeAttributeProvider(v -> edgeAttributes(v).build());
        return exporter;
    }

    protected DOTAttributes vertexAttributes(Graph<GraphNode<?>, Arc> vertex) {
        DOTAttributes res = new DOTAttributes();
        res.set("label", vertex.vertexSet().stream()
                .map(graphNode -> "%04d: %s".formatted(graphNode.getId(), graphNode.getLabel()))
                .sorted()
                .collect(Collectors.joining("\n")));
        return res;
    }

    protected DOTAttributes edgeAttributes(DefaultEdge arc) {
        return new DOTAttributes();
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -26,8 +26,11 @@ public class ICFGTest {
        createGraph("TestInlineFunctions.java", "grafoInlineVariables");
        System.out.println("Grafo 4 generado...");

        createGraph("TestExamplePaper.java", "grafoInlineVariables");
        createGraph("TestExamplePaper.java", "grafoPaper");
        System.out.println("Grafo 5 generado...");

        createGraph("TestExamplePaperSimple.java", "grafoPaperSimple");
        System.out.println("Grafo 6 generado...");
    }

    private static void createGraph(String fileName, String graphName) throws IOException {
+78 −0
Original line number Diff line number Diff line
public class TestExamplePaperSimple {

    public static void main(String[] args) {

        System.out.println("LLAMAMOS P");
        while (1 > 2) {
            System.out.println("S1");
            p1();
            System.out.println("S2");
            p1();
            System.out.println("S3");
        }

        if (1 > 2) {
            System.out.println("S4");
            r1();
            System.out.println("S5");
        } else {
            System.out.println("S6");
            r1();
            System.out.println("S7");
        }

        if (20 < 10) {
            while (10 > 0) {
                r1();
                System.out.println("S9");
            }
        } else {
            while (10 > 0) {
                r1();
                System.out.println("S10");
            }
        }

        System.out.println("S11");
        l();
        System.out.println("S12");
    }

    public static void p1() { p2(); }
    public static void p2() { p3(); }
    public static void p3() {
        while (1 > 2) {
            System.out.println("P3!");
        }
    }

    public static void r1() {
        System.out.println("S20");
        if (21 > 0) {
            r1();
        }
        System.out.println("S23");
        System.out.println("S24");
    }

    public static void l() {
        if (1 > 0) {
            m();
            System.out.println("L3");
        }
    }

    public static void m() {
        if (1 > 0) {
            n();
            System.out.println("M3");
        }
    }

    public static void n() {
        if (1 > 0) {
            l();
            System.out.println("N3");
        }
    }
}
 No newline at end of file