Verified Commit 3c771a29 authored by Carlos Galindo's avatar Carlos Galindo
Browse files

TAPAS-2020: implementation of the algorithm proposed

parent 59640025
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeS
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import es.upv.mist.slicing.graphs.augmented.ASDG;
import es.upv.mist.slicing.graphs.augmented.PSDG;
import es.upv.mist.slicing.graphs.augmented.TapasSDG;
import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG;
import es.upv.mist.slicing.graphs.sdg.SDG;
import es.upv.mist.slicing.slicing.FileLineSlicingCriterion;
@@ -236,6 +237,7 @@ public class Slicer {
            case "ASDG":  sdg = new ASDG();     break;
            case "PSDG":  sdg = new PSDG();     break;
            case "ESSDG": sdg = new ESSDG();    break;
            case "TSDG":  sdg = new TapasSDG(); break;
            default:
                throw new IllegalArgumentException("Unknown type of graph. Available graphs are SDG, ASDG, PSDG, ESSDG");
        }
+4 −2
Original line number Diff line number Diff line
@@ -69,12 +69,13 @@ public class ACFGBuilder extends CFGBuilder {
        // Link previous statement to the switch's selector
        switchEntriesStack.push(new LinkedList<>());
        breakStack.push(new LinkedList<>());
        GraphNode<?> cond = connectTo(switchStmt, String.format("switch (%s)", switchStmt.getSelector()));
        GraphNode<SwitchStmt> cond = connectTo(switchStmt, String.format("switch (%s)", switchStmt.getSelector()));
        hangingNodes.remove(cond);
        switchStack.push(cond);
        switchStmt.getSelector().accept(this, arg);
        // expr --> each case (fallthrough by default, so case --> case too)
        for (SwitchEntryStmt entry : switchStmt.getEntries()) {
            entry.accept(this, arg); // expr && prev case --> case --> next case
            hangingNodes.add(cond); // expr --> next case
        }
        // The next statement will be linked to:
        //		1. All break statements that broke from the switch (done with break section)
@@ -84,6 +85,7 @@ public class ACFGBuilder extends CFGBuilder {
        if (ASTUtils.switchHasDefaultCase(switchStmt))
            hangingNodes.remove(cond);
        List<GraphNode<SwitchEntryStmt>> entries = switchEntriesStack.pop();
        switchStack.pop();
        GraphNode<SwitchEntryStmt> def = null;
        for (GraphNode<SwitchEntryStmt> entry : entries) {
            if (entry.getAstNode().getLabel().isEmpty()) {
+70 −0
Original line number Diff line number Diff line
package es.upv.mist.slicing.graphs.augmented;

import es.upv.mist.slicing.arcs.Arc;
import es.upv.mist.slicing.arcs.pdg.ControlDependencyArc;
import es.upv.mist.slicing.nodes.GraphNode;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * An alternative program dependence graph to the {@link PPDG}, which
 * performs the same optimization without altering the slicing algorithm,
 * thus guaranteeing that it's kept ats its original linear time complexity.
 */
public class TapasPDG extends APDG {
    public TapasPDG() {
        super();
    }

    public TapasPDG(ACFG acfg) {
        super(acfg);
    }

    @Override
    protected Builder createBuilder() {
        return new Builder();
    }

    public class Builder extends APDG.Builder {
        @Override
        protected void buildControlDependency() {
            super.buildControlDependency();
            algorithm1();
        }

        /** Removes all incoming edges of a pseudo-predicate, when (1) there is a control
         *  dependency between itself and another pseudo-predicate, and (2) both jump to
         *  the same instruction. */
        protected void algorithm1() {
            Set<ControlDependencyArc> Ac = edgeSet().stream()
                    .filter(Arc::isControlDependencyArc)
                    .map(ControlDependencyArc.class::cast)
                    .collect(Collectors.toSet());
            Set<Arc> arcsToRemove = new HashSet<>();
            for (ControlDependencyArc arc : Ac) {
                GraphNode<?> ns = getEdgeSource(arc);
                GraphNode<?> ne = getEdgeTarget(arc);
                ACFG acfg = (ACFG) cfg;
                if (acfg.isPseudoPredicate(ns) && acfg.isPseudoPredicate(ne) && jumpDest(ns).equals(jumpDest(ne))) {
                    edgeSet().stream()
                            .filter(Arc::isControlDependencyArc)
                            .filter(a -> getEdgeTarget(a).equals(ne))
                            .forEach(arcsToRemove::add);
                }
            }
            arcsToRemove.forEach(TapasPDG.this::removeEdge);
        }

        /** Obtain the target of a jump instruction. In the CFG, it should
         *  have just one executable outgoing edge. */
        protected GraphNode<?> jumpDest(GraphNode<?> jumpNode) {
            Set<Arc> outgoing = new HashSet<>(cfg.outgoingEdgesOf(jumpNode));
            outgoing.removeIf(Arc::isNonExecutableControlFlowArc);
            if (outgoing.size() != 1)
                throw new IllegalArgumentException("Jump has 0 or multiple executable outgoing edges.");
            return cfg.getEdgeTarget(outgoing.iterator().next());
        }
    }
}
+19 −0
Original line number Diff line number Diff line
package es.upv.mist.slicing.graphs.augmented;

import es.upv.mist.slicing.graphs.cfg.CFG;
import es.upv.mist.slicing.graphs.pdg.PDG;

public class TapasSDG extends ASDG {
    @Override
    protected Builder createBuilder() {
        return new Builder();
    }

    public class Builder extends ASDG.Builder {
        @Override
        protected PDG createPDG(CFG cfg) {
            assert cfg instanceof ACFG;
            return new TapasPDG((ACFG) cfg);
        }
    }
}
+11 −5
Original line number Diff line number Diff line
@@ -55,6 +55,8 @@ public class CFGBuilder extends VoidVisitorAdapter<Void> {
    protected final Map<SimpleName, List<GraphNode<ContinueStmt>>> continueMap = new HashMap<>();
    /** Return statements that should be connected to the final node, if it is created at the end of the */
    protected final List<GraphNode<ReturnStmt>> returnList = new LinkedList<>();
    /** Stack of switch statements. */
    protected final Deque<GraphNode<SwitchStmt>> switchStack = new LinkedList<>();
    /** Stack of lists of hanging cases on switch statements */
    protected final Deque<List<GraphNode<SwitchEntryStmt>>> switchEntriesStack = new LinkedList<>();

@@ -271,8 +273,10 @@ public class CFGBuilder extends VoidVisitorAdapter<Void> {
    @Override
    public void visit(SwitchEntryStmt entryStmt, Void arg) {
        // Case header (prev -> case EXPR)
        GraphNode<SwitchEntryStmt> node = connectTo(entryStmt, entryStmt.getLabel().isPresent() ?
                "case " + entryStmt.getLabel().get() : "default");
        GraphNode<SwitchEntryStmt> node = graph.addVertex(entryStmt.getLabel().isPresent() ?
                "case " + entryStmt.getLabel().get() : "default", entryStmt);
        graph.addControlFlowArc(switchStack.element(), node);
        hangingNodes.add(node);
        switchEntriesStack.peek().add(node);
        // Case body (case EXPR --> body)
        entryStmt.getStatements().accept(this, arg);
@@ -284,12 +288,13 @@ public class CFGBuilder extends VoidVisitorAdapter<Void> {
        // Link previous statement to the switch's selector
        switchEntriesStack.push(new LinkedList<>());
        breakStack.push(new LinkedList<>());
        GraphNode<?> cond = connectTo(switchStmt, String.format("switch (%s)", switchStmt.getSelector()));
        GraphNode<SwitchStmt> cond = connectTo(switchStmt, String.format("switch (%s)", switchStmt.getSelector()));
        hangingNodes.remove(cond);
        switchStack.push(cond);
        switchStmt.getSelector().accept(this, arg);
        // expr --> each case (fallthrough by default, so case --> case too)
        for (SwitchEntryStmt entry : switchStmt.getEntries()) {
            entry.accept(this, arg); // expr && prev case --> case --> next case
            hangingNodes.add(cond); // expr --> next case
        }
        // The next statement will be linked to:
        //		1. All break statements that broke from the switch (done with break section)
@@ -298,7 +303,8 @@ public class CFGBuilder extends VoidVisitorAdapter<Void> {
        // If the last case is a default case, remove the selector node from the list of nodes (see 2)
        if (ASTUtils.switchHasDefaultCase(switchStmt))
            hangingNodes.remove(cond);
        List<GraphNode<SwitchEntryStmt>> entries = switchEntriesStack.pop();
        switchEntriesStack.pop();
        switchStack.pop();
        // End block and break section
        hangingNodes.addAll(breakStack.pop());
    }