Loading sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SummaryArcAnalyzer.java +62 −21 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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<>()); Loading @@ -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) Loading @@ -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())) Loading @@ -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) Loading @@ -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(); } } sdg-core/src/main/java/es/upv/mist/slicing/nodes/exceptionsensitive/ExceptionExitNode.java +6 −0 Original line number Diff line number Diff line Loading @@ -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; } } sdg-core/src/main/java/es/upv/mist/slicing/nodes/exceptionsensitive/ExitNode.java +3 −1 Original line number Diff line number Diff line Loading @@ -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); } sdg-core/src/main/java/es/upv/mist/slicing/nodes/exceptionsensitive/NormalExitNode.java +5 −0 Original line number Diff line number Diff line Loading @@ -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; } } sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/MethodExitNode.java +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SummaryArcAnalyzer.java +62 −21 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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<>()); Loading @@ -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) Loading @@ -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())) Loading @@ -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) Loading @@ -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(); } }
sdg-core/src/main/java/es/upv/mist/slicing/nodes/exceptionsensitive/ExceptionExitNode.java +6 −0 Original line number Diff line number Diff line Loading @@ -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; } }
sdg-core/src/main/java/es/upv/mist/slicing/nodes/exceptionsensitive/ExitNode.java +3 −1 Original line number Diff line number Diff line Loading @@ -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); }
sdg-core/src/main/java/es/upv/mist/slicing/nodes/exceptionsensitive/NormalExitNode.java +5 −0 Original line number Diff line number Diff line Loading @@ -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; } }
sdg-core/src/main/java/es/upv/mist/slicing/nodes/io/MethodExitNode.java +1 −1 Original line number Diff line number Diff line Loading @@ -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