Commit 5dc18b6e authored by Carlos Galindo's avatar Carlos Galindo
Browse files

added polymorphic calls

* Call targets are computed based on static types (same as variable action's dynamic types).
* Usage and definition finders now deal with accumulating information in the same variable action ('-arg-in-' or '-scope-in-'). That variable action is created in the VariableVisitor, and the transference is prepared at the end of the finder, instead of with each handleActualAction.
* ObjectTree: pattern now accepts any variable, including fake patterns like ('-root-' or '-arg-in-')
* Tests updated
parent a9133823
Loading
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -167,7 +167,7 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E
            // =============== Method calls ===============
            @Override
            public void visit(MethodCallExpr n, Void arg) {
                n.resolve().toAst().ifPresent(decl -> createNormalEdge(decl, n));
                n.resolve().toAst().ifPresent(decl -> createPolyEdges(decl, n));
                super.visit(n, arg);
            }

+1 −2
Original line number Diff line number Diff line
package es.upv.mist.slicing.graphs;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
@@ -203,7 +202,7 @@ public class ExpressionObjectTreeFinder {
                for (VariableAction variableAction : graphNode.getVariableActions()) {
                    if (variableAction instanceof VariableAction.CallMarker) {
                        VariableAction.CallMarker marker = (VariableAction.CallMarker) variableAction;
                        if (ASTUtils.equalsWithRange((Node) marker.getCall(), (Node) call) && !marker.isEnter()) {
                        if (ASTUtils.equalsWithRange(marker.getCall(), call) && !marker.isEnter()) {
                            assert lastUseOut != null;
                            list.add(new Pair<>(lastUseOut, arg));
                            return;
+40 −9
Original line number Diff line number Diff line
@@ -44,23 +44,30 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder
            Optional<Expression> arg = extractArgument(def, edge, false);
            if (arg.isEmpty())
                return;
            ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), def.getName(), arg.get());
            ActualIONode actualOut = locateActualOutNode(edge, def.getName())
                    .orElseGet(() -> ActualIONode.createActualOut(edge.getCall(), def.getName(), arg.get()));
            extractOutputVariablesAsMovables(arg.get(), movables, graphNode, actualOut, def);
        } else if (def.isField()) {
            if (def.isStatic()) {
                // Known limitation: static fields
            } else {
                assert !(edge.getCall() instanceof ObjectCreationExpr);
                ActualIONode actualOut = ActualIONode.createActualOut(edge.getCall(), def.getName(), null);
                ActualIONode actualOut = locateActualOutNode(edge, def.getName())
                        .orElseGet(() -> ActualIONode.createActualOut(edge.getCall(), def.getName(), null));
                Optional<Expression> scope = ASTUtils.getResolvableScope(edge.getCall());
                if (scope.isPresent()) {
                    extractOutputVariablesAsMovables(scope.get(), movables, graphNode, actualOut, def);
                } else {
                    assert def.hasObjectTree();
                    Optional<VariableAction> optVA = locateDefinition(graphNode, "this");
                    if (optVA.isPresent())
                        optVA.get().getObjectTree().addAll(def.getObjectTree());
                    else {
                        var movableDef = new Definition(DeclarationType.FIELD, "this", graphNode, (ObjectTree) def.getObjectTree().clone());
                        movables.add(new Movable(movableDef, actualOut));
                    }
                }
            }
        } else {
            throw new IllegalStateException("Definition must be either from a parameter or a field!");
        }
@@ -75,6 +82,10 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder
        e.accept(new OutNodeVariableVisitor(), defExpressions);
        for (Expression expression : defExpressions) {
            assert def.hasObjectTree();
            Optional<VariableAction> optVa = locateDefinition(graphNode, expression.toString());
            if (optVa.isPresent()) {
                optVa.get().getObjectTree().addAll(def.getObjectTree());
            } else {
                DeclarationType type = DeclarationType.valueOf(expression);
                Definition inner = new Definition(type, expression.toString(), graphNode, (ObjectTree) def.getObjectTree().clone());
                if (defExpressions.size() > 1)
@@ -82,6 +93,26 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder
                movables.add(new Movable(inner, actualOut));
            }
        }
    }

    /** Find the actual out node in the given edge call that corresponds to the given variable name. */
    protected Optional<ActualIONode> locateActualOutNode(CallGraph.Edge<?> edge, String name) {
        return edge.getGraphNode().getSyntheticNodesInMovables().stream()
                .filter(ActualIONode.class::isInstance)
                .map(ActualIONode.class::cast)
                .filter(ActualIONode::isOutput)
                .filter(actual -> actual.getVariableName().equals(name))
                .filter(actual -> ASTUtils.equalsWithRange(actual.getAstNode(), edge.getCall()))
                .findFirst();
    }

    /** Try to locate the definition for the given variable name in the given node. */
    protected Optional<VariableAction> locateDefinition(GraphNode<?> graphNode, String name) {
        return graphNode.getVariableActions().stream()
                .filter(va -> va.getName().equals(name))
                .filter(VariableAction::isDefinition)
                .findAny();
    }

    @Override
    protected Stream<Definition> mapAndFilterActionStream(Stream<VariableAction> stream, CFG cfg) {
+69 −22
Original line number Diff line number Diff line
package es.upv.mist.slicing.graphs.sdg;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.CallableDeclaration;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.expr.ThisExpr;
import com.github.javaparser.resolution.Resolvable;
import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration;
import es.upv.mist.slicing.graphs.CallGraph;
import es.upv.mist.slicing.graphs.ExpressionObjectTreeFinder;
import es.upv.mist.slicing.graphs.cfg.CFG;
import es.upv.mist.slicing.nodes.GraphNode;
import es.upv.mist.slicing.nodes.ObjectTree;
import es.upv.mist.slicing.nodes.VariableAction;
import es.upv.mist.slicing.nodes.VariableAction.Definition;
import es.upv.mist.slicing.nodes.VariableAction.Movable;
import es.upv.mist.slicing.nodes.VariableAction.Usage;
import es.upv.mist.slicing.nodes.io.ActualIONode;
@@ -20,7 +19,9 @@ import es.upv.mist.slicing.utils.ASTUtils;

import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/** An interprocedural usage finder, which adds the associated actions to formal and actual nodes in the CFGs. */
@@ -29,6 +30,32 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder<Usag
        super(callGraph, cfgMap);
    }

    @Override
    public void save() {
        super.save();
        markTransferenceToRoot();
    }

    /** For every variable action -scope-in- or -arg-in- in the graph,
     *  runs {@link ExpressionObjectTreeFinder#locateAndMarkTransferenceToRoot(Expression, VariableAction)}. */
    protected void markTransferenceToRoot() {
        for (CallGraph.Edge<?> edge : graph.edgeSet()) {
            for (ActualIONode actualIn : locateActualInNode(edge)) {
                for (VariableAction va : edge.getGraphNode().getVariableActions()) {
                    if (va instanceof Movable && ((Movable) va).getRealNode().equals(actualIn)) {
                        ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(edge.getGraphNode());
                        if (va.getName().equals("-scope-in-")) {
                            Expression scope = Objects.requireNonNullElseGet(actualIn.getArgument(), ThisExpr::new);
                            finder.locateAndMarkTransferenceToRoot(scope, va);
                        } else if (va.getName().equals("-arg-in-")) {
                            finder.locateAndMarkTransferenceToRoot(actualIn.getArgument(), va);
                        }
                    }
                }
            }
        }
    }

    @Override
    protected void handleFormalAction(CallGraph.Vertex vertex, Usage use) {
        CFG cfg = cfgMap.get(vertex.getDeclaration());
@@ -43,12 +70,9 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder<Usag
        if (use.isParameter()) {
            if (!use.isPrimitive()) {
                assert use.hasObjectTree();
                ActualIONode actualIn = locateActualInNode(edge, use.getName());
                Definition def = new Definition(VariableAction.DeclarationType.SYNTHETIC, "-arg-in-", graphNode, (ObjectTree) use.getObjectTree().clone());
                Movable movDef = new Movable(def, actualIn);
                graphNode.addVariableActionAfterLastMatchingRealNode(movDef, actualIn);
                ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode);
                finder.locateAndMarkTransferenceToRoot(actualIn.getArgument(), def);
                int index = ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), use.getName());
                VariableAction argIn = locateArgIn(graphNode, edge.getCall(), index);
                argIn.getObjectTree().addAll(use.getObjectTree());
            }
        } else if (use.isField()) {
            if (use.isStatic()) {
@@ -56,29 +80,52 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder<Usag
            } else {
                // An object creation expression input an existing object via actual-in because it creates it.
                assert !(edge.getCall() instanceof ObjectCreationExpr);
                ActualIONode actualIn = locateActualInNode(edge, use.getName());
                Definition def = new Definition(VariableAction.DeclarationType.SYNTHETIC, "-scope-in-", graphNode, (ObjectTree) use.getObjectTree().clone());
                Movable movDef = new Movable(def, actualIn);
                Expression scope = Objects.requireNonNullElseGet(actualIn.getArgument(), ThisExpr::new);
                graphNode.addVariableActionAfterLastMatchingRealNode(movDef, actualIn);
                ExpressionObjectTreeFinder finder = new ExpressionObjectTreeFinder(graphNode);
                finder.locateAndMarkTransferenceToRoot(scope, def);
                VariableAction scopeIn = locateScopeIn(graphNode, edge.getCall());
                scopeIn.getObjectTree().addAll(use.getObjectTree());
            }
        } else {
            throw new IllegalStateException("Definition must be either from a parameter or a field!");
        }
    }

    /** Locates the actual-in node associated with the given variable name and call edge. */
    protected ActualIONode locateActualInNode(CallGraph.Edge<?> edge, String name) {
    /** Find all actual in nodes in the given call. */
    protected Set<ActualIONode> locateActualInNode(CallGraph.Edge<?> edge) {
        return edge.getGraphNode().getSyntheticNodesInMovables().stream()
                .filter(ActualIONode.class::isInstance)
                .map(ActualIONode.class::cast)
                .filter(ActualIONode::isInput)
                .filter(actual -> actual.getVariableName().equals(name))
                .filter(actual -> ASTUtils.equalsWithRange(actual.getAstNode(), (Node) edge.getCall()))
                .findFirst()
                .orElseThrow(() -> new IllegalStateException("can't locate actual-in node"));
                .filter(actual -> ASTUtils.equalsWithRange(actual.getAstNode(), edge.getCall()))
                .collect(Collectors.toSet());
    }

    /** Find the -arg-in- variable action that corresponds to the given node, call and index. */
    protected VariableAction locateArgIn(GraphNode<?> graphNode, Resolvable<? extends ResolvedMethodLikeDeclaration> call, int index) {
        return locateActionIn(graphNode, call, index, "-arg-in-");
    }

    /** Find the -scope-in- variable action that corresponds to the given node and call. */
    protected VariableAction locateScopeIn(GraphNode<?> graphNode, Resolvable<? extends ResolvedMethodLikeDeclaration> call) {
        return locateActionIn(graphNode, call, 0, "-scope-in-");
    }

    /** Find the nth variable action from the given node and call that matches the given name. 0 represents the first occurrence. */
    protected VariableAction locateActionIn(GraphNode<?> graphNode, Resolvable<? extends ResolvedMethodLikeDeclaration> call, int index, String actionName) {
        boolean inCall = false;
        for (VariableAction va : graphNode.getVariableActions()) {
            if (va instanceof VariableAction.CallMarker && ASTUtils.equalsWithRange(((VariableAction.CallMarker) va).getCall(), call)) {
                if (((VariableAction.CallMarker) va).isEnter())
                    inCall = true;
                else
                    break; // The call has ended, can't find the action now
            }
            if (inCall && va.isDefinition() && va.getName().equals(actionName)) {
                if (index == 0)
                    return va;
                else
                    index--;
            }
        }
        throw new IllegalStateException("Could not locate " + actionName + " for call " + call + " in node " + graphNode);
    }

    @Override
+1 −4
Original line number Diff line number Diff line
@@ -11,9 +11,6 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static es.upv.mist.slicing.graphs.cfg.CFGBuilder.VARIABLE_NAME_OUTPUT;
import static es.upv.mist.slicing.graphs.exceptionsensitive.ESCFG.ACTIVE_EXCEPTION_VARIABLE;

/**
 * A tree data structure that mimics the tree found in an object's fields.
 * Each tree contains a MemberNode that represents its, including a name.
@@ -29,7 +26,7 @@ public class ObjectTree implements Cloneable {
    public static final String ROOT_NAME = "-root-";

    /** Regex pattern to split the root from the fields of a field access expression. */
    private static final Pattern FIELD_SPLIT = Pattern.compile("^(?<root>(([_0-9A-Za-z]+\\.)*this)|([_0-9A-Za-z]+)|(" + ROOT_NAME + ")|(" + VARIABLE_NAME_OUTPUT + ")|(" + ACTIVE_EXCEPTION_VARIABLE + "))(\\.(?<fields>.+))?$");
    private static final Pattern FIELD_SPLIT = Pattern.compile("^(?<root>(([_0-9A-Za-z]+\\.)*this)|(?<dash>(-?))([_0-9A-Za-z]+\\k<dash>)+)(\\.(?<fields>.+))?$");

    /** Direct children of this tree node, mapped by field name. */
    private final Map<String, ObjectTree> childrenMap = new HashMap<>();
Loading