Java tutorial
/* * CPAchecker is a tool for configurable software verification. * This file is part of CPAchecker. * * Copyright (C) 2007-2014 Dirk Beyer * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * * CPAchecker web page: * http://cpachecker.sosy-lab.org */ package edu.buaa.satla.analysis.util; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashSet; import java.util.List; import java.util.Set; import org.sosy_lab.cpachecker.cfa.model.CFAEdge; import org.sosy_lab.cpachecker.cfa.model.CFANode; import org.sosy_lab.cpachecker.cfa.model.FunctionCallEdge; import org.sosy_lab.cpachecker.cfa.model.FunctionReturnEdge; import org.sosy_lab.cpachecker.cfa.model.FunctionSummaryEdge; import org.sosy_lab.cpachecker.cfa.model.MultiEdge; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; /** * This class provides strategies for iterating through a CFA * (a set of {@link CFANode}s connected by {@link CFAEdge}s). * Strategies differ for example in the direction (forwards/backwards), * and whether summary edges are recognized. * * Instances of this class are always immutable, thread-safe and may be re-used. * Thus, care must be taken when calling methods of this class which return a * CFATraversal instance. This never mutate the instance on which they are called! * * Right code: * <code> * CFATraversal traversal = CFATraversal.allEdgesForward(); * traversal = traversal.backwards(); * traversal.traverse(...); * </code> * * Wrong code: * <code> * CFATraversal traversal = CFATraversal.allEdgesForward(); * traversal.backwards(); // WRONG!!! Has no effect! * traversal.traverse(...); * </code> * * For traversing the CFA, a {@link CFAVisitor} needs to be given. * Several default implementations are available. * * Important: The instances of this class do not track a set of already visited * nodes. Thus a visitor may be called several times for a single node. * If the visitor never specifies to stop the traversal and the CFA contains loops, * this will produce an infinite loop! * It is strongly recommended to use the {@link NodeCollectingCFAVisitor} to * prevent this and visit each node only once (wrap your own visitor in it). */ public class CFATraversal { private static final Function<CFANode, Iterable<CFAEdge>> FORWARD_EDGE_SUPPLIER = new Function<CFANode, Iterable<CFAEdge>>() { @Override public Iterable<CFAEdge> apply(CFANode node) { return CFAUtils.allLeavingEdges(node); } }; private static final Function<CFANode, Iterable<CFAEdge>> BACKWARD_EDGE_SUPPLIER = new Function<CFANode, Iterable<CFAEdge>>() { @Override public Iterable<CFAEdge> apply(CFANode node) { return CFAUtils.allEnteringEdges(node); } }; // function providing the outgoing edges for a CFANode private final Function<CFANode, Iterable<CFAEdge>> edgeSupplier; // function providing the successor node of an edge private final Function<CFAEdge, CFANode> successorSupplier; // predicate for whether an edge should be ignored private final Predicate<CFAEdge> ignoreEdge; protected CFATraversal(Function<CFANode, Iterable<CFAEdge>> pEdgeSupplier, Function<CFAEdge, CFANode> pSuccessorSupplier, Predicate<CFAEdge> pIgnoreEdge) { edgeSupplier = pEdgeSupplier; successorSupplier = pSuccessorSupplier; ignoreEdge = pIgnoreEdge; } /** * Returns a default instance of this class, which iterates forward through * the CFA, visiting all nodes and edges in a DFS-like strategy. */ public static final CFATraversal dfs() { return new CFATraversal(FORWARD_EDGE_SUPPLIER, CFAUtils.TO_SUCCESSOR, Predicates.<CFAEdge>alwaysFalse()); } /** * Returns a new instance of this class which behaves exactly like the current * instance, except its traversal direction is reversed (e.g., going backwards * instead of going forwards). */ public CFATraversal backwards() { if (edgeSupplier == FORWARD_EDGE_SUPPLIER) { return new CFATraversal(BACKWARD_EDGE_SUPPLIER, CFAUtils.TO_PREDECESSOR, ignoreEdge); } else if (edgeSupplier == BACKWARD_EDGE_SUPPLIER) { return new CFATraversal(FORWARD_EDGE_SUPPLIER, CFAUtils.TO_SUCCESSOR, ignoreEdge); } else { throw new AssertionError(); } } /** * Returns a new instance of this class which behaves exactly like the current * instance, except it ignores summary edges ({@link FunctionSummaryEdge}s). * It will not call the visitor for them, and it will not follow this edge * during traversing. */ public CFATraversal ignoreSummaryEdges() { return new CFATraversal(edgeSupplier, successorSupplier, Predicates.<CFAEdge>or(ignoreEdge, Predicates.instanceOf(FunctionSummaryEdge.class))); } /** * Returns a new instance of this class which behaves exactly like the current * instance, except it ignores function call and return edges. * It will not call the visitor for them, and it will not follow this edge * during traversing. Thus it will always stay inside the current function. */ @SuppressWarnings("unchecked") public CFATraversal ignoreFunctionCalls() { return new CFATraversal(edgeSupplier, successorSupplier, Predicates.<CFAEdge>or(ignoreEdge, Predicates.instanceOf(FunctionCallEdge.class), Predicates.instanceOf(FunctionReturnEdge.class))); } /** * Traverse through the CFA according to the strategy represented by the * current instance, starting at a given node and passing each * encountered node and edge to a given visitor. * @param startingNode The starting node. * @param visitor The visitor to notify. */ public void traverse(final CFANode startingNode, final CFATraversal.CFAVisitor visitor) { Deque<CFANode> toProcess = new ArrayDeque<>(); toProcess.addLast(startingNode); while (!toProcess.isEmpty()) { CFANode n = toProcess.removeLast(); CFATraversal.TraversalProcess result = visitor.visitNode(n); if (result == TraversalProcess.ABORT) { return; } if (result != TraversalProcess.SKIP) { for (CFAEdge edge : edgeSupplier.apply(n)) { if (ignoreEdge.apply(edge)) { continue; } result = visitor.visitEdge(edge); if (result == TraversalProcess.ABORT) { return; } if (result != TraversalProcess.SKIP) { toProcess.addLast(successorSupplier.apply(edge)); } } } } return; } /** * Traverse through the CFA according to the strategy represented by the * current instance, starting at a given node and passing each * encountered node and edge to a given visitor. * * Each node will be visited only once. * This method does the same as wrapping the given visitor in a * {@link NodeCollectingCFAVisitor} and calling {@link #traverse(CFANode, CFAVisitor)}. * * @param startingNode The starting node. * @param visitor The visitor to notify. */ public void traverseOnce(final CFANode startingNode, final CFATraversal.CFAVisitor visitor) { traverse(startingNode, new NodeCollectingCFAVisitor(visitor)); } /** * Traverse through the CFA according to the strategy represented by the * current instance, starting at a given node and collecting all encountered nodes. * @param startingNode The starting node. * @return A modifiable reference to the set of visited nodes. */ public Set<CFANode> collectNodesReachableFrom(final CFANode startingNode) { NodeCollectingCFAVisitor visitor = new NodeCollectingCFAVisitor(); this.traverse(startingNode, visitor); return visitor.getVisitedNodes(); } // --- Useful visitor implementations --- /** * An implementation of {@link CFAVisitor} which does two things: * - It keeps a set of all visited nodes, and provides this set after the traversal process. * - It prevents the traversal process from visiting a node twice. * * Because of the last point it is suggested to always use this visitor. * * Instances of this visitor may be re-used. * In this case, the following uses will re-use the set of visited nodes from * the first time (i.e., a node visited in the first traversal will not be * visited in the second traversal) */ public final static class NodeCollectingCFAVisitor extends ForwardingCFAVisitor { private final Set<CFANode> visitedNodes = new HashSet<>(); /** * Creates a new instance which delegates calls to another visitor, but * never calls that visitor twice for the same node. * @param pDelegate The visitor to delegate to. */ public NodeCollectingCFAVisitor(CFAVisitor pDelegate) { super(pDelegate); } /** * Convenience constructor for cases when you only need the functionality * of this visitor and no other visitor. */ public NodeCollectingCFAVisitor() { super(DefaultCFAVisitor.INSTANCE); } @Override public TraversalProcess visitNode(CFANode pNode) { if (visitedNodes.add(pNode)) { return super.visitNode(pNode); } return TraversalProcess.SKIP; } /** * Get the set of nodes this visitor has seen so far. * This set may be modified by the caller. Nodes put in this set will not be * visited by this visitor, nodes removed from this set may be visited again. * * This method may be called even before the first time this visitor is used. * The returned set may not be modified during a traversal process. * * @return A modifiable reference to the set of visited nodes. */ public Set<CFANode> getVisitedNodes() { return visitedNodes; } } /** * An implementation of {@link CFAVisitor} which keeps track of all visited * edges. */ public final static class EdgeCollectingCFAVisitor extends ForwardingCFAVisitor { private final List<CFAEdge> visitedEdges = new ArrayList<>(); /** * Creates a new instance which delegates calls to another visitor. * @param pDelegate The visitor to delegate to. */ public EdgeCollectingCFAVisitor(CFAVisitor pDelegate) { super(pDelegate); } /** * Convenience constructor for cases when you only need the functionality * of this visitor and a {@link NodeCollectingCFAVisitor}. */ public EdgeCollectingCFAVisitor() { super(new NodeCollectingCFAVisitor()); } @Override public TraversalProcess visitEdge(CFAEdge pEdge) { visitedEdges.add(pEdge); return super.visitEdge(pEdge); } /** * Get the list of edges this visitor has seen so far in chronological order. * The list may contain edges twice, if they were visited several times. * Note that this is not the case if the {@link NodeCollectingCFAVisitor} * is used. * * The returned list may be modified, but not during the traversal process. * This will have no effect on the visitor, aside from the results of future * calls to this method. * * @return A reference to the set of visited nodes. */ public List<CFAEdge> getVisitedEdges() { return visitedEdges; } } /** * An implementation of {@link CFAVisitor} which delegates to several other * visitors. * All visitors will be called in the same order for each edge and node. * If one visitor returns ABORT, the other visitors will still be called, * and ABORT is returned. * If one visitor returns SKIP, the other visitors will still be called, * and SKIP is returned if none of them returned ABORT. * Otherweise CONTINUE is returned. */ public static class CompositeCFAVisitor implements CFAVisitor { private final ImmutableList<CFAVisitor> visitors; public CompositeCFAVisitor(Iterable<CFAVisitor> pVisitors) { visitors = ImmutableList.copyOf(pVisitors); } public CompositeCFAVisitor(CFAVisitor... pVisitors) { visitors = ImmutableList.copyOf(pVisitors); } @Override public TraversalProcess visitEdge(CFAEdge pEdge) { TraversalProcess totalResult = TraversalProcess.CONTINUE; for (CFAVisitor visitor : visitors) { TraversalProcess result = visitor.visitEdge(pEdge); if (result == TraversalProcess.ABORT) { totalResult = TraversalProcess.ABORT; } else if (result == TraversalProcess.SKIP && totalResult != TraversalProcess.ABORT) { totalResult = TraversalProcess.SKIP; } } return totalResult; } @Override public TraversalProcess visitNode(CFANode pNode) { TraversalProcess totalResult = TraversalProcess.CONTINUE; for (CFAVisitor visitor : visitors) { TraversalProcess result = visitor.visitNode(pNode); if (result == TraversalProcess.ABORT) { totalResult = TraversalProcess.ABORT; } else if (result == TraversalProcess.SKIP && totalResult != TraversalProcess.ABORT) { totalResult = TraversalProcess.SKIP; } } return totalResult; } } /** * An implementation of {@link CFAVisitor} that delegates to another visitor * and splits {@link MultiEdge}s into their contained edges during the process. * * No additional CFANodes are visited, only edges. * The edges of one MultiEdge are always handled in one sequence * from start to end (forwads), with no other edges or nodes in between, * regardless of the actual CFATraversal instance. * Thus it is best to use this implementation only with CFATraversal.dfs(). */ public static class SplitMultiEdgesCFAVisitor extends CFATraversal.ForwardingCFAVisitor { protected SplitMultiEdgesCFAVisitor(CFAVisitor pDelegate) { super(pDelegate); } @Override public TraversalProcess visitEdge(CFAEdge pEdge) { if (pEdge instanceof MultiEdge) { for (CFAEdge edge : ((MultiEdge) pEdge).getEdges()) { TraversalProcess result = visitEdge(edge); if (result != TraversalProcess.CONTINUE) { return result; } } return TraversalProcess.CONTINUE; } else { return super.visitEdge(pEdge); } } } // --- Types and classes for implementors of CFAVisitors /** * Interface for CFA traversal visitors used by {@link CFATraversal#traverse(CFANode, CFAVisitor)}. * * If any of these method throws an exception, the traversal process is * immediately aborted and the exception is passed to the caller. * * @see CFATraversal */ public static interface CFAVisitor { /** * Called for each edge the traversal process encounters. * @param edge The current CFAEdge. * @return A value of {@link TraversalProcess} to steer the traversal process. */ TraversalProcess visitEdge(CFAEdge edge); /** * Called for each node the traversal process encounters. * @param node The current CFANode. * @return A value of {@link TraversalProcess} to steer the traversal process. */ TraversalProcess visitNode(CFANode node); } /** * An enum for possible actions a visitor can tell the traversal strategy to * do next. */ public static enum TraversalProcess { /** * Continue normally. */ CONTINUE, /** * Skip following the currently handled node or edge (i.e., the successors won't be visited). */ SKIP, /** * Completely abort the traversal process (forgetting all nodes and edges which are still to be visited). */ ABORT; } /** * A default implementation of {@link CFAVisitor} which does nothing and * always returns {@link TraversalProcess#CONTINUE}. */ public static class DefaultCFAVisitor implements CFAVisitor { private static final CFAVisitor INSTANCE = new DefaultCFAVisitor(); @Override public TraversalProcess visitEdge(CFAEdge pEdge) { return TraversalProcess.CONTINUE; } @Override public TraversalProcess visitNode(CFANode pNode) { return TraversalProcess.CONTINUE; } } /** * A default implementation of {@link CFAVisitor} which forwards everything to * another visitor. */ public abstract static class ForwardingCFAVisitor implements CFAVisitor { protected final CFAVisitor delegate; protected ForwardingCFAVisitor(CFAVisitor pDelegate) { delegate = pDelegate; } @Override public TraversalProcess visitEdge(CFAEdge pEdge) { return delegate.visitEdge(pEdge); } @Override public TraversalProcess visitNode(CFANode pNode) { return delegate.visitNode(pNode); } } }