/// any transfer function logic and the sub-expression level (if any).
class CoreEngine {
friend class StmtNodeBuilder;
+ friend class GenericNodeBuilderImpl;
friend class BranchNodeBuilder;
friend class IndirectGotoNodeBuilder;
friend class SwitchNodeBuilder;
const GRState* getState() const { return Pred->State; }
};
+class GenericNodeBuilderImpl {
+protected:
+ CoreEngine &engine;
+ ExplodedNode *pred;
+ bool HasGeneratedNode;
+ ProgramPoint pp;
+ llvm::SmallVector<ExplodedNode*, 2> sinksGenerated;
+
+ ExplodedNode *generateNodeImpl(const GRState *state, ExplodedNode *pred,
+ ProgramPoint programPoint, bool asSink);
+
+ GenericNodeBuilderImpl(CoreEngine &eng, ExplodedNode *pr, ProgramPoint p)
+ : engine(eng), pred(pr), HasGeneratedNode(false), pp(p) {}
+
+public:
+ bool hasGeneratedNode() const { return HasGeneratedNode; }
+
+ WorkList &getWorkList() { return *engine.WList; }
+
+ ExplodedNode* getPredecessor() const { return pred; }
+
+ BlockCounter getBlockCounter() const {
+ return engine.WList->getBlockCounter();
+ }
+
+ const llvm::SmallVectorImpl<ExplodedNode*> &sinks() const {
+ return sinksGenerated;
+ }
+};
+
+template <typename PP>
+class GenericNodeBuilder : public GenericNodeBuilderImpl {
+public:
+ GenericNodeBuilder(CoreEngine &eng, ExplodedNode *pr, const PP &p)
+ : GenericNodeBuilderImpl(eng, pr, p) {}
+
+ ExplodedNode *generateNode(const GRState *state, ExplodedNode *pred,
+ PP programPoint, bool asSink) {
+ return generateNodeImpl(state, pred, programPoint, asSink);
+ }
+
+ const PP &getProgramPoint() const { return cast<PP>(pp); }
+};
+
class EndOfFunctionNodeBuilder {
CoreEngine &Eng;
const CFGBlock& B;
void ProcessTemporaryDtor(const CFGTemporaryDtor D,
StmtNodeBuilder &builder);
- /// processCFGBlockEntrance - Called by CoreEngine when start processing
- /// a CFGBlock. This method returns true if the analysis should continue
- /// exploring the given path, and false otherwise.
- bool processCFGBlockEntrance(const CFGBlock* B, const ExplodedNode *Pred,
- BlockCounter BC);
-
+ /// Called by CoreEngine when processing the entrance of a CFGBlock.
+ virtual void processCFGBlockEntrance(ExplodedNodeSet &dstNodes,
+ GenericNodeBuilder<BlockEntrance> &nodeBuilder);
+
/// ProcessBranch - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a branch condition.
void processBranch(const Stmt* Condition, const Stmt* Term,
#ifndef LLVM_CLANG_GR_SUBENGINE_H
#define LLVM_CLANG_GR_SUBENGINE_H
+#include "clang/Analysis/ProgramPoint.h"
#include "clang/StaticAnalyzer/PathSensitive/SVals.h"
namespace clang {
class Stmt;
namespace ento {
-
+
+template <typename PP> class GenericNodeBuilder;
class AnalysisManager;
+class ExplodedNodeSet;
class ExplodedNode;
class GRState;
class GRStateManager;
/// nodes by processing the 'effects' of a block-level statement.
virtual void processCFGElement(const CFGElement E, StmtNodeBuilder& builder)=0;
- /// Called by CoreEngine when start processing
- /// a CFGBlock. This method returns true if the analysis should continue
- /// exploring the given path, and false otherwise.
- virtual bool processCFGBlockEntrance(const CFGBlock* B,
- const ExplodedNode *Pred,
- BlockCounter BC) = 0;
+ /// Called by CoreEngine when it starts processing a CFGBlock. The
+ /// SubEngine is expected to populate dstNodes with new nodes representing
+ /// updated analysis state, or generate no nodes at all if it doesn't.
+ virtual void processCFGBlockEntrance(ExplodedNodeSet &dstNodes,
+ GenericNodeBuilder<BlockEntrance> &nodeBuilder) = 0;
/// Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a branch condition.
// Block entrance. (Update counters).
//===----------------------------------------------------------------------===//
-bool ExprEngine::processCFGBlockEntrance(const CFGBlock* B,
- const ExplodedNode *Pred,
- BlockCounter BC) {
- return BC.getNumVisited(Pred->getLocationContext()->getCurrentStackFrame(),
- B->getBlockID()) < AMgr.getMaxVisit();
+void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes,
+ GenericNodeBuilder<BlockEntrance> &nodeBuilder){
+
+ // FIXME: Refactor this into a checker.
+ const CFGBlock *block = nodeBuilder.getProgramPoint().getBlock();
+ ExplodedNode *pred = nodeBuilder.getPredecessor();
+
+ if (nodeBuilder.getBlockCounter().getNumVisited(
+ pred->getLocationContext()->getCurrentStackFrame(),
+ block->getBlockID()) >= AMgr.getMaxVisit()) {
+
+ static int tag = 0;
+ const BlockEntrance &BE = nodeBuilder.getProgramPoint();
+ BlockEntrance BE_tagged(BE.getBlock(), BE.getLocationContext(), &tag);
+ nodeBuilder.generateNode(pred->getState(), pred, BE_tagged, true);
+ }
}
//===----------------------------------------------------------------------===//
return;
}
- // FIXME: Should we allow processCFGBlockEntrance to also manipulate state?
-
- if (SubEng.processCFGBlockEntrance(Blk, Pred, WList->getBlockCounter()))
- generateNode(BlockEntrance(Blk, Pred->getLocationContext()),
- Pred->State, Pred);
+ // Call into the subengine to process entering the CFGBlock.
+ ExplodedNodeSet dstNodes;
+ BlockEntrance BE(Blk, Pred->getLocationContext());
+ GenericNodeBuilder<BlockEntrance> nodeBuilder(*this, Pred, BE);
+ SubEng.processCFGBlockEntrance(dstNodes, nodeBuilder);
+
+ if (dstNodes.empty()) {
+ if (!nodeBuilder.hasGeneratedNode()) {
+ // Auto-generate a node and enqueue it to the worklist.
+ generateNode(BE, Pred->State, Pred);
+ }
+ }
else {
- blocksAborted.push_back(std::make_pair(L, Pred));
+ for (ExplodedNodeSet::iterator I = dstNodes.begin(), E = dstNodes.end();
+ I != E; ++I) {
+ WList->enqueue(*I);
+ }
+ }
+
+ for (llvm::SmallVectorImpl<ExplodedNode*>::const_iterator
+ I = nodeBuilder.sinks().begin(), E = nodeBuilder.sinks().end();
+ I != E; ++I) {
+ blocksAborted.push_back(std::make_pair(L, *I));
}
}
if (IsNew) WList->enqueue(Node);
}
+ExplodedNode *
+GenericNodeBuilderImpl::generateNodeImpl(const GRState *state,
+ ExplodedNode *pred,
+ ProgramPoint programPoint,
+ bool asSink) {
+
+ HasGeneratedNode = true;
+ bool isNew;
+ ExplodedNode *node = engine.getGraph().getNode(programPoint, state, &isNew);
+ if (pred)
+ node->addPredecessor(pred, engine.getGraph());
+ if (isNew) {
+ if (asSink) {
+ node->markAsSink();
+ sinksGenerated.push_back(node);
+ }
+ return node;
+ }
+ return 0;
+}
+
StmtNodeBuilder::StmtNodeBuilder(const CFGBlock* b, unsigned idx,
ExplodedNode* N, CoreEngine* e,
GRStateManager &mgr)
-// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-max-loop 6 -verify %s
//===----------------------------------------------------------------------===//
// The following code is reduced using delta-debugging from