Convert GRExprEngine::VisitCallExpr() to use a worklist instead of recursion to evaluate the arguments of a CallExpr. This simplifies the logic and makes it easier to read. (it also avoids any issues with blowing out the stack if the CallExpr had a ridiculous number of arguments)

llvm-svn: 91613
This commit is contained in:
Ted Kremenek 2009-12-17 20:06:29 +00:00
parent 50272752fa
commit 94cc33f33e
2 changed files with 64 additions and 49 deletions

View File

@ -266,10 +266,6 @@ protected:
void VisitCall(CallExpr* CE, ExplodedNode* Pred,
CallExpr::arg_iterator AI, CallExpr::arg_iterator AE,
ExplodedNodeSet& Dst);
void VisitCallRec(CallExpr* CE, ExplodedNode* Pred,
CallExpr::arg_iterator AI, CallExpr::arg_iterator AE,
ExplodedNodeSet& Dst, const FunctionProtoType *,
unsigned ParamIdx = 0);
/// VisitCast - Transfer function logic for all casts (implicit and explicit).
void VisitCast(Expr* CastE, Expr* Ex, ExplodedNode* Pred,

View File

@ -1549,94 +1549,112 @@ void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S,
// Transfer function: Function calls.
//===----------------------------------------------------------------------===//
namespace {
class CallExprWLItem {
public:
CallExpr::arg_iterator I;
ExplodedNode *N;
CallExprWLItem(const CallExpr::arg_iterator &i, ExplodedNode *n)
: I(i), N(n) {}
};
} // end anonymous namespace
void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred,
CallExpr::arg_iterator AI,
CallExpr::arg_iterator AE,
ExplodedNodeSet& Dst) {
// Determine the type of function we're calling (if available).
const FunctionProtoType *Proto = NULL;
QualType FnType = CE->getCallee()->IgnoreParens()->getType();
if (const PointerType *FnTypePtr = FnType->getAs<PointerType>())
Proto = FnTypePtr->getPointeeType()->getAs<FunctionProtoType>();
VisitCallRec(CE, Pred, AI, AE, Dst, Proto, /*ParamIdx=*/0);
}
void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred,
CallExpr::arg_iterator AI,
CallExpr::arg_iterator AE,
ExplodedNodeSet& Dst,
const FunctionProtoType *Proto,
unsigned ParamIdx) {
// Process the arguments.
if (AI != AE) {
// If the call argument is being bound to a reference parameter,
// visit it as an lvalue, not an rvalue.
// Create a worklist to process the arguments.
llvm::SmallVector<CallExprWLItem, 20> WorkList;
WorkList.reserve(AE - AI);
WorkList.push_back(CallExprWLItem(AI, Pred));
ExplodedNodeSet ArgsEvaluated;
while (!WorkList.empty()) {
CallExprWLItem Item = WorkList.back();
WorkList.pop_back();
if (Item.I == AE) {
ArgsEvaluated.insert(Item.N);
continue;
}
// Evaluate the argument.
ExplodedNodeSet Tmp;
const unsigned ParamIdx = Item.I - AI;
bool VisitAsLvalue = false;
if (Proto && ParamIdx < Proto->getNumArgs())
VisitAsLvalue = Proto->getArgType(ParamIdx)->isReferenceType();
ExplodedNodeSet DstTmp;
if (VisitAsLvalue)
VisitLValue(*AI, Pred, DstTmp);
VisitLValue(*Item.I, Item.N, Tmp);
else
Visit(*AI, Pred, DstTmp);
++AI;
Visit(*Item.I, Item.N, Tmp);
// Enqueue evaluating the next argument on the worklist.
++(Item.I);
for (ExplodedNodeSet::iterator DI=DstTmp.begin(), DE=DstTmp.end(); DI != DE;
++DI)
VisitCallRec(CE, *DI, AI, AE, Dst, Proto, ParamIdx + 1);
return;
for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI)
WorkList.push_back(CallExprWLItem(Item.I, *NI));
}
// If we reach here we have processed all of the arguments. Evaluate
// the callee expression.
// Now process the call itself. First evaluate the callee.
ExplodedNodeSet DstTmp;
Expr* Callee = CE->getCallee()->IgnoreParens();
for (ExplodedNodeSet::iterator NI=ArgsEvaluated.begin(),
NE=ArgsEvaluated.end();
NI != NE; ++NI) {
{ // Enter new scope to make the lifetime of 'DstTmp2' bounded.
ExplodedNodeSet DstTmp2;
Visit(Callee, Pred, DstTmp2);
Visit(Callee, *NI, DstTmp2);
// Perform the previsit of the CallExpr, storing the results in DstTmp.
CheckerVisit(CE, DstTmp, DstTmp2, true);
}
// Finally, evaluate the function call.
// Finally, evaluate the function call. We try each of the checkers
// to see if the can evaluate the function call.
ExplodedNodeSet DstTmp3;
for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end();
DI != DE; ++DI) {
const GRState* state = GetState(*DI);
SVal L = state->getSVal(Callee);
// FIXME: Add support for symbolic function calls (calls involving
// function pointer values that are symbolic).
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
ExplodedNodeSet DstChecker;
// If the callee is processed by a checker, skip the rest logic.
if (CheckerEvalCall(CE, DstChecker, *DI))
DstTmp3.insert(DstChecker);
else {
for (ExplodedNodeSet::iterator DI_Checker = DstChecker.begin(),
DE_Checker = DstChecker.end();
DI_Checker != DE_Checker; ++DI_Checker) {
// Dispatch to the plug-in transfer function.
DE_Checker = DstChecker.end();
DI_Checker != DE_Checker; ++DI_Checker) {
// Dispatch to the plug-in transfer function.
unsigned OldSize = DstTmp3.size();
SaveOr OldHasGen(Builder->HasGeneratedNode);
Pred = *DI_Checker;
// Dispatch to transfer function logic to handle the call itself.
// FIXME: Allow us to chain together transfer functions.
assert(Builder && "GRStmtNodeBuilder must be defined.");
assert(Builder && "GRStmtNodeBuilder must be defined.");
getTF().EvalCall(DstTmp3, *this, *Builder, CE, L, Pred);
// Handle the case where no nodes where generated. Auto-generate that
// contains the updated state if we aren't generating sinks.
if (!Builder->BuildSinks && DstTmp3.size() == OldSize &&
@ -1645,9 +1663,10 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred,
}
}
}
// Perform the post-condition check of the CallExpr.
CheckerVisit(CE, Dst, DstTmp3, false);
// Finally, perform the post-condition check of the CallExpr and store
// the created nodes in 'Dst'.
CheckerVisit(CE, Dst, DstTmp3, false);
}
//===----------------------------------------------------------------------===//