Loading sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java +38 −0 Original line number Diff line number Diff line package es.upv.mist.slicing.graphs; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.expr.Expression; Loading Loading @@ -59,6 +60,43 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E .map(decl -> (CallableDeclaration<?>) decl); } /** Locates the calls to a given declaration. The result is any node that represents a call. */ public Stream<Node> callsTo(CallableDeclaration<?> callee) { return incomingEdgesOf(findVertexByDeclaration(callee)).stream() .map(Edge::getCall) .map(Node.class::cast); } /** Locates the calls contained in a given method or constructor. * See {@link #callsTo(CallableDeclaration)} for the return value. */ public Stream<Node> callsFrom(CallableDeclaration<?> caller) { return outgoingEdgesOf(findVertexByDeclaration(caller)).stream() .map(Edge::getCall) .map(Node.class::cast); } /** Locates the methods that may call the given declaration. */ public Stream<CallableDeclaration<?>> callersOf(CallableDeclaration<?> callee) { return incomingEdgesOf(findVertexByDeclaration(callee)).stream() .map(this::getEdgeSource) .map(Vertex::getDeclaration); } /** Locates the methods that may be called when executing the given declaration. */ public Stream<CallableDeclaration<?>> calleesOf(CallableDeclaration<?> caller) { return outgoingEdgesOf(findVertexByDeclaration(caller)).stream() .map(this::getEdgeTarget) .map(Vertex::getDeclaration); } /** Locate the vertex that represents in this graph the given declaration. */ protected Vertex findVertexByDeclaration(CallableDeclaration<?> declaration) { return vertexSet().stream() .filter(v -> v.declaration == declaration || ASTUtils.equalsWithRange(v.declaration, declaration)) .findFirst().orElseThrow(); } @Override public void build(NodeList<CompilationUnit> arg) { if (isBuilt()) Loading sdg-core/src/main/java/es/upv/mist/slicing/graphs/oo/DynamicTypeResolver.java +133 −41 Original line number Diff line number Diff line package es.upv.mist.slicing.graphs.oo; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.expr.CastExpr; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.stmt.ReturnStmt; import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration; import com.github.javaparser.resolution.types.ResolvedType; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.VariableAction; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; import es.upv.mist.slicing.utils.ASTUtils; import java.util.HashSet; import java.util.Objects; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; /** A dynamic type solver that complements Javaparser's {@code resolve()} method. */ /** A dynamic type solver that complements JavaParser's {@code resolve()} method. */ public class DynamicTypeResolver { protected final CFG cfg; protected final Map<CallableDeclaration<?>, CFG> cfgMap; protected final ClassGraph classGraph; protected final CallGraph callGraph; public DynamicTypeResolver(CFG cfg, ClassGraph classGraph) { this.cfg = cfg; public DynamicTypeResolver(Map<CallableDeclaration<?>, CFG> cfgMap, ClassGraph classGraph, CallGraph callGraph) { this.cfgMap = cfgMap; this.classGraph = classGraph; this.callGraph = callGraph; } /** Obtains the set of dynamic types that the variable passed as argument * may take. <strong>The current implementation is intra-procedural!</strong> */ public Set<ResolvedType> resolve(VariableAction action) { return resolveIntraProcedural(action); /** Obtains the possible dynamic types of the given expression, which is contained within a GraphNode. * Only expressions of a reference type are allowed (e.g. objects, arrays, but not primitives). */ public Set<ResolvedType> resolve(Expression expression, GraphNode<?> container) { assert expression.calculateResolvedType().isReference(): "The expression must be of reference type (no primitives)."; return resolveStreamed(expression, container).collect(Collectors.toSet()); } /** Obtains a list of possible dynamic types for the given action, by * searching for definitions intraprocedurally. */ public Set<ResolvedType> resolveIntraProcedural(VariableAction action) { return cfg.findLastDefinitionsFrom(action).stream() .map(VariableAction::asDefinition) .filter(Objects::nonNull) .map(def -> { Expression expr = unwrapExpr(def.getExpression()); if (expr.isObjectCreationExpr() || expr.isArrayCreationExpr() || expr.isArrayInitializerExpr()) return Set.of(expr.calculateResolvedType()); else return findAllSubtypes(def.getExpression().calculateResolvedType()); }) .reduce(new HashSet<>(), (a, b) -> { a.addAll(b); return a; }); /** Directs each kind of expression to the appropriate resolve method. */ protected Stream<ResolvedType> resolveStreamed(Expression expression, GraphNode<?> container) { if (expression.isMethodCallExpr()) return resolveMethodCallExpr(expression.asMethodCallExpr()); if (expression.isNameExpr() || expression.isFieldAccessExpr()) // May be field, local variable or parameter return resolveVariable(expression, container); if (expression.isArrayAccessExpr() || expression.isThisExpr()) return anyTypeOf(expression); if (expression.isCastExpr()) return resolveCast(expression.asCastExpr(), container); if (expression.isEnclosedExpr()) return resolveStreamed(expression.asEnclosedExpr().getInner(), container); if (expression.isObjectCreationExpr() || expression.isArrayCreationExpr()) return Stream.of(expression.calculateResolvedType()); throw new IllegalArgumentException("The given expression is not an object-compatible one."); } /** Unwraps an enclosed expression, and other constructs that are type-transparent. */ protected Expression unwrapExpr(Expression expr) { if (expr.isCastExpr()) if (ASTUtils.isDownCast(expr.asCastExpr())) return expr; else return unwrapExpr(expr.asCastExpr().getExpression()); if (expr.isEnclosedExpr()) return unwrapExpr(expr.asEnclosedExpr().getInner()); return expr; /** Checks the possible values of all ReturnStmt of this call's target methods. */ protected Stream<ResolvedType> resolveMethodCallExpr(MethodCallExpr methodCallExpr) { assert !methodCallExpr.calculateResolvedType().isVoid(); return callGraph.getCallTargets(methodCallExpr) .map(cfgMap::get) .flatMap(cfg -> cfg.vertexSet().stream()) .filter(node -> node.getAstNode() instanceof ReturnStmt) .flatMap(node -> resolveStreamed(((ReturnStmt) node.getAstNode()).getExpression().orElseThrow(), node)); } /** Extracts from the call graph all the subtypes of the given argument. * The input is included as part of the output. */ protected Set<ResolvedType> findAllSubtypes(ResolvedType t) { return classGraph.subclassesOf(t.asReferenceType().getTypeDeclaration().orElseThrow().asClass()).stream() /** Searches for the corresponding VariableAction object, then calls {@link #resolveVariableAction(VariableAction)}. */ protected Stream<ResolvedType> resolveVariable(Expression expression, GraphNode<?> graphNode) { Optional<VariableAction> va = graphNode.getVariableActions().stream() .filter(action -> ASTUtils.equalsWithRange(action.getVariableExpression(), expression)) .findFirst(); if (va.isEmpty()) return anyTypeOf(expression); return resolveVariableAction(va.get()); } /** * Looks up the last definitions according to the CFG, then resolves the last assignment to it. * If the last definition was not in this method (in the case of parameters or fields), it * uses auxiliary methods to locate the last interprocedural definition. * Otherwise, the last expression(s) assigned to it is found and recursively resolved. */ protected Stream<ResolvedType> resolveVariableAction(VariableAction va) { CFG cfg = cfgMap.get(findCallableDeclarationFromGraphNode(va.getGraphNode())); return cfg.findLastDefinitionsFrom(va).stream() .flatMap(def -> { if (def.asDefinition().getExpression() == null) { if (def instanceof VariableAction.Movable && ((VariableAction.Movable) def).getRealNode() instanceof FormalIONode) return resolveFormalIn((FormalIONode) ((VariableAction.Movable) def).getRealNode()); throw new IllegalArgumentException("Definition was not movable and hadn't an expression."); } return resolveStreamed(def.asDefinition().getExpression(), def.getGraphNode()); }); } /** Locate the declaration (method or constructor) where the given node is located. */ protected CallableDeclaration<?> findCallableDeclarationFromGraphNode(GraphNode<?> node) { return cfgMap.values().stream() .filter(cfg -> cfg.containsVertex(node)) .map(CFG::getDeclaration) .findFirst().orElseThrow(); } /** Looks up the expression assigned to all corresponding actual-in nodes and resolves it. */ protected Stream<ResolvedType> resolveFormalIn(FormalIONode formalIn) { assert formalIn.isInput(); return callGraph.callsTo(findCallableDeclarationFromGraphNode(formalIn)) .map(this::findNodeInMapByAST) .map(GraphNode::getVariableActions) .flatMap(List::stream) .filter(VariableAction.Movable.class::isInstance) .map(VariableAction.Movable.class::cast) .flatMap(movable -> { GraphNode<?> realNode = movable.getRealNode(); if (!(realNode instanceof ActualIONode)) return Stream.empty(); ActualIONode actualIn = (ActualIONode) realNode; if (!actualIn.matchesFormalIO(formalIn)) return Stream.empty(); return resolveStreamed(actualIn.getArgument(), movable.getGraphNode()); }); } /** Locate the CFG graph node in which the argument is contained. * Its time requirement is linear with the number of total nodes in */ protected GraphNode<?> findNodeInMapByAST(Node astNode) { return cfgMap.values().stream() .map(cfg -> cfg.findNodeByASTNode(astNode)) .filter(Optional::isPresent) .map(Optional::get) .findFirst().orElseThrow(); } /** Checks if the cast marks a more specific type and returns that one. * Otherwise, it unwraps the cast expression and recursively resolves the type * of the inner expression. */ protected Stream<ResolvedType> resolveCast(CastExpr cast, GraphNode<?> container) { if (ASTUtils.isDownCast(cast)) return Stream.of(cast.getType().resolve()); return resolveStreamed(cast.getExpression(), container); } /** Returns all possible types that the given expression can be, by obtaining its static type * and locating all subtypes in the class graph. */ protected Stream<ResolvedType> anyTypeOf(Expression expression) { ResolvedClassDeclaration type = expression.calculateResolvedType().asReferenceType() .getTypeDeclaration().orElseThrow().asClass(); return classGraph.subclassesOf(type).stream() .map(ClassOrInterfaceDeclaration::resolve) .map(ASTUtils::resolvedTypeDeclarationToResolvedType) .collect(Collectors.toSet()); .map(ASTUtils::resolvedTypeDeclarationToResolvedType); } } sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java +4 −4 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte /** Obtains the expression passed as argument for the given action at the given call. If {@code input} * is false, primitive parameters will be skipped, as their value cannot be redefined.*/ protected Expression extractArgument(VariableAction action, CallGraph.Edge<?> edge, boolean input) { ResolvedValueDeclaration resolved = action.getNameExpr().resolve(); ResolvedValueDeclaration resolved = action.getResolvedValueDeclaration(); CallableDeclaration<?> callTarget = graph.getEdgeTarget(edge).getDeclaration(); if (resolved.isParameter()) { ResolvedParameterDeclaration p = resolved.asParameter(); Loading @@ -83,7 +83,7 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte int paramIndex = ASTUtils.getMatchingParameterIndex(callTarget, p); return ASTUtils.getResolvableArgs(edge.getCall()).get(paramIndex); } else if (resolved.isField()) { return action.getNameExpr(); return action.getVariableExpression(); } else { throw new IllegalArgumentException("Variable should be either param or field!"); } Loading Loading @@ -116,8 +116,8 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte ResolvedValueDeclaration r1 = null; ResolvedValueDeclaration r2 = null; try { r1 = o1.getAction().getNameExpr().resolve(); r2 = o2.getAction().getNameExpr().resolve(); r1 = o1.getAction().getResolvedValueDeclaration(); r2 = o2.getAction().getResolvedValueDeclaration(); if (r1.isParameter() && r2.isParameter()) return -Integer.compare(ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), r1.asParameter()), ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), r2.asParameter())); Loading sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralDefinitionFinder.java +2 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder @Override protected void handleFormalAction(CallableDeclaration<?> declaration, VariableAction.Definition def) { CFG cfg = cfgMap.get(declaration); ResolvedValueDeclaration resolved = def.getNameExpr().resolve(); ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); if (!resolved.isParameter() || !resolved.getType().isPrimitive()) { FormalIONode formalOut = FormalIONode.createFormalOut(declaration, resolved); cfg.getExitNode().addMovableVariable(new VariableAction.Movable(def.toUsage(cfg.getExitNode()), formalOut)); Loading @@ -40,7 +40,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder protected void handleActualAction(CallGraph.Edge<?> edge, VariableAction.Definition def) { Set<VariableAction.Movable> movables = new HashSet<>(); GraphNode<?> graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = def.getNameExpr().resolve(); ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); Expression arg = extractArgument(def, edge, false); if (arg == null) return; Loading sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java +2 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder<Vari @Override protected void handleFormalAction(CallableDeclaration<?> declaration, VariableAction.Usage use) { CFG cfg = cfgMap.get(declaration); ResolvedValueDeclaration resolved = use.getNameExpr().resolve(); ResolvedValueDeclaration resolved = use.getResolvedValueDeclaration(); FormalIONode formalIn = FormalIONode.createFormalIn(declaration, resolved); cfg.getRootNode().addMovableVariable(new VariableAction.Movable(use.toDefinition(cfg.getRootNode()), formalIn)); } Loading @@ -36,7 +36,7 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder<Vari protected void handleActualAction(CallGraph.Edge<?> edge, VariableAction.Usage use) { Set<VariableAction.Movable> movables = new HashSet<>(); GraphNode<?> graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = use.getNameExpr().resolve(); ResolvedValueDeclaration resolved = use.getResolvedValueDeclaration(); Expression argument = extractArgument(use, edge, true); ActualIONode actualIn = ActualIONode.createActualIn(edge.getCall(), resolved, argument); argument.accept(new VariableVisitor( Loading Loading
sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java +38 −0 Original line number Diff line number Diff line package es.upv.mist.slicing.graphs; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.expr.Expression; Loading Loading @@ -59,6 +60,43 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E .map(decl -> (CallableDeclaration<?>) decl); } /** Locates the calls to a given declaration. The result is any node that represents a call. */ public Stream<Node> callsTo(CallableDeclaration<?> callee) { return incomingEdgesOf(findVertexByDeclaration(callee)).stream() .map(Edge::getCall) .map(Node.class::cast); } /** Locates the calls contained in a given method or constructor. * See {@link #callsTo(CallableDeclaration)} for the return value. */ public Stream<Node> callsFrom(CallableDeclaration<?> caller) { return outgoingEdgesOf(findVertexByDeclaration(caller)).stream() .map(Edge::getCall) .map(Node.class::cast); } /** Locates the methods that may call the given declaration. */ public Stream<CallableDeclaration<?>> callersOf(CallableDeclaration<?> callee) { return incomingEdgesOf(findVertexByDeclaration(callee)).stream() .map(this::getEdgeSource) .map(Vertex::getDeclaration); } /** Locates the methods that may be called when executing the given declaration. */ public Stream<CallableDeclaration<?>> calleesOf(CallableDeclaration<?> caller) { return outgoingEdgesOf(findVertexByDeclaration(caller)).stream() .map(this::getEdgeTarget) .map(Vertex::getDeclaration); } /** Locate the vertex that represents in this graph the given declaration. */ protected Vertex findVertexByDeclaration(CallableDeclaration<?> declaration) { return vertexSet().stream() .filter(v -> v.declaration == declaration || ASTUtils.equalsWithRange(v.declaration, declaration)) .findFirst().orElseThrow(); } @Override public void build(NodeList<CompilationUnit> arg) { if (isBuilt()) Loading
sdg-core/src/main/java/es/upv/mist/slicing/graphs/oo/DynamicTypeResolver.java +133 −41 Original line number Diff line number Diff line package es.upv.mist.slicing.graphs.oo; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.expr.CastExpr; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.stmt.ReturnStmt; import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration; import com.github.javaparser.resolution.types.ResolvedType; import es.upv.mist.slicing.graphs.CallGraph; import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.nodes.VariableAction; import es.upv.mist.slicing.nodes.io.ActualIONode; import es.upv.mist.slicing.nodes.io.FormalIONode; import es.upv.mist.slicing.utils.ASTUtils; import java.util.HashSet; import java.util.Objects; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; /** A dynamic type solver that complements Javaparser's {@code resolve()} method. */ /** A dynamic type solver that complements JavaParser's {@code resolve()} method. */ public class DynamicTypeResolver { protected final CFG cfg; protected final Map<CallableDeclaration<?>, CFG> cfgMap; protected final ClassGraph classGraph; protected final CallGraph callGraph; public DynamicTypeResolver(CFG cfg, ClassGraph classGraph) { this.cfg = cfg; public DynamicTypeResolver(Map<CallableDeclaration<?>, CFG> cfgMap, ClassGraph classGraph, CallGraph callGraph) { this.cfgMap = cfgMap; this.classGraph = classGraph; this.callGraph = callGraph; } /** Obtains the set of dynamic types that the variable passed as argument * may take. <strong>The current implementation is intra-procedural!</strong> */ public Set<ResolvedType> resolve(VariableAction action) { return resolveIntraProcedural(action); /** Obtains the possible dynamic types of the given expression, which is contained within a GraphNode. * Only expressions of a reference type are allowed (e.g. objects, arrays, but not primitives). */ public Set<ResolvedType> resolve(Expression expression, GraphNode<?> container) { assert expression.calculateResolvedType().isReference(): "The expression must be of reference type (no primitives)."; return resolveStreamed(expression, container).collect(Collectors.toSet()); } /** Obtains a list of possible dynamic types for the given action, by * searching for definitions intraprocedurally. */ public Set<ResolvedType> resolveIntraProcedural(VariableAction action) { return cfg.findLastDefinitionsFrom(action).stream() .map(VariableAction::asDefinition) .filter(Objects::nonNull) .map(def -> { Expression expr = unwrapExpr(def.getExpression()); if (expr.isObjectCreationExpr() || expr.isArrayCreationExpr() || expr.isArrayInitializerExpr()) return Set.of(expr.calculateResolvedType()); else return findAllSubtypes(def.getExpression().calculateResolvedType()); }) .reduce(new HashSet<>(), (a, b) -> { a.addAll(b); return a; }); /** Directs each kind of expression to the appropriate resolve method. */ protected Stream<ResolvedType> resolveStreamed(Expression expression, GraphNode<?> container) { if (expression.isMethodCallExpr()) return resolveMethodCallExpr(expression.asMethodCallExpr()); if (expression.isNameExpr() || expression.isFieldAccessExpr()) // May be field, local variable or parameter return resolveVariable(expression, container); if (expression.isArrayAccessExpr() || expression.isThisExpr()) return anyTypeOf(expression); if (expression.isCastExpr()) return resolveCast(expression.asCastExpr(), container); if (expression.isEnclosedExpr()) return resolveStreamed(expression.asEnclosedExpr().getInner(), container); if (expression.isObjectCreationExpr() || expression.isArrayCreationExpr()) return Stream.of(expression.calculateResolvedType()); throw new IllegalArgumentException("The given expression is not an object-compatible one."); } /** Unwraps an enclosed expression, and other constructs that are type-transparent. */ protected Expression unwrapExpr(Expression expr) { if (expr.isCastExpr()) if (ASTUtils.isDownCast(expr.asCastExpr())) return expr; else return unwrapExpr(expr.asCastExpr().getExpression()); if (expr.isEnclosedExpr()) return unwrapExpr(expr.asEnclosedExpr().getInner()); return expr; /** Checks the possible values of all ReturnStmt of this call's target methods. */ protected Stream<ResolvedType> resolveMethodCallExpr(MethodCallExpr methodCallExpr) { assert !methodCallExpr.calculateResolvedType().isVoid(); return callGraph.getCallTargets(methodCallExpr) .map(cfgMap::get) .flatMap(cfg -> cfg.vertexSet().stream()) .filter(node -> node.getAstNode() instanceof ReturnStmt) .flatMap(node -> resolveStreamed(((ReturnStmt) node.getAstNode()).getExpression().orElseThrow(), node)); } /** Extracts from the call graph all the subtypes of the given argument. * The input is included as part of the output. */ protected Set<ResolvedType> findAllSubtypes(ResolvedType t) { return classGraph.subclassesOf(t.asReferenceType().getTypeDeclaration().orElseThrow().asClass()).stream() /** Searches for the corresponding VariableAction object, then calls {@link #resolveVariableAction(VariableAction)}. */ protected Stream<ResolvedType> resolveVariable(Expression expression, GraphNode<?> graphNode) { Optional<VariableAction> va = graphNode.getVariableActions().stream() .filter(action -> ASTUtils.equalsWithRange(action.getVariableExpression(), expression)) .findFirst(); if (va.isEmpty()) return anyTypeOf(expression); return resolveVariableAction(va.get()); } /** * Looks up the last definitions according to the CFG, then resolves the last assignment to it. * If the last definition was not in this method (in the case of parameters or fields), it * uses auxiliary methods to locate the last interprocedural definition. * Otherwise, the last expression(s) assigned to it is found and recursively resolved. */ protected Stream<ResolvedType> resolveVariableAction(VariableAction va) { CFG cfg = cfgMap.get(findCallableDeclarationFromGraphNode(va.getGraphNode())); return cfg.findLastDefinitionsFrom(va).stream() .flatMap(def -> { if (def.asDefinition().getExpression() == null) { if (def instanceof VariableAction.Movable && ((VariableAction.Movable) def).getRealNode() instanceof FormalIONode) return resolveFormalIn((FormalIONode) ((VariableAction.Movable) def).getRealNode()); throw new IllegalArgumentException("Definition was not movable and hadn't an expression."); } return resolveStreamed(def.asDefinition().getExpression(), def.getGraphNode()); }); } /** Locate the declaration (method or constructor) where the given node is located. */ protected CallableDeclaration<?> findCallableDeclarationFromGraphNode(GraphNode<?> node) { return cfgMap.values().stream() .filter(cfg -> cfg.containsVertex(node)) .map(CFG::getDeclaration) .findFirst().orElseThrow(); } /** Looks up the expression assigned to all corresponding actual-in nodes and resolves it. */ protected Stream<ResolvedType> resolveFormalIn(FormalIONode formalIn) { assert formalIn.isInput(); return callGraph.callsTo(findCallableDeclarationFromGraphNode(formalIn)) .map(this::findNodeInMapByAST) .map(GraphNode::getVariableActions) .flatMap(List::stream) .filter(VariableAction.Movable.class::isInstance) .map(VariableAction.Movable.class::cast) .flatMap(movable -> { GraphNode<?> realNode = movable.getRealNode(); if (!(realNode instanceof ActualIONode)) return Stream.empty(); ActualIONode actualIn = (ActualIONode) realNode; if (!actualIn.matchesFormalIO(formalIn)) return Stream.empty(); return resolveStreamed(actualIn.getArgument(), movable.getGraphNode()); }); } /** Locate the CFG graph node in which the argument is contained. * Its time requirement is linear with the number of total nodes in */ protected GraphNode<?> findNodeInMapByAST(Node astNode) { return cfgMap.values().stream() .map(cfg -> cfg.findNodeByASTNode(astNode)) .filter(Optional::isPresent) .map(Optional::get) .findFirst().orElseThrow(); } /** Checks if the cast marks a more specific type and returns that one. * Otherwise, it unwraps the cast expression and recursively resolves the type * of the inner expression. */ protected Stream<ResolvedType> resolveCast(CastExpr cast, GraphNode<?> container) { if (ASTUtils.isDownCast(cast)) return Stream.of(cast.getType().resolve()); return resolveStreamed(cast.getExpression(), container); } /** Returns all possible types that the given expression can be, by obtaining its static type * and locating all subtypes in the class graph. */ protected Stream<ResolvedType> anyTypeOf(Expression expression) { ResolvedClassDeclaration type = expression.calculateResolvedType().asReferenceType() .getTypeDeclaration().orElseThrow().asClass(); return classGraph.subclassesOf(type).stream() .map(ClassOrInterfaceDeclaration::resolve) .map(ASTUtils::resolvedTypeDeclarationToResolvedType) .collect(Collectors.toSet()); .map(ASTUtils::resolvedTypeDeclarationToResolvedType); } }
sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralActionFinder.java +4 −4 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte /** Obtains the expression passed as argument for the given action at the given call. If {@code input} * is false, primitive parameters will be skipped, as their value cannot be redefined.*/ protected Expression extractArgument(VariableAction action, CallGraph.Edge<?> edge, boolean input) { ResolvedValueDeclaration resolved = action.getNameExpr().resolve(); ResolvedValueDeclaration resolved = action.getResolvedValueDeclaration(); CallableDeclaration<?> callTarget = graph.getEdgeTarget(edge).getDeclaration(); if (resolved.isParameter()) { ResolvedParameterDeclaration p = resolved.asParameter(); Loading @@ -83,7 +83,7 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte int paramIndex = ASTUtils.getMatchingParameterIndex(callTarget, p); return ASTUtils.getResolvableArgs(edge.getCall()).get(paramIndex); } else if (resolved.isField()) { return action.getNameExpr(); return action.getVariableExpression(); } else { throw new IllegalArgumentException("Variable should be either param or field!"); } Loading Loading @@ -116,8 +116,8 @@ public abstract class InterproceduralActionFinder<A extends VariableAction> exte ResolvedValueDeclaration r1 = null; ResolvedValueDeclaration r2 = null; try { r1 = o1.getAction().getNameExpr().resolve(); r2 = o2.getAction().getNameExpr().resolve(); r1 = o1.getAction().getResolvedValueDeclaration(); r2 = o2.getAction().getResolvedValueDeclaration(); if (r1.isParameter() && r2.isParameter()) return -Integer.compare(ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), r1.asParameter()), ASTUtils.getMatchingParameterIndex(graph.getEdgeTarget(edge).getDeclaration(), r2.asParameter())); Loading
sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralDefinitionFinder.java +2 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder @Override protected void handleFormalAction(CallableDeclaration<?> declaration, VariableAction.Definition def) { CFG cfg = cfgMap.get(declaration); ResolvedValueDeclaration resolved = def.getNameExpr().resolve(); ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); if (!resolved.isParameter() || !resolved.getType().isPrimitive()) { FormalIONode formalOut = FormalIONode.createFormalOut(declaration, resolved); cfg.getExitNode().addMovableVariable(new VariableAction.Movable(def.toUsage(cfg.getExitNode()), formalOut)); Loading @@ -40,7 +40,7 @@ public class InterproceduralDefinitionFinder extends InterproceduralActionFinder protected void handleActualAction(CallGraph.Edge<?> edge, VariableAction.Definition def) { Set<VariableAction.Movable> movables = new HashSet<>(); GraphNode<?> graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = def.getNameExpr().resolve(); ResolvedValueDeclaration resolved = def.getResolvedValueDeclaration(); Expression arg = extractArgument(def, edge, false); if (arg == null) return; Loading
sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/InterproceduralUsageFinder.java +2 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder<Vari @Override protected void handleFormalAction(CallableDeclaration<?> declaration, VariableAction.Usage use) { CFG cfg = cfgMap.get(declaration); ResolvedValueDeclaration resolved = use.getNameExpr().resolve(); ResolvedValueDeclaration resolved = use.getResolvedValueDeclaration(); FormalIONode formalIn = FormalIONode.createFormalIn(declaration, resolved); cfg.getRootNode().addMovableVariable(new VariableAction.Movable(use.toDefinition(cfg.getRootNode()), formalIn)); } Loading @@ -36,7 +36,7 @@ public class InterproceduralUsageFinder extends InterproceduralActionFinder<Vari protected void handleActualAction(CallGraph.Edge<?> edge, VariableAction.Usage use) { Set<VariableAction.Movable> movables = new HashSet<>(); GraphNode<?> graphNode = edge.getGraphNode(); ResolvedValueDeclaration resolved = use.getNameExpr().resolve(); ResolvedValueDeclaration resolved = use.getResolvedValueDeclaration(); Expression argument = extractArgument(use, edge, true); ActualIONode actualIn = ActualIONode.createActualIn(edge.getCall(), resolved, argument); argument.accept(new VariableVisitor( Loading