[analyzer] Refactor VisitObjCMessage and VisitCallExpr to rely on the

same implementation for call evaluation.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160530 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Anna Zaks 2012-07-19 23:38:13 +00:00
Родитель d23ec89dcc
Коммит e81ce256b6
5 изменённых файлов: 94 добавлений и 88 удалений

Просмотреть файл

@ -467,9 +467,17 @@ public:
ExplodedNode *Pred, ProgramStateRef St, SVal TargetLV, SVal Val,
const ProgramPointTag *tag = 0);
/// \brief Create a new state in which the call return value is binded to the
/// call origin expression.
ProgramStateRef bindReturnValue(const CallEvent &Call,
const LocationContext *LCtx,
ProgramStateRef State);
void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
const SimpleCall &Call);
void defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
/// \bried Default implementation of call evaluation.
void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred,
const CallEvent &Call);
private:
void evalLoadCommon(ExplodedNodeSet &Dst,
@ -491,8 +499,7 @@ private:
const ProgramPointTag *tag, bool isLoad);
bool shouldInlineDecl(const Decl *D, ExplodedNode *Pred);
bool inlineCall(ExplodedNodeSet &Dst, const CallEvent &Call,
ExplodedNode *Pred);
bool inlineCall(const CallEvent &Call, ExplodedNode *Pred);
bool replayWithoutInlining(ExplodedNode *P, const LocationContext *CalleeLC);
};

Просмотреть файл

@ -568,8 +568,10 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
}
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
if (!anyEvaluated)
Eng.defaultEvalCall(Dst, Pred, Call);
if (!anyEvaluated) {
NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
Eng.defaultEvalCall(B, Pred, Call);
}
}
}

Просмотреть файл

@ -54,9 +54,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
Call, *this);
ExplodedNodeSet DstInvalidated;
StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext);
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
defaultEvalCall(DstInvalidated, *I, Call);
defaultEvalCall(Bldr, *I, Call);
ExplodedNodeSet DstPostCall;
getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated,
@ -77,9 +78,10 @@ void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD,
Call, *this);
ExplodedNodeSet DstInvalidated;
StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext);
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
defaultEvalCall(DstInvalidated, *I, Call);
defaultEvalCall(Bldr, *I, Call);
ExplodedNodeSet DstPostCall;
getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,

Просмотреть файл

@ -266,8 +266,7 @@ bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) {
return true;
}
bool ExprEngine::inlineCall(ExplodedNodeSet &Dst,
const CallEvent &Call,
bool ExprEngine::inlineCall(const CallEvent &Call,
ExplodedNode *Pred) {
if (!getAnalysisManager().shouldInlineCall())
return false;
@ -342,16 +341,16 @@ bool ExprEngine::inlineCall(ExplodedNodeSet &Dst,
return true;
}
static ProgramStateRef getInlineFailedState(ExplodedNode *&N,
static ProgramStateRef getInlineFailedState(ProgramStateRef State,
const Stmt *CallE) {
void *ReplayState = N->getState()->get<ReplayWithoutInlining>();
void *ReplayState = State->get<ReplayWithoutInlining>();
if (!ReplayState)
return 0;
assert(ReplayState == (const void*)CallE && "Backtracked to the wrong call.");
(void)CallE;
return N->getState()->remove<ReplayWithoutInlining>();
return State->remove<ReplayWithoutInlining>();
}
void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
@ -419,38 +418,69 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
Call, *this);
}
void ExprEngine::defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
const LocationContext *LCtx,
ProgramStateRef State) {
const Expr *E = Call.getOriginExpr();
if (!E)
return State;
// Some method families have known return values.
if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
switch (Msg->getMethodFamily()) {
default:
break;
case OMF_autorelease:
case OMF_retain:
case OMF_self: {
// These methods return their receivers.
return State->BindExpr(E, LCtx, Msg->getReceiverSVal());
break;
}
}
}
// Conjure a symbol if the return value is unknown.
QualType ResultTy = Call.getResultType();
SValBuilder &SVB = getSValBuilder();
unsigned Count = currentBuilderContext->getCurrentBlockCount();
SVal R = SVB.getConjuredSymbolVal(0, E, LCtx, ResultTy, Count);
return State->BindExpr(E, LCtx, R);
}
void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
const CallEvent &Call) {
ProgramStateRef State = 0;
const Expr *E = Call.getOriginExpr();
// Try to inline the call.
// The origin expression here is just used as a kind of checksum;
// for CallEvents that do not have origin expressions, this should still be
// safe.
const Expr *E = Call.getOriginExpr();
ProgramStateRef state = getInlineFailedState(Pred, E);
if (state == 0 && inlineCall(Dst, Call, Pred))
return;
if (!isa<ObjCMethodCall>(Call)) {
State = getInlineFailedState(Pred->getState(), E);
if (State == 0 && inlineCall(Call, Pred)) {
// If we inlined the call, the successor has been manually added onto
// the work list and we should not consider it for subsequent call
// handling steps.
Bldr.takeNodes(Pred);
return;
}
}
// If we can't inline it, handle the return value and invalidate the regions.
NodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
if (State == 0)
State = Pred->getState();
// Invalidate any regions touched by the call.
unsigned Count = currentBuilderContext->getCurrentBlockCount();
if (state == 0)
state = Pred->getState();
state = Call.invalidateRegions(Count, state);
State = Call.invalidateRegions(Count, State);
// Conjure a symbol value to use as the result.
if (E) {
QualType ResultTy = Call.getResultType();
SValBuilder &SVB = getSValBuilder();
const LocationContext *LCtx = Pred->getLocationContext();
SVal RetVal = SVB.getConjuredSymbolVal(0, E, LCtx, ResultTy, Count);
state = state->BindExpr(E, LCtx, RetVal);
}
// Construct and bind the return value.
State = bindReturnValue(Call, Pred->getLocationContext(), State);
// And make the result node.
Bldr.generateNode(Call.getProgramPoint(), state, Pred);
Bldr.generateNode(Call.getProgramPoint(), State, Pred);
}
void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,

Просмотреть файл

@ -158,9 +158,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(),
DE = dstGenericPrevisit.end(); DI != DE; ++DI) {
ExplodedNode *Pred = *DI;
bool RaisesException = false;
if (msg.isInstanceMessage()) {
SVal recVal = msg.getReceiverSVal();
@ -182,13 +180,19 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
// Check if the "raise" message was sent.
assert(notNilState);
if (msg.getSelector() == RaiseSel)
RaisesException = true;
if (msg.getSelector() == RaiseSel) {
// If we raise an exception, for now treat it as a sink.
// Eventually we will want to handle exceptions properly.
Bldr.generateNode(currentStmt, Pred, Pred->getState(), true);
continue;
}
// If we raise an exception, for now treat it as a sink.
// Eventually we will want to handle exceptions properly.
// Dispatch to plug-in transfer function.
evalObjCMessage(Bldr, msg, Pred, notNilState, RaisesException);
// Generate a transition to non-Nil state.
if (notNilState != state)
Pred = Bldr.generateNode(currentStmt, Pred, notNilState);
// Evaluate the call.
defaultEvalCall(Bldr, Pred, msg);
}
} else {
// Check for special class methods.
@ -222,19 +226,25 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
}
Selector S = msg.getSelector();
bool RaisesException = false;
for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) {
if (S == NSExceptionInstanceRaiseSelectors[i]) {
RaisesException = true;
break;
}
}
if (RaisesException) {
// If we raise an exception, for now treat it as a sink.
// Eventually we will want to handle exceptions properly.
Bldr.generateNode(currentStmt, Pred, Pred->getState(), true);
continue;
}
}
}
// If we raise an exception, for now treat it as a sink.
// Eventually we will want to handle exceptions properly.
// Dispatch to plug-in transfer function.
evalObjCMessage(Bldr, msg, Pred, Pred->getState(), RaisesException);
// Evaluate the call.
defaultEvalCall(Bldr, Pred, msg);
}
}
@ -246,48 +256,3 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit,
msg, *this);
}
void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr,
const ObjCMethodCall &msg,
ExplodedNode *Pred,
ProgramStateRef state,
bool GenSink) {
// First handle the return value.
SVal ReturnValue = UnknownVal();
// Some method families have known return values.
switch (msg.getMethodFamily()) {
default:
break;
case OMF_autorelease:
case OMF_retain:
case OMF_self: {
// These methods return their receivers.
ReturnValue = msg.getReceiverSVal();
break;
}
}
const LocationContext *LCtx = Pred->getLocationContext();
unsigned BlockCount = currentBuilderContext->getCurrentBlockCount();
// If we failed to figure out the return value, use a conjured value instead.
if (ReturnValue.isUnknown()) {
SValBuilder &SVB = getSValBuilder();
QualType ResultTy = msg.getResultType();
const Expr *CurrentE = cast<Expr>(currentStmt);
ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, LCtx, ResultTy,
BlockCount);
}
// Bind the return value.
state = state->BindExpr(currentStmt, LCtx, ReturnValue);
// Invalidate the arguments (and the receiver)
state = msg.invalidateRegions(BlockCount, state);
// And create the new node.
Bldr.generateNode(currentStmt, Pred, state, GenSink);
assert(Bldr.hasGeneratedNodes());
}