Commit 5617f6ff authored by Carlos Galindo's avatar Carlos Galindo
Browse files

Generate summary arcs between return nodes and actual-in.

parent 0984e234
Loading
Loading
Loading
Loading
Loading
+62 −21
Original line number Diff line number Diff line
@@ -4,6 +4,8 @@ import com.github.javaparser.ast.body.CallableDeclaration;
import es.upv.mist.slicing.graphs.BackwardDataFlowAnalysis;
import es.upv.mist.slicing.graphs.CallGraph;
import es.upv.mist.slicing.nodes.SyntheticNode;
import es.upv.mist.slicing.nodes.exceptionsensitive.ExitNode;
import es.upv.mist.slicing.nodes.exceptionsensitive.ReturnNode;
import es.upv.mist.slicing.nodes.io.ActualIONode;
import es.upv.mist.slicing.nodes.io.CallNode;
import es.upv.mist.slicing.nodes.io.FormalIONode;
@@ -11,6 +13,7 @@ import es.upv.mist.slicing.nodes.io.OutputNode;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis<CallGraph.Vertex, CallGraph.Edge<?>, Map<SyntheticNode<CallableDeclaration<?>>, Set<FormalIONode>>> {
    protected final SDG sdg;
@@ -28,8 +31,10 @@ public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis<CallGraph.Verte

    @Override
    protected Map<SyntheticNode<CallableDeclaration<?>>, Set<FormalIONode>> initialValue(CallGraph.Vertex vertex) {
        var value = vertexDataMap.get(vertex);
        if (value == null) {
        Map<SyntheticNode<CallableDeclaration<?>>, Set<FormalIONode>> value;
        if (vertexDataMap.containsKey(vertex)) {
            value = vertexDataMap.get(vertex);
        } else {
            value = new HashMap<>();
            for (var formalOut : getFormalOutNodes(vertex.getDeclaration()))
                value.put(formalOut, new HashSet<>());
@@ -38,20 +43,31 @@ public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis<CallGraph.Verte
        return value;
    }

    /** Obtain all nodes that represent the output of a method declaration. These include formal-out,
     *  return nodes and normal/exception exit nodes (for exception handling). */
    protected Set<SyntheticNode<CallableDeclaration<?>>> getFormalOutNodes(CallableDeclaration<?> declaration) {
        Set<SyntheticNode<CallableDeclaration<?>>> set = sdg.vertexSet().stream()
        Set<SyntheticNode<CallableDeclaration<?>>> set = new HashSet<>();
        Stream.concat(
                Stream.concat(
                        sdg.vertexSet().stream() // formal-out nodes
                                .filter(FormalIONode.class::isInstance)
                                .map(FormalIONode.class::cast)
                .filter(FormalIONode::isOutput)
                .collect(Collectors.toSet());
        sdg.vertexSet().stream()
                                .filter(FormalIONode::isOutput),
                        sdg.vertexSet().stream() // output nodes (the value returned)
                                .filter(OutputNode.class::isInstance)
                .map(OutputNode.class::cast)
                .filter(on -> on.getAstNode().equals(declaration))
                                .map(OutputNode.class::cast)),
                sdg.vertexSet().stream() // normal/exception exit nodes (for exception handling)
                        .filter(ExitNode.class::isInstance)
                        .map(ExitNode.class::cast))
                // Only nodes that match the current declaration
                .filter(node -> node.getAstNode() == declaration)
                .forEach(set::add);
        return set;
    }

    /** Given an output or formal-out node, locate the formal-in nodes it depends on.
     *  This search should be performed intra-procedurally, the parent class will take
     *  care of the rest of cases by adding summary arcs computed for other declarations. */
    protected Set<FormalIONode> computeFormalIn(SyntheticNode<CallableDeclaration<?>> formalOut) {
        return sdg.createSlicingAlgorithm().traverseProcedure(formalOut).getGraphNodes().stream()
                .filter(FormalIONode.class::isInstance)
@@ -60,15 +76,17 @@ public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis<CallGraph.Verte
                .collect(Collectors.toSet());
    }

    /** Generate all summary arcs for a given call. Arc generation should be idempotent:
     *  if this method is called repeatedly it should not create duplicate summary arcs. */
    protected void saveDeclaration(CallGraph.Vertex vertex) {
        var result = vertexDataMap.get(vertex);
        for (CallGraph.Edge<?> edge : graph.incomingEdgesOf(vertex)) {
            for (var entry : result.entrySet()) {
                var actualOutOpt = getActualOut(edge, entry.getKey());
                var actualOutOpt = findOutputNode(edge, entry.getKey());
                if (actualOutOpt.isEmpty())
                    continue;
                for (var formalIn : entry.getValue()) {
                    var actualInOpt = getActualIn(edge, formalIn);
                    var actualInOpt = findActualIn(edge, formalIn);
                    if (actualInOpt.isEmpty())
                        continue;
                    if (!sdg.containsEdge(actualInOpt.get(), actualOutOpt.get()))
@@ -78,7 +96,10 @@ public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis<CallGraph.Verte
        }
    }

    protected Optional<ActualIONode> getActualIn(CallGraph.Edge<?> edge, FormalIONode formalIn) {
    /** Find the actual-in that represents the given formal-in in the given call.
     *  There may not be one. In that case, the dependency between formal-in/out should
     *  not result in a summary arc. */
    protected Optional<ActualIONode> findActualIn(CallGraph.Edge<?> edge, FormalIONode formalIn) {
        return sdg.vertexSet().stream()
                .filter(ActualIONode.class::isInstance)
                .map(ActualIONode.class::cast)
@@ -87,28 +108,48 @@ public class SummaryArcAnalyzer extends BackwardDataFlowAnalysis<CallGraph.Verte
                .findAny();
    }

    protected Optional<SyntheticNode<?>> getActualOut(CallGraph.Edge<?> edge, SyntheticNode<CallableDeclaration<?>> formalOut) {
    /** Find the actual-out, return or exception/normal return node that represents the given
     *  formal-out, output or exception/normal exit node in the given call. There may not be one.
     *  In that case, the dependency between formal-in/out should not result in a summary arc. */
    protected Optional<? extends SyntheticNode<?>> findOutputNode(CallGraph.Edge<?> edge, SyntheticNode<CallableDeclaration<?>> formalOut) {
        if (formalOut instanceof FormalIONode)
            return Optional.ofNullable(getActualOut(edge, (FormalIONode) formalOut));
            return findActualOut(edge, (FormalIONode) formalOut);
        if (formalOut instanceof OutputNode)
            return Optional.ofNullable(getActualOut(edge));
            return Optional.of(findReturnNode(edge));
        if (formalOut instanceof ExitNode)
            return getReturnNode(edge, (ExitNode) formalOut);
        throw new IllegalArgumentException("invalid type");
    }

    protected ActualIONode getActualOut(CallGraph.Edge<?> edge, FormalIONode formalOut) {
    /** Find the actual-out node that corresponds to the given formal-out in the given call.
     *  To locate any actual-out, you should use {@link #findOutputNode(CallGraph.Edge, SyntheticNode)}. */
    protected Optional<ActualIONode> findActualOut(CallGraph.Edge<?> edge, FormalIONode formalOut) {
        return sdg.vertexSet().stream()
                .filter(ActualIONode.class::isInstance)
                .map(ActualIONode.class::cast)
                .filter(n -> n.getAstNode() == edge.getCall())
                .filter(n -> n.matchesFormalIO(formalOut))
                .findAny().orElse(null);
                .findAny();
    }

    protected CallNode.Return getActualOut(CallGraph.Edge<?> edge) {
    /** Find the return node of the given call. There is only one per method.
     *  To locate any actual-out, you should use {@link #findOutputNode(CallGraph.Edge, SyntheticNode)}. */
    protected CallNode.Return findReturnNode(CallGraph.Edge<?> edge) {
        return sdg.vertexSet().stream()
                .filter(CallNode.Return.class::isInstance)
                .map(CallNode.Return.class::cast)
                .filter(n -> n.getAstNode() == edge.getCall())
                .findAny().orElse(null);
                .findAny().orElseThrow();
    }

    /** Find the exception/normal return node that corresponds to the given exception/normal exit in the given call.
     *  To locate any actual-out, you should use {@link #findOutputNode(CallGraph.Edge, SyntheticNode)}. */
    protected Optional<ReturnNode> getReturnNode(CallGraph.Edge<?> edge, ExitNode exitNode) {
        return sdg.vertexSet().stream()
                .filter(ReturnNode.class::isInstance)
                .map(ReturnNode.class::cast)
                .filter(n -> n.getAstNode() == edge.getCall())
                .filter(exitNode::matchesReturnNode)
                .findAny();
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -28,4 +28,10 @@ public class ExceptionExitNode extends ExitNode {
    public int hashCode() {
        return Objects.hash(super.hashCode(), exceptionType);
    }

    @Override
    public boolean matchesReturnNode(ReturnNode node) {
        // TODO: this is a temporary solution. When 1 exception return node per type is implemented, they must be compared
        return node instanceof ExceptionReturnNode;
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -6,8 +6,10 @@ import es.upv.mist.slicing.nodes.SyntheticNode;
import java.util.LinkedList;

/** A node that summarizes the normal or exceptional exits of a declaration. */
public abstract class ExitNode extends SyntheticNode<CallableDeclaration> {
public abstract class ExitNode extends SyntheticNode<CallableDeclaration<?>> {
    protected ExitNode(String label, CallableDeclaration<?> astNode) {
        super(label, astNode, new LinkedList<>());
    }

    public abstract boolean matchesReturnNode(ReturnNode node);
}
+5 −0
Original line number Diff line number Diff line
@@ -7,4 +7,9 @@ public class NormalExitNode extends ExitNode {
    public NormalExitNode(CallableDeclaration<?> astNode) {
        super("normal exit", astNode);
    }

    @Override
    public boolean matchesReturnNode(ReturnNode node) {
        return node instanceof NormalReturnNode;
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ import es.upv.mist.slicing.nodes.SyntheticNode;
import java.util.LinkedList;

/** A node representing the returned value from a declaration. */
public class MethodExitNode extends SyntheticNode<CallableDeclaration> {
public class MethodExitNode extends SyntheticNode<CallableDeclaration<?>> {
    public MethodExitNode(CallableDeclaration<?> astNode) {
        super("Exit", astNode, new LinkedList<>());
    }
Loading