forked from OSchip/llvm-project
[analyzer] Migrate argument invalidation from CFRefCount to ExprEngine.
This is a common path for function and C++ method calls, Objective-C messages and property accesses, and C++ construct-exprs. As support, add message receiver accessors to ObjCMessage and CallOrObjCMessage. llvm-svn: 138718
This commit is contained in:
parent
7bfd86d046
commit
d188d66e69
|
@ -410,6 +410,10 @@ protected:
|
|||
void evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg,
|
||||
ExplodedNode *Pred, const ProgramState *state);
|
||||
|
||||
const ProgramState *invalidateArguments(const ProgramState *State,
|
||||
const CallOrObjCMessage &Call,
|
||||
const LocationContext *LC);
|
||||
|
||||
const ProgramState *MarkBranch(const ProgramState *St, const Stmt *Terminator,
|
||||
bool branchTaken);
|
||||
|
||||
|
|
|
@ -88,6 +88,21 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
SVal getInstanceReceiverSVal(const ProgramState *State,
|
||||
const LocationContext *LC) const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
if (!isInstanceMessage())
|
||||
return UndefinedVal();
|
||||
if (const Expr *Ex = getInstanceReceiver())
|
||||
return State->getSValAsScalarOrLoc(Ex);
|
||||
|
||||
// An instance message with no expression means we are sending to super.
|
||||
// In this case the object reference is the same as 'self'.
|
||||
const ImplicitParamDecl *SelfDecl = LC->getSelfDecl();
|
||||
assert(SelfDecl && "No message receiver Expr, but not in an ObjC method");
|
||||
return State->getSVal(State->getRegion(SelfDecl, LC));
|
||||
}
|
||||
|
||||
bool isInstanceMessage() const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
|
||||
|
@ -98,7 +113,7 @@ public:
|
|||
}
|
||||
|
||||
const ObjCMethodDecl *getMethodDecl() const;
|
||||
|
||||
|
||||
const ObjCInterfaceDecl *getReceiverInterface() const;
|
||||
|
||||
SourceLocation getSuperLoc() const {
|
||||
|
@ -108,45 +123,58 @@ public:
|
|||
return cast<ObjCPropertyRefExpr>(MsgOrPropE)->getReceiverLocation();
|
||||
}
|
||||
|
||||
SourceRange getSourceRange() const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
SourceRange getSourceRange() const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
return MsgOrPropE->getSourceRange();
|
||||
}
|
||||
|
||||
unsigned getNumArgs() const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
|
||||
return msgE->getNumArgs();
|
||||
return isPropertySetter() ? 1 : 0;
|
||||
}
|
||||
unsigned getNumArgs() const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
|
||||
return msgE->getNumArgs();
|
||||
return isPropertySetter() ? 1 : 0;
|
||||
}
|
||||
|
||||
SVal getArgSVal(unsigned i, const ProgramState *state) const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
assert(i < getNumArgs() && "Invalid index for argument");
|
||||
if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
|
||||
return state->getSVal(msgE->getArg(i));
|
||||
assert(isPropertySetter());
|
||||
return SetterArgV;
|
||||
}
|
||||
SVal getArgSVal(unsigned i, const ProgramState *state) const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
assert(i < getNumArgs() && "Invalid index for argument");
|
||||
if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
|
||||
return state->getSVal(msgE->getArg(i));
|
||||
assert(isPropertySetter());
|
||||
return SetterArgV;
|
||||
}
|
||||
|
||||
QualType getArgType(unsigned i) const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
assert(i < getNumArgs() && "Invalid index for argument");
|
||||
if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
|
||||
return msgE->getArg(i)->getType();
|
||||
assert(isPropertySetter());
|
||||
return cast<ObjCPropertyRefExpr>(MsgOrPropE)->getType();
|
||||
}
|
||||
|
||||
const Expr *getArgExpr(unsigned i) const;
|
||||
QualType getArgType(unsigned i) const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
assert(i < getNumArgs() && "Invalid index for argument");
|
||||
if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
|
||||
return msgE->getArg(i)->getType();
|
||||
assert(isPropertySetter());
|
||||
return cast<ObjCPropertyRefExpr>(MsgOrPropE)->getType();
|
||||
}
|
||||
|
||||
SourceRange getArgSourceRange(unsigned i) const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
assert(i < getNumArgs() && "Invalid index for argument");
|
||||
if (const Expr *argE = getArgExpr(i))
|
||||
return argE->getSourceRange();
|
||||
return OriginE->getSourceRange();
|
||||
}
|
||||
const Expr *getArgExpr(unsigned i) const;
|
||||
|
||||
SourceRange getArgSourceRange(unsigned i) const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
assert(i < getNumArgs() && "Invalid index for argument");
|
||||
if (const Expr *argE = getArgExpr(i))
|
||||
return argE->getSourceRange();
|
||||
return OriginE->getSourceRange();
|
||||
}
|
||||
|
||||
SourceRange getReceiverSourceRange() const {
|
||||
assert(isValid() && "This ObjCMessage is uninitialized!");
|
||||
if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
|
||||
return msgE->getReceiverRange();
|
||||
|
||||
const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
|
||||
if (propE->isObjectReceiver())
|
||||
return propE->getBase()->getSourceRange();
|
||||
|
||||
// FIXME: This isn't a range.
|
||||
return propE->getReceiverLocation();
|
||||
}
|
||||
};
|
||||
|
||||
class ObjCPropertyGetter : public ObjCMessage {
|
||||
|
@ -211,6 +239,7 @@ public:
|
|||
|
||||
SVal getFunctionCallee() const;
|
||||
SVal getCXXCallee() const;
|
||||
SVal getInstanceMessageReceiver(const LocationContext *LC) const;
|
||||
|
||||
unsigned getNumArgs() const {
|
||||
if (!CallE)
|
||||
|
@ -244,6 +273,11 @@ public:
|
|||
return getArg(i)->getSourceRange();
|
||||
return Msg.getArgSourceRange(i);
|
||||
}
|
||||
|
||||
SourceRange getReceiverSourceRange() const {
|
||||
assert(isObjCMessage());
|
||||
return Msg.getReceiverSourceRange();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -40,50 +40,6 @@ using namespace clang;
|
|||
using namespace ento;
|
||||
using llvm::StrInStrNoCase;
|
||||
|
||||
namespace {
|
||||
class InstanceReceiver {
|
||||
ObjCMessage Msg;
|
||||
const LocationContext *LC;
|
||||
public:
|
||||
InstanceReceiver() : LC(0) { }
|
||||
InstanceReceiver(const ObjCMessage &msg,
|
||||
const LocationContext *lc = 0) : Msg(msg), LC(lc) {}
|
||||
|
||||
bool isValid() const {
|
||||
return Msg.isValid() && Msg.isInstanceMessage();
|
||||
}
|
||||
operator bool() const {
|
||||
return isValid();
|
||||
}
|
||||
|
||||
SVal getSValAsScalarOrLoc(const ProgramState *state) {
|
||||
assert(isValid());
|
||||
// We have an expression for the receiver? Fetch the value
|
||||
// of that expression.
|
||||
if (const Expr *Ex = Msg.getInstanceReceiver())
|
||||
return state->getSValAsScalarOrLoc(Ex);
|
||||
|
||||
// Otherwise we are sending a message to super. In this case the
|
||||
// object reference is the same as 'self'.
|
||||
if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl())
|
||||
return state->getSVal(state->getRegion(SelfDecl, LC));
|
||||
|
||||
return UnknownVal();
|
||||
}
|
||||
|
||||
SourceRange getSourceRange() const {
|
||||
assert(isValid());
|
||||
if (const Expr *Ex = Msg.getInstanceReceiver())
|
||||
return Ex->getSourceRange();
|
||||
|
||||
// Otherwise we are sending a message to super.
|
||||
SourceLocation L = Msg.getSuperLoc();
|
||||
assert(L.isValid());
|
||||
return SourceRange(L, L);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
class GenericNodeBuilderRefCount {
|
||||
StmtNodeBuilder *SNB;
|
||||
|
@ -1651,28 +1607,6 @@ public:
|
|||
}
|
||||
|
||||
const LangOptions& getLangOptions() const { return LOpts; }
|
||||
|
||||
// Calls.
|
||||
|
||||
void evalCallOrMessage(ExplodedNodeSet &Dst, ExprEngine &Eng,
|
||||
StmtNodeBuilder &Builder,
|
||||
const CallOrObjCMessage &callOrMsg,
|
||||
InstanceReceiver Receiver, const MemRegion *Callee,
|
||||
ExplodedNode *Pred, const ProgramState *state);
|
||||
|
||||
virtual void evalCall(ExplodedNodeSet &Dst,
|
||||
ExprEngine& Eng,
|
||||
StmtNodeBuilder& Builder,
|
||||
const CallExpr *CE, SVal L,
|
||||
ExplodedNode *Pred);
|
||||
|
||||
|
||||
virtual void evalObjCMessage(ExplodedNodeSet &Dst,
|
||||
ExprEngine& Engine,
|
||||
StmtNodeBuilder& Builder,
|
||||
ObjCMessage msg,
|
||||
ExplodedNode *Pred,
|
||||
const ProgramState *state);
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
@ -2439,133 +2373,6 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
|
|||
return RetTy;
|
||||
}
|
||||
|
||||
void CFRefCount::evalCallOrMessage(ExplodedNodeSet &Dst, ExprEngine &Eng,
|
||||
StmtNodeBuilder &Builder,
|
||||
const CallOrObjCMessage &callOrMsg,
|
||||
InstanceReceiver Receiver,
|
||||
const MemRegion *Callee,
|
||||
ExplodedNode *Pred,
|
||||
const ProgramState *state) {
|
||||
|
||||
SmallVector<const MemRegion*, 10> RegionsToInvalidate;
|
||||
|
||||
// Invalidate all instance variables of the receiver of a message.
|
||||
// FIXME: We should be able to do better with inter-procedural analysis.
|
||||
if (Receiver) {
|
||||
SVal V = Receiver.getSValAsScalarOrLoc(state);
|
||||
if (const MemRegion *region = V.getAsRegion())
|
||||
RegionsToInvalidate.push_back(region);
|
||||
}
|
||||
|
||||
// Invalidate all instance variables for the callee of a C++ method call.
|
||||
// FIXME: We should be able to do better with inter-procedural analysis.
|
||||
// FIXME: we can probably do better for const versus non-const methods.
|
||||
if (callOrMsg.isCXXCall()) {
|
||||
if (const MemRegion *callee = callOrMsg.getCXXCallee().getAsRegion())
|
||||
RegionsToInvalidate.push_back(callee);
|
||||
}
|
||||
|
||||
for (unsigned idx = 0, e = callOrMsg.getNumArgs(); idx != e; ++idx) {
|
||||
SVal V = callOrMsg.getArgSVal(idx);
|
||||
|
||||
// If we are passing a location wrapped as an integer, unwrap it and
|
||||
// invalidate the values referred by the location.
|
||||
if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V))
|
||||
V = Wrapped->getLoc();
|
||||
else if (!isa<Loc>(V))
|
||||
continue;
|
||||
|
||||
if (const MemRegion *R = V.getAsRegion()) {
|
||||
// Invalidate the value of the variable passed by reference.
|
||||
|
||||
// Are we dealing with an ElementRegion? If the element type is
|
||||
// a basic integer type (e.g., char, int) and the underying region
|
||||
// is a variable region then strip off the ElementRegion.
|
||||
// FIXME: We really need to think about this for the general case
|
||||
// as sometimes we are reasoning about arrays and other times
|
||||
// about (char*), etc., is just a form of passing raw bytes.
|
||||
// e.g., void *p = alloca(); foo((char*)p);
|
||||
if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
|
||||
// Checking for 'integral type' is probably too promiscuous, but
|
||||
// we'll leave it in for now until we have a systematic way of
|
||||
// handling all of these cases. Eventually we need to come up
|
||||
// with an interface to StoreManager so that this logic can be
|
||||
// approriately delegated to the respective StoreManagers while
|
||||
// still allowing us to do checker-specific logic (e.g.,
|
||||
// invalidating reference counts), probably via callbacks.
|
||||
if (ER->getElementType()->isIntegralOrEnumerationType()) {
|
||||
const MemRegion *superReg = ER->getSuperRegion();
|
||||
if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) ||
|
||||
isa<ObjCIvarRegion>(superReg))
|
||||
R = cast<TypedRegion>(superReg);
|
||||
}
|
||||
// FIXME: What about layers of ElementRegions?
|
||||
}
|
||||
|
||||
// Mark this region for invalidation. We batch invalidate regions
|
||||
// below for efficiency.
|
||||
RegionsToInvalidate.push_back(R);
|
||||
} else {
|
||||
// Nuke all other arguments passed by reference.
|
||||
// FIXME: is this necessary or correct? This handles the non-Region
|
||||
// cases. Is it ever valid to store to these?
|
||||
state = state->unbindLoc(cast<Loc>(V));
|
||||
}
|
||||
}
|
||||
|
||||
// Block calls result in all captured values passed-via-reference to be
|
||||
// invalidated.
|
||||
if (const BlockDataRegion *BR = dyn_cast_or_null<BlockDataRegion>(Callee))
|
||||
RegionsToInvalidate.push_back(BR);
|
||||
|
||||
// Invalidate designated regions using the batch invalidation API.
|
||||
|
||||
// FIXME: We can have collisions on the conjured symbol if the
|
||||
// expression *I also creates conjured symbols. We probably want
|
||||
// to identify conjured symbols by an expression pair: the enclosing
|
||||
// expression (the context) and the expression itself. This should
|
||||
// disambiguate conjured symbols.
|
||||
unsigned Count = Builder.getCurrentBlockCount();
|
||||
StoreManager::InvalidatedSymbols IS;
|
||||
|
||||
const Expr *Ex = callOrMsg.getOriginExpr();
|
||||
|
||||
// NOTE: Even if RegionsToInvalidate is empty, we must still invalidate
|
||||
// global variables.
|
||||
// NOTE: RetainReleaseChecker handles the actual invalidation of symbols.
|
||||
state =
|
||||
state->invalidateRegions(RegionsToInvalidate,
|
||||
Ex, Count, &IS,
|
||||
/* invalidateGlobals = */
|
||||
Eng.doesInvalidateGlobals(callOrMsg));
|
||||
|
||||
Builder.MakeNode(Dst, Ex, Pred, state);
|
||||
}
|
||||
|
||||
|
||||
void CFRefCount::evalCall(ExplodedNodeSet &Dst,
|
||||
ExprEngine& Eng,
|
||||
StmtNodeBuilder& Builder,
|
||||
const CallExpr *CE, SVal L,
|
||||
ExplodedNode *Pred) {
|
||||
|
||||
evalCallOrMessage(Dst, Eng, Builder, CallOrObjCMessage(CE, Pred->getState()),
|
||||
InstanceReceiver(), L.getAsRegion(), Pred,
|
||||
Pred->getState());
|
||||
}
|
||||
|
||||
void CFRefCount::evalObjCMessage(ExplodedNodeSet &Dst,
|
||||
ExprEngine& Eng,
|
||||
StmtNodeBuilder& Builder,
|
||||
ObjCMessage msg,
|
||||
ExplodedNode *Pred,
|
||||
const ProgramState *state) {
|
||||
|
||||
evalCallOrMessage(Dst, Eng, Builder, CallOrObjCMessage(msg, Pred->getState()),
|
||||
InstanceReceiver(msg, Pred->getLocationContext()),
|
||||
/* Callee = */ 0, Pred, state);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Pieces of the retain/release checker implemented using a CheckerVisitor.
|
||||
// More pieces of the retain/release checker will be migrated to this interface
|
||||
|
@ -2744,7 +2551,7 @@ public:
|
|||
void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const;
|
||||
void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const;
|
||||
void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call,
|
||||
InstanceReceiver Receiver, CheckerContext &C) const;
|
||||
CheckerContext &C) const;
|
||||
|
||||
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
|
||||
|
||||
|
@ -3013,7 +2820,7 @@ void RetainReleaseChecker::checkPostStmt(const CallExpr *CE,
|
|||
if (!Summ)
|
||||
return;
|
||||
|
||||
checkSummary(*Summ, CallOrObjCMessage(CE, state), InstanceReceiver(), C);
|
||||
checkSummary(*Summ, CallOrObjCMessage(CE, state), C);
|
||||
}
|
||||
|
||||
void RetainReleaseChecker::checkPostStmt(const CXXConstructExpr *CE,
|
||||
|
@ -3030,7 +2837,7 @@ void RetainReleaseChecker::checkPostStmt(const CXXConstructExpr *CE,
|
|||
return;
|
||||
|
||||
const ProgramState *state = C.getState();
|
||||
checkSummary(*Summ, CallOrObjCMessage(CE, state), InstanceReceiver(), C);
|
||||
checkSummary(*Summ, CallOrObjCMessage(CE, state), C);
|
||||
}
|
||||
|
||||
void RetainReleaseChecker::checkPostObjCMessage(const ObjCMessage &Msg,
|
||||
|
@ -3052,13 +2859,11 @@ void RetainReleaseChecker::checkPostObjCMessage(const ObjCMessage &Msg,
|
|||
if (!Summ)
|
||||
return;
|
||||
|
||||
checkSummary(*Summ, CallOrObjCMessage(Msg, state),
|
||||
InstanceReceiver(Msg, Pred->getLocationContext()), C);
|
||||
checkSummary(*Summ, CallOrObjCMessage(Msg, state), C);
|
||||
}
|
||||
|
||||
void RetainReleaseChecker::checkSummary(const RetainSummary &Summ,
|
||||
const CallOrObjCMessage &CallOrMsg,
|
||||
InstanceReceiver Receiver,
|
||||
CheckerContext &C) const {
|
||||
const ProgramState *state = C.getState();
|
||||
|
||||
|
@ -3084,13 +2889,15 @@ void RetainReleaseChecker::checkSummary(const RetainSummary &Summ,
|
|||
|
||||
// Evaluate the effect on the message receiver.
|
||||
bool ReceiverIsTracked = false;
|
||||
if (!hasErr && Receiver) {
|
||||
if (SymbolRef Sym = Receiver.getSValAsScalarOrLoc(state).getAsLocSymbol()) {
|
||||
if (!hasErr && CallOrMsg.isObjCMessage()) {
|
||||
const LocationContext *LC = C.getPredecessor()->getLocationContext();
|
||||
SVal Receiver = CallOrMsg.getInstanceMessageReceiver(LC);
|
||||
if (SymbolRef Sym = Receiver.getAsLocSymbol()) {
|
||||
if (const RefVal *T = state->get<RefBindings>(Sym)) {
|
||||
ReceiverIsTracked = true;
|
||||
state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(), hasErr);
|
||||
if (hasErr) {
|
||||
ErrorRange = Receiver.getSourceRange();
|
||||
ErrorRange = CallOrMsg.getReceiverSourceRange();
|
||||
ErrorSym = Sym;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -198,17 +198,6 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
|
|||
#endif
|
||||
|
||||
// Default semantics: invalidate all regions passed as arguments.
|
||||
SmallVector<const MemRegion*, 10> regionsToInvalidate;
|
||||
|
||||
// FIXME: We can have collisions on the conjured symbol if the
|
||||
// expression *I also creates conjured symbols. We probably want
|
||||
// to identify conjured symbols by an expression pair: the enclosing
|
||||
// expression (the context) and the expression itself. This should
|
||||
// disambiguate conjured symbols.
|
||||
unsigned blockCount = Builder->getCurrentBlockCount();
|
||||
|
||||
// NOTE: Even if RegionsToInvalidate is empty, we must still invalidate
|
||||
// global variables.
|
||||
ExplodedNodeSet destCall;
|
||||
|
||||
for (ExplodedNodeSet::iterator
|
||||
|
@ -216,23 +205,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
|
|||
i != e; ++i)
|
||||
{
|
||||
ExplodedNode *Pred = *i;
|
||||
const LocationContext *LC = Pred->getLocationContext();
|
||||
const ProgramState *state = Pred->getState();
|
||||
|
||||
// Accumulate list of regions that are invalidated.
|
||||
for (CXXConstructExpr::const_arg_iterator
|
||||
ai = E->arg_begin(), ae = E->arg_end();
|
||||
ai != ae; ++ai)
|
||||
{
|
||||
SVal val = state->getSVal(*ai);
|
||||
if (const MemRegion *region = val.getAsRegion())
|
||||
regionsToInvalidate.push_back(region);
|
||||
}
|
||||
|
||||
// Invalidate the regions.
|
||||
state = state->invalidateRegions(regionsToInvalidate,
|
||||
E, blockCount, 0,
|
||||
/* invalidateGlobals = */ true);
|
||||
|
||||
state = invalidateArguments(state, CallOrObjCMessage(E, state), LC);
|
||||
Builder->MakeNode(destCall, E, Pred, state);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/Analysis/Support/SaveAndRestore.h"
|
||||
|
||||
|
@ -63,6 +64,100 @@ void ExprEngine::processCallExit(CallExitNodeBuilder &B) {
|
|||
B.generateNode(state);
|
||||
}
|
||||
|
||||
const ProgramState *
|
||||
ExprEngine::invalidateArguments(const ProgramState *State,
|
||||
const CallOrObjCMessage &Call,
|
||||
const LocationContext *LC) {
|
||||
SmallVector<const MemRegion *, 8> RegionsToInvalidate;
|
||||
|
||||
if (Call.isObjCMessage()) {
|
||||
// Invalidate all instance variables of the receiver of an ObjC message.
|
||||
// FIXME: We should be able to do better with inter-procedural analysis.
|
||||
if (const MemRegion *MR = Call.getInstanceMessageReceiver(LC).getAsRegion())
|
||||
RegionsToInvalidate.push_back(MR);
|
||||
|
||||
} else if (Call.isCXXCall()) {
|
||||
// Invalidate all instance variables for the callee of a C++ method call.
|
||||
// FIXME: We should be able to do better with inter-procedural analysis.
|
||||
// FIXME: We can probably do better for const versus non-const methods.
|
||||
if (const MemRegion *Callee = Call.getCXXCallee().getAsRegion())
|
||||
RegionsToInvalidate.push_back(Callee);
|
||||
|
||||
} else if (Call.isFunctionCall()) {
|
||||
// Block calls invalidate all captured-by-reference values.
|
||||
if (const MemRegion *Callee = Call.getFunctionCallee().getAsRegion()) {
|
||||
if (isa<BlockDataRegion>(Callee))
|
||||
RegionsToInvalidate.push_back(Callee);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned idx = 0, e = Call.getNumArgs(); idx != e; ++idx) {
|
||||
SVal V = Call.getArgSVal(idx);
|
||||
|
||||
// If we are passing a location wrapped as an integer, unwrap it and
|
||||
// invalidate the values referred by the location.
|
||||
if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V))
|
||||
V = Wrapped->getLoc();
|
||||
else if (!isa<Loc>(V))
|
||||
continue;
|
||||
|
||||
if (const MemRegion *R = V.getAsRegion()) {
|
||||
// Invalidate the value of the variable passed by reference.
|
||||
|
||||
// Are we dealing with an ElementRegion? If the element type is
|
||||
// a basic integer type (e.g., char, int) and the underying region
|
||||
// is a variable region then strip off the ElementRegion.
|
||||
// FIXME: We really need to think about this for the general case
|
||||
// as sometimes we are reasoning about arrays and other times
|
||||
// about (char*), etc., is just a form of passing raw bytes.
|
||||
// e.g., void *p = alloca(); foo((char*)p);
|
||||
if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
|
||||
// Checking for 'integral type' is probably too promiscuous, but
|
||||
// we'll leave it in for now until we have a systematic way of
|
||||
// handling all of these cases. Eventually we need to come up
|
||||
// with an interface to StoreManager so that this logic can be
|
||||
// approriately delegated to the respective StoreManagers while
|
||||
// still allowing us to do checker-specific logic (e.g.,
|
||||
// invalidating reference counts), probably via callbacks.
|
||||
if (ER->getElementType()->isIntegralOrEnumerationType()) {
|
||||
const MemRegion *superReg = ER->getSuperRegion();
|
||||
if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) ||
|
||||
isa<ObjCIvarRegion>(superReg))
|
||||
R = cast<TypedRegion>(superReg);
|
||||
}
|
||||
// FIXME: What about layers of ElementRegions?
|
||||
}
|
||||
|
||||
// Mark this region for invalidation. We batch invalidate regions
|
||||
// below for efficiency.
|
||||
RegionsToInvalidate.push_back(R);
|
||||
} else {
|
||||
// Nuke all other arguments passed by reference.
|
||||
// FIXME: is this necessary or correct? This handles the non-Region
|
||||
// cases. Is it ever valid to store to these?
|
||||
State = State->unbindLoc(cast<Loc>(V));
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate designated regions using the batch invalidation API.
|
||||
|
||||
// FIXME: We can have collisions on the conjured symbol if the
|
||||
// expression *I also creates conjured symbols. We probably want
|
||||
// to identify conjured symbols by an expression pair: the enclosing
|
||||
// expression (the context) and the expression itself. This should
|
||||
// disambiguate conjured symbols.
|
||||
assert(Builder && "Invalidating arguments outside of a statement context");
|
||||
unsigned Count = Builder->getCurrentBlockCount();
|
||||
StoreManager::InvalidatedSymbols IS;
|
||||
|
||||
// NOTE: Even if RegionsToInvalidate is empty, we may still invalidate
|
||||
// global variables.
|
||||
return State->invalidateRegions(RegionsToInvalidate,
|
||||
Call.getOriginExpr(), Count,
|
||||
&IS, doesInvalidateGlobals(Call));
|
||||
|
||||
}
|
||||
|
||||
void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
|
||||
ExplodedNodeSet &dst) {
|
||||
// Perform the previsit of the CallExpr.
|
||||
|
@ -108,16 +203,19 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
|
|||
unsigned Count = Builder.getCurrentBlockCount();
|
||||
SVal RetVal = SVB.getConjuredSymbolVal(0, CE, ResultTy, Count);
|
||||
|
||||
// Generate a new ExplodedNode with the return value set.
|
||||
// Generate a new state with the return value set.
|
||||
state = state->BindExpr(CE, RetVal);
|
||||
Pred = Builder.generateNode(CE, state, Pred);
|
||||
|
||||
// Invalidate the arguments.
|
||||
const LocationContext *LC = Pred->getLocationContext();
|
||||
state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state), LC);
|
||||
|
||||
// Then handle everything else.
|
||||
unsigned oldSize = Dst.size();
|
||||
SaveOr OldHasGen(Builder.hasGeneratedNode);
|
||||
|
||||
// Dispatch to transfer function logic to handle the rest of the call.
|
||||
Eng.getTF().evalCall(Dst, Eng, Builder, CE, L, Pred);
|
||||
//Eng.getTF().evalCall(Dst, Eng, 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.
|
||||
|
|
|
@ -273,7 +273,12 @@ void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg,
|
|||
// Bind the return value.
|
||||
state = state->BindExpr(currentStmt, ReturnValue);
|
||||
|
||||
// Invalidate the arguments (and the receiver)
|
||||
const LocationContext *LC = Pred->getLocationContext();
|
||||
state = invalidateArguments(state, CallOrObjCMessage(msg, state), LC);
|
||||
Builder->MakeNode(Dst, msg.getOriginExpr(), Pred, state);
|
||||
|
||||
// Now we can handle the other aspects of the message.
|
||||
getTF().evalObjCMessage(Dst, *this, *Builder, msg, Pred, state);
|
||||
//getTF().evalObjCMessage(Dst, *this, *Builder, msg, Pred, state);
|
||||
}
|
||||
|
||||
|
|
|
@ -150,3 +150,9 @@ SVal CallOrObjCMessage::getCXXCallee() const {
|
|||
cast<CXXMemberCallExpr>(ActualCall)->getImplicitObjectArgument();
|
||||
return State->getSVal(callee);
|
||||
}
|
||||
|
||||
SVal
|
||||
CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const {
|
||||
assert(isObjCMessage());
|
||||
return Msg.getInstanceReceiverSVal(State, LC);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue