Hard bifurcate the state into nil receiver and non-nil receiver, so that

we don't need to use the DoneEvaluation hack when check for 
ObjCMessageExpr.

PreVisitObjCMessageExpr() only checks for undefined receiver or arguments.

Add checker interface EvalNilReceiver(). This is a 'once-and-done' interface.

llvm-svn: 90296
This commit is contained in:
Zhongxing Xu 2009-12-02 05:49:12 +00:00
parent d186a02987
commit af35329f71
6 changed files with 99 additions and 103 deletions

View File

@ -53,22 +53,10 @@ public:
OldTag(B.Tag, tag),
OldPointKind(B.PointKind, K),
OldHasGen(B.HasGeneratedNode),
state(st), statement(stmt), size(Dst.size()),
DoneEvaluating(false) {}
state(st), statement(stmt), size(Dst.size()) {}
~CheckerContext();
// FIXME: This were added to support CallAndMessageChecker to indicating
// to GRExprEngine to "stop evaluating" a message expression under certain
// cases. This is *not* meant to be a permanent API change, and was added
// to aid in the transition of removing logic for checks from GRExprEngine.
void setDoneEvaluating() {
DoneEvaluating = true;
}
bool isDoneEvaluating() const {
return DoneEvaluating;
}
ConstraintManager &getConstraintManager() {
return Eng.getConstraintManager();
}
@ -165,7 +153,7 @@ private:
friend class GRExprEngine;
// FIXME: Remove the 'tag' option.
bool GR_Visit(ExplodedNodeSet &Dst,
void GR_Visit(ExplodedNodeSet &Dst,
GRStmtNodeBuilder &Builder,
GRExprEngine &Eng,
const Stmt *S,
@ -177,7 +165,14 @@ private:
_PreVisit(C, S);
else
_PostVisit(C, S);
return C.isDoneEvaluating();
}
bool GR_EvalNilReceiver(ExplodedNodeSet &Dst, GRStmtNodeBuilder &Builder,
GRExprEngine &Eng, const ObjCMessageExpr *ME,
ExplodedNode *Pred, const GRState *state, void *tag) {
CheckerContext C(Dst, Builder, Eng, Pred, tag, ProgramPoint::PostStmtKind,
ME, state);
return EvalNilReceiver(C, ME);
}
// FIXME: Remove the 'tag' option.
@ -231,6 +226,10 @@ public:
virtual void VisitBranchCondition(GRBranchNodeBuilder &Builder,
GRExprEngine &Eng,
Stmt *Condition, void *tag) {}
virtual bool EvalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME) {
return false;
}
};
} // end clang namespace

View File

@ -209,8 +209,13 @@ public:
protected:
/// CheckerVisit - Dispatcher for performing checker-specific logic
/// at specific statements.
bool CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
void CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
bool isPrevisit);
void CheckerEvalNilReceiver(const ObjCMessageExpr *ME,
ExplodedNodeSet &Dst,
const GRState *state,
ExplodedNode *Pred);
void CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE,
ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
@ -358,9 +363,10 @@ public:
}
protected:
void EvalObjCMessageExpr(ExplodedNodeSet& Dst, ObjCMessageExpr* ME, ExplodedNode* Pred) {
void EvalObjCMessageExpr(ExplodedNodeSet& Dst, ObjCMessageExpr* ME,
ExplodedNode* Pred, const GRState *state) {
assert (Builder && "GRStmtNodeBuilder must be defined.");
getTF().EvalObjCMessageExpr(Dst, *this, *Builder, ME, Pred);
getTF().EvalObjCMessageExpr(Dst, *this, *Builder, ME, Pred, state);
}
const GRState* MarkBranch(const GRState* St, Stmt* Terminator,

View File

@ -47,7 +47,8 @@ public:
GRExprEngine& Engine,
GRStmtNodeBuilder& Builder,
ObjCMessageExpr* ME,
ExplodedNode* Pred) {}
ExplodedNode* Pred,
const GRState *state) {}
// Stores.

View File

@ -1985,7 +1985,7 @@ public:
Expr* Receiver,
const RetainSummary& Summ,
ExprIterator arg_beg, ExprIterator arg_end,
ExplodedNode* Pred);
ExplodedNode* Pred, const GRState *state);
virtual void EvalCall(ExplodedNodeSet& Dst,
GRExprEngine& Eng,
@ -1998,7 +1998,8 @@ public:
GRExprEngine& Engine,
GRStmtNodeBuilder& Builder,
ObjCMessageExpr* ME,
ExplodedNode* Pred);
ExplodedNode* Pred,
const GRState *state);
bool EvalObjCMessageExprAux(ExplodedNodeSet& Dst,
GRExprEngine& Engine,
@ -2777,10 +2778,7 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst,
Expr* Receiver,
const RetainSummary& Summ,
ExprIterator arg_beg, ExprIterator arg_end,
ExplodedNode* Pred) {
// Get the state.
const GRState *state = Builder.GetState(Pred);
ExplodedNode* Pred, const GRState *state) {
// Evaluate the effect of the arguments.
RefVal::Kind hasErr = (RefVal::Kind) 0;
@ -3013,34 +3011,23 @@ void CFRefCount::EvalCall(ExplodedNodeSet& Dst,
assert(Summ);
EvalSummary(Dst, Eng, Builder, CE, 0, *Summ,
CE->arg_begin(), CE->arg_end(), Pred);
CE->arg_begin(), CE->arg_end(), Pred, Builder.GetState(Pred));
}
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder& Builder,
ObjCMessageExpr* ME,
ExplodedNode* Pred) {
// FIXME: Since we moved the nil check into a checker, we could get nil
// receiver here. Need a better way to check such case.
if (Expr* Receiver = ME->getReceiver()) {
const GRState *state = Pred->getState();
DefinedOrUnknownSVal L=cast<DefinedOrUnknownSVal>(state->getSVal(Receiver));
if (!state->Assume(L, true)) {
Dst.Add(Pred);
return;
}
}
ExplodedNode* Pred,
const GRState *state) {
RetainSummary *Summ =
ME->getReceiver()
? Summaries.getInstanceMethodSummary(ME, Builder.GetState(Pred),
Pred->getLocationContext())
? Summaries.getInstanceMethodSummary(ME, state,Pred->getLocationContext())
: Summaries.getClassMethodSummary(ME);
assert(Summ && "RetainSummary is null");
EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), *Summ,
ME->arg_begin(), ME->arg_end(), Pred);
ME->arg_begin(), ME->arg_end(), Pred, state);
}
namespace {

View File

@ -41,6 +41,7 @@ public:
void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
bool EvalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME);
private:
void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
@ -148,28 +149,12 @@ void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C,
}
}
}
}
// Check if the receiver was nil and then returns a value that may
// be garbage.
if (const Expr *Receiver = ME->getReceiver()) {
DefinedOrUnknownSVal receiverVal =
cast<DefinedOrUnknownSVal>(state->getSVal(Receiver));
const GRState *notNullState, *nullState;
llvm::tie(notNullState, nullState) = state->Assume(receiverVal);
if (nullState && !notNullState) {
HandleNilReceiver(C, nullState, ME);
C.setDoneEvaluating(); // FIXME: eventually remove.
return;
}
assert(notNullState);
state = notNullState;
}
// Add a state transition if the state has changed.
C.addTransition(state);
bool CallAndMessageChecker::EvalNilReceiver(CheckerContext &C,
const ObjCMessageExpr *ME) {
HandleNilReceiver(C, C.getState(), ME);
return true; // Nil receiver is not handled elsewhere.
}
void CallAndMessageChecker::EmitNilReceiverBug(CheckerContext &C,

View File

@ -116,20 +116,18 @@ public:
// Checker worklist routines.
//===----------------------------------------------------------------------===//
bool GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
ExplodedNodeSet &Src, bool isPrevisit) {
if (Checkers.empty()) {
Dst.insert(Src);
return false;
Dst = Src;
return;
}
ExplodedNodeSet Tmp;
ExplodedNodeSet *PrevSet = &Src;
bool stopProcessingAfterCurrentChecker = false;
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I)
{
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){
ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst
: (PrevSet == &Tmp) ? &Src : &Tmp;
@ -138,31 +136,26 @@ bool GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
Checker *checker = I->second;
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
NI != NE; ++NI) {
// FIXME: Halting evaluation of the checkers is something we may
// not support later. The design is still evolving.
if (checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI,
tag, isPrevisit)) {
if (CurrSet != &Dst)
Dst.insert(*CurrSet);
stopProcessingAfterCurrentChecker = true;
continue;
}
assert(stopProcessingAfterCurrentChecker == false &&
"Inconsistent setting of 'stopProcessingAfterCurrentChecker'");
}
if (stopProcessingAfterCurrentChecker)
return true;
// Continue on to the next checker. Update the current NodeSet.
NI != NE; ++NI)
checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit);
PrevSet = CurrSet;
}
// Don't autotransition. The CheckerContext objects should do this
// automatically.
return false;
}
void GRExprEngine::CheckerEvalNilReceiver(const ObjCMessageExpr *ME,
ExplodedNodeSet &Dst,
const GRState *state,
ExplodedNode *Pred) {
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) {
void *tag = I->first;
Checker *checker = I->second;
if (checker->GR_EvalNilReceiver(Dst, *Builder, *this, ME, Pred, state, tag))
break;
}
}
// FIXME: This is largely copy-paste from CheckerVisit(). Need to
@ -1922,10 +1915,7 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME,
ExplodedNodeSet Src, DstTmp;
Src.Add(Pred);
if (CheckerVisit(ME, DstTmp, Src, true)) {
Dst.insert(DstTmp);
return;
}
CheckerVisit(ME, DstTmp, Src, true);
unsigned size = Dst.size();
@ -1934,10 +1924,38 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME,
Pred = *DI;
bool RaisesException = false;
if (ME->getReceiver()) {
if (const Expr *Receiver = ME->getReceiver()) {
const GRState *state = Pred->getState();
// Bifurcate the state into nil and non-nil ones.
DefinedOrUnknownSVal receiverVal =
cast<DefinedOrUnknownSVal>(state->getSVal(Receiver));
const GRState *notNilState, *nilState;
llvm::tie(notNilState, nilState) = state->Assume(receiverVal);
// There are three cases: can be nil or non-nil, must be nil, must be
// non-nil. We handle must be nil, and merge the rest two into non-nil.
if (nilState && !notNilState) {
CheckerEvalNilReceiver(ME, Dst, nilState, Pred);
return;
}
assert(notNilState);
// Check if the "raise" message was sent.
if (ME->getSelector() == RaiseSel)
RaisesException = true;
// Check if we raise an exception. For now treat these as sinks.
// Eventually we will want to handle exceptions properly.
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
if (RaisesException)
Builder->BuildSinks = true;
// Dispatch to plug-in transfer function.
SaveOr OldHasGen(Builder->HasGeneratedNode);
EvalObjCMessageExpr(Dst, ME, Pred, notNilState);
}
else {
@ -1984,17 +2002,17 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME,
RaisesException = true; break;
}
}
// Check if we raise an exception. For now treat these as sinks.
// Eventually we will want to handle exceptions properly.
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
if (RaisesException)
Builder->BuildSinks = true;
// Dispatch to plug-in transfer function.
SaveOr OldHasGen(Builder->HasGeneratedNode);
EvalObjCMessageExpr(Dst, ME, Pred, Builder->GetState(Pred));
}
// Check if we raise an exception. For now treat these as sinks. Eventually
// we will want to handle exceptions properly.
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
if (RaisesException)
Builder->BuildSinks = true;
// Dispatch to plug-in transfer function.
SaveOr OldHasGen(Builder->HasGeneratedNode);
EvalObjCMessageExpr(Dst, ME, Pred);
}
// Handle the case where no nodes where generated. Auto-generate that