Newer
Older
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef);
addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ);
// Create the "autorelease" selector.
Ted Kremenek
committed
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : Autorelease);
addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ);
// For NSWindow, allocated objects are (initially) self-owned.
RetainSummary *NSWindowSumm =
getPersistentSummary(RetEffect::MakeReceiverAlias(), SelfOwn);
addInstMethSummary("NSWindow", NSWindowSumm, "initWithContentRect",
"styleMask", "backing", "defer", NULL);
addInstMethSummary("NSWindow", NSWindowSumm, "initWithContentRect",
"styleMask", "backing", "defer", "screen", NULL);
Ted Kremenek
committed
// For NSPanel (which subclasses NSWindow), allocated objects are not
// self-owned.
addInstMethSummary("NSPanel", InitSumm, "initWithContentRect",
"styleMask", "backing", "defer", NULL);
Ted Kremenek
committed
addInstMethSummary("NSPanel", InitSumm, "initWithContentRect",
"styleMask", "backing", "defer", "screen", NULL);
// Create NSAssertionHandler summaries.
Ted Kremenek
committed
addPanicSummary("NSAssertionHandler", "handleFailureInFunction", "file",
"lineNumber", "description", NULL);
addPanicSummary("NSAssertionHandler", "handleFailureInMethod", "object",
"file", "lineNumber", "description", NULL);
Ted Kremenek
committed
}
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
// Reference-counting logic (typestate + counts).
//===----------------------------------------------------------------------===//
namespace {
class VISIBILITY_HIDDEN RefVal {
public:
enum Kind {
Owned = 0, // Owning reference.
NotOwned, // Reference is not owned by still valid (not freed).
Released, // Object has been released.
ReturnedOwned, // Returned object passes ownership to caller.
ReturnedNotOwned, // Return object does not pass ownership to caller.
ErrorUseAfterRelease, // Object used after released.
ErrorReleaseNotOwned, // Release of an object that was not owned.
ErrorLeak // A memory leak due to excessive reference counts.
};
private:
Kind kind;
unsigned Cnt;
QualType T;
RefVal(Kind k, unsigned cnt, QualType t) : kind(k), Cnt(cnt), T(t) {}
RefVal(Kind k, unsigned cnt = 0) : kind(k), Cnt(cnt) {}
public:
Kind getKind() const { return kind; }
unsigned getCount() const { return Cnt; }
QualType getType() const { return T; }
// Useful predicates.
static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
static bool isLeak(Kind k) { return k == ErrorLeak; }
Ted Kremenek
committed
bool isOwned() const {
return getKind() == Owned;
}
bool isNotOwned() const {
return getKind() == NotOwned;
}
bool isReturnedOwned() const {
return getKind() == ReturnedOwned;
}
bool isReturnedNotOwned() const {
return getKind() == ReturnedNotOwned;
}
bool isNonLeakError() const {
Kind k = getKind();
return isError(k) && !isLeak(k);
}
// State creation: normal state.
static RefVal makeOwned(QualType t, unsigned Count = 1) {
return RefVal(Owned, Count, t);
static RefVal makeNotOwned(QualType t, unsigned Count = 0) {
return RefVal(NotOwned, Count, t);
static RefVal makeReturnedOwned(unsigned Count) {
return RefVal(ReturnedOwned, Count);
}
static RefVal makeReturnedNotOwned() {
return RefVal(ReturnedNotOwned);
}
// Comparison, profiling, and pretty-printing.
bool operator==(const RefVal& X) const {
}
Ted Kremenek
committed
RefVal operator-(size_t i) const {
return RefVal(getKind(), getCount() - i, getType());
}
RefVal operator+(size_t i) const {
return RefVal(getKind(), getCount() + i, getType());
}
RefVal operator^(Kind k) const {
return RefVal(k, getCount(), getType());
}
void Profile(llvm::FoldingSetNodeID& ID) const {
ID.AddInteger((unsigned) kind);
ID.AddInteger(Cnt);
}
Ted Kremenek
committed
void print(std::ostream& Out) const;
Ted Kremenek
committed
void RefVal::print(std::ostream& Out) const {
if (!T.isNull())
Out << "Tracked Type:" << T.getAsString() << '\n';
Ted Kremenek
committed
switch (getKind()) {
default: assert(false);
case Owned: {
Out << "Owned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
Ted Kremenek
committed
break;
Ted Kremenek
committed
Out << "NotOwned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
break;
}
case ReturnedOwned: {
Out << "ReturnedOwned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
Ted Kremenek
committed
break;
Ted Kremenek
committed
case ReturnedNotOwned: {
Out << "ReturnedNotOwned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
break;
}
Ted Kremenek
committed
case Released:
Out << "Released";
break;
case ErrorLeak:
Out << "Leaked";
break;
Ted Kremenek
committed
case ErrorUseAfterRelease:
Out << "Use-After-Release [ERROR]";
break;
case ErrorReleaseNotOwned:
Out << "Release of Not-Owned [ERROR]";
break;
}
}
Ted Kremenek
committed
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// RefBindings - State used to track object reference counts.
//===----------------------------------------------------------------------===//
typedef llvm::ImmutableMap<SymbolID, RefVal> RefBindings;
typedef RefBindings::Factory RefBFactoryTy;
static int RefBIndex = 0;
namespace clang {
template<> struct GRStateTrait<RefBindings> {
typedef RefBindings data_type;
typedef RefBFactoryTy& context_type;
typedef SymbolID key_type;
typedef RefVal value_type;
typedef const RefVal* lookup_type;
static RefBindings MakeData(void* const* p) {
return p ? RefBindings((RefBindings::TreeTy*) *p) : RefBindings(0);
}
static void* MakeVoidPtr(RefBindings B) {
return B.getRoot();
}
static void* GDMIndex() {
return &RefBIndex;
}
static lookup_type Lookup(RefBindings B, SymbolID K) {
return B.lookup(K);
}
static data_type Set(RefBindings B, key_type K, value_type E,
RefBFactoryTy& F) {
return F.Add(B, K, E);
}
static data_type Remove(RefBindings B, SymbolID K, RefBFactoryTy& F) {
return F.Remove(B, K);
}
};
}
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Transfer functions.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
namespace {
class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
public:
typedef llvm::DenseMap<GRExprEngine::NodeTy*,std::pair<Expr*, SymbolID> >
ReleasesNotOwnedTy;
Ted Kremenek
committed
typedef ReleasesNotOwnedTy UseAfterReleasesTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*, std::vector<SymbolID>*>
LeaksTy;
class BindingsPrinter : public GRState::Printer {
Ted Kremenek
committed
public:
virtual void Print(std::ostream& Out, const GRState* state,
const char* nl, const char* sep);
Ted Kremenek
committed
};
private:
RetainSummaryManager Summaries;
const LangOptions& LOpts;
Ted Kremenek
committed
RefBFactoryTy RefBFactory;
UseAfterReleasesTy UseAfterReleases;
ReleasesNotOwnedTy ReleasesNotOwned;
LeaksTy Leaks;
BindingsPrinter Printer;
RefBindings Update(RefBindings B, SymbolID sym, RefVal V, ArgEffect E,
RefVal::Kind& hasErr);
Ted Kremenek
committed
RefVal::Kind& Update(GRStateRef& state, SymbolID sym, RefVal V,
ArgEffect E, RefVal::Kind& hasErr) {
state = state.set<RefBindings>(Update(state.get<RefBindings>(), sym, V,
E, hasErr));
return hasErr;
}
void ProcessNonLeakError(ExplodedNodeSet<GRState>& Dst,
GRStmtNodeBuilder<GRState>& Builder,
Expr* NodeExpr, Expr* ErrorExpr,
ExplodedNode<GRState>* Pred,
const GRState* St,
RefVal::Kind hasErr, SymbolID Sym);
const GRState* HandleSymbolDeath(GRStateManager& VMgr,
const GRState* St,
SymbolID sid, RefVal V, bool& hasLeak);
Ted Kremenek
committed
Ted Kremenek
committed
CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts)
: Summaries(Ctx, gcenabled),
Ted Kremenek
committed
LOpts(lopts) {}
virtual ~CFRefCount() {
for (LeaksTy::iterator I = Leaks.begin(), E = Leaks.end(); I!=E; ++I)
delete I->second;
}
virtual void RegisterChecks(GRExprEngine& Eng);
Ted Kremenek
committed
virtual void getStatePrinters(std::vector<GRState::Printer*>& Printers) {
Printers.push_back(&Printer);
Ted Kremenek
committed
}
bool isGCEnabled() const { return Summaries.isGCEnabled(); }
const LangOptions& getLangOptions() const { return LOpts; }
void EvalSummary(ExplodedNodeSet<GRState>& Dst,
GRExprEngine& Eng,
Expr* Ex,
Expr* Receiver,
RetainSummary* Summ,
ExprIterator arg_beg, ExprIterator arg_end,
virtual void EvalCall(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
Ted Kremenek
committed
virtual void EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Engine,
Ted Kremenek
committed
ObjCMessageExpr* ME,
Ted Kremenek
committed
bool EvalObjCMessageExprAux(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Engine,
Ted Kremenek
committed
ObjCMessageExpr* ME,
Ted Kremenek
committed
Ted Kremenek
committed
// Stores.
virtual void EvalStore(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Engine,
GRStmtNodeBuilder<GRState>& Builder,
Expr* E, ExplodedNode<GRState>* Pred,
const GRState* St, RVal TargetLV, RVal Val);
Ted Kremenek
committed
// End-of-path.
virtual void EvalEndPath(GRExprEngine& Engine,
Ted Kremenek
committed
virtual void EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Engine,
GRStmtNodeBuilder<GRState>& Builder,
ExplodedNode<GRState>* Pred,
const GRState* St,
const GRStateManager::DeadSymbolsTy& Dead);
// Return statements.
virtual void EvalReturn(ExplodedNodeSet<GRState>& Dst,
GRExprEngine& Engine,
ReturnStmt* S,
// Assumptions.
virtual const GRState* EvalAssume(GRStateManager& VMgr,
const GRState* St, RVal Cond,
bool Assumption, bool& isFeasible);
Ted Kremenek
committed
// Error iterators.
typedef UseAfterReleasesTy::iterator use_after_iterator;
typedef ReleasesNotOwnedTy::iterator bad_release_iterator;
typedef LeaksTy::iterator leaks_iterator;
Ted Kremenek
committed
use_after_iterator use_after_begin() { return UseAfterReleases.begin(); }
use_after_iterator use_after_end() { return UseAfterReleases.end(); }
Ted Kremenek
committed
bad_release_iterator bad_release_begin() { return ReleasesNotOwned.begin(); }
bad_release_iterator bad_release_end() { return ReleasesNotOwned.end(); }
leaks_iterator leaks_begin() { return Leaks.begin(); }
leaks_iterator leaks_end() { return Leaks.end(); }
};
} // end anonymous namespace
void CFRefCount::BindingsPrinter::Print(std::ostream& Out, const GRState* state,
const char* nl, const char* sep) {
Ted Kremenek
committed
RefBindings B = state->get<RefBindings>();
Ted Kremenek
committed
if (!B.isEmpty())
Ted Kremenek
committed
Out << sep << nl;
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
Out << (*I).first << " : ";
(*I).second.print(Out);
Out << nl;
}
}
static inline ArgEffect GetArgE(RetainSummary* Summ, unsigned idx) {
Ted Kremenek
committed
return Summ ? Summ->getArg(idx) : MayEscape;
static inline RetEffect GetRetEffect(RetainSummary* Summ) {
return Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet();
static inline ArgEffect GetReceiverE(RetainSummary* Summ) {
return Summ ? Summ->getReceiverEffect() : DoNothing;
}
static inline bool IsEndPath(RetainSummary* Summ) {
return Summ ? Summ->isEndPath() : false;
}
void CFRefCount::ProcessNonLeakError(ExplodedNodeSet<GRState>& Dst,
GRStmtNodeBuilder<GRState>& Builder,
Expr* NodeExpr, Expr* ErrorExpr,
ExplodedNode<GRState>* Pred,
const GRState* St,
RefVal::Kind hasErr, SymbolID Sym) {
Builder.BuildSinks = true;
GRExprEngine::NodeTy* N = Builder.MakeNode(Dst, NodeExpr, Pred, St);
if (!N) return;
switch (hasErr) {
default: assert(false);
case RefVal::ErrorUseAfterRelease:
UseAfterReleases[N] = std::make_pair(ErrorExpr, Sym);
break;
case RefVal::ErrorReleaseNotOwned:
ReleasesNotOwned[N] = std::make_pair(ErrorExpr, Sym);
break;
}
}
/// GetReturnType - Used to get the return type of a message expression or
/// function call with the intention of affixing that type to a tracked symbol.
/// While the the return type can be queried directly from RetEx, when
/// invoking class methods we augment to the return type to be that of
/// a pointer to the class (as opposed it just being id).
static QualType GetReturnType(Expr* RetE, ASTContext& Ctx) {
QualType RetTy = RetE->getType();
// FIXME: We aren't handling id<...>.
const PointerType* PT = RetTy->getAsPointerType();
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
if (!PT)
return RetTy;
// If RetEx is not a message expression just return its type.
// If RetEx is a message expression, return its types if it is something
/// more specific than id.
ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(RetE);
if (!ME || !Ctx.isObjCIdType(PT->getPointeeType()))
return RetTy;
ObjCInterfaceDecl* D = ME->getClassInfo().first;
// At this point we know the return type of the message expression is id.
// If we have an ObjCInterceDecl, we know this is a call to a class method
// whose type we can resolve. In such cases, promote the return type to
// Class*.
return !D ? RetTy : Ctx.getPointerType(Ctx.getObjCInterfaceType(D));
}
void CFRefCount::EvalSummary(ExplodedNodeSet<GRState>& Dst,
GRExprEngine& Eng,
Expr* Ex,
Expr* Receiver,
RetainSummary* Summ,
ExprIterator arg_beg, ExprIterator arg_end,
// Get the state.
Ted Kremenek
committed
GRStateRef state(Builder.GetState(Pred), Eng.getStateManager());
// Evaluate the effect of the arguments.
RefVal::Kind hasErr = (RefVal::Kind) 0;
unsigned idx = 0;
Expr* ErrorExpr = NULL;
SymbolID ErrorSym = 0;
Ted Kremenek
committed
for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) {
RVal V = state.GetRVal(*I);
if (isa<lval::SymbolVal>(V)) {
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
Ted Kremenek
committed
if (RefBindings::data_type* T = state.get<RefBindings>(Sym))
if (Update(state, Sym, *T, GetArgE(Summ, idx), hasErr)) {
ErrorExpr = *I;
Ted Kremenek
committed
ErrorSym = Sym;
break;
}
else if (isa<LVal>(V)) {
#if 0
// Nuke all arguments passed by reference.
StateMgr.Unbind(StVals, cast<LVal>(V));
#else
if (lval::DeclVal* DV = dyn_cast<lval::DeclVal>(&V)) {
if (GetArgE(Summ, idx) == DoNothingByRef)
continue;
// Invalidate the value of the variable passed by reference.
// FIXME: Either this logic should also be replicated in GRSimpleVals
// or should be pulled into a separate "constraint engine."
// 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.
// Is the invalidated variable something that we were tracking?
Ted Kremenek
committed
RVal X = state.GetRVal(*DV);
if (isa<lval::SymbolVal>(X)) {
SymbolID Sym = cast<lval::SymbolVal>(X).getSymbol();
Ted Kremenek
committed
state = state.remove<RefBindings>(Sym, RefBFactory);
// Set the value of the variable to be a conjured symbol.
unsigned Count = Builder.getCurrentBlockCount();
SymbolID NewSym = Eng.getSymbolManager().getConjuredSymbol(*I, Count);
Ted Kremenek
committed
state = state.SetRVal(*DV,
LVal::IsLValType(DV->getDecl()->getType())
? cast<RVal>(lval::SymbolVal(NewSym))
: cast<RVal>(nonlval::SymbolVal(NewSym)));
}
else {
// Nuke all other arguments passed by reference.
Ted Kremenek
committed
state = state.Unbind(cast<LVal>(V));
}
#endif
else if (isa<nonlval::LValAsInteger>(V))
Ted Kremenek
committed
state = state.Unbind(cast<nonlval::LValAsInteger>(V).getLVal());
Ted Kremenek
committed
RVal V = state.GetRVal(Receiver);
if (isa<lval::SymbolVal>(V)) {
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
Ted Kremenek
committed
if (const RefVal* T = state.get<RefBindings>(Sym))
if (Update(state, Sym, *T, GetReceiverE(Summ), hasErr)) {
Ted Kremenek
committed
ErrorSym = Sym;
if (hasErr) {
Ted Kremenek
committed
ProcessNonLeakError(Dst, Builder, Ex, ErrorExpr, Pred, state,
hasErr, ErrorSym);
// Consult the summary for the return value.
RetEffect RE = GetRetEffect(Summ);
switch (RE.getKind()) {
default:
assert (false && "Unhandled RetEffect."); break;
Ted Kremenek
committed
case RetEffect::NoRet:
// Make up a symbol for the return value (not reference counted).
// FIXME: This is basically copy-and-paste from GRSimpleVals. We
// should compose behavior, not copy it.
if (Ex->getType() != Eng.getContext().VoidTy) {
unsigned Count = Builder.getCurrentBlockCount();
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
Ted Kremenek
committed
RVal X = LVal::IsLValType(Ex->getType())
? cast<RVal>(lval::SymbolVal(Sym))
: cast<RVal>(nonlval::SymbolVal(Sym));
Ted Kremenek
committed
state = state.SetRVal(Ex, X, Eng.getCFG().isBlkExpr(Ex), false);
Ted Kremenek
committed
break;
case RetEffect::Alias: {
assert (arg_end >= arg_beg);
assert (idx < (unsigned) (arg_end - arg_beg));
Ted Kremenek
committed
RVal V = state.GetRVal(*(arg_beg+idx));
state = state.SetRVal(Ex, V, Eng.getCFG().isBlkExpr(Ex), false);
break;
}
case RetEffect::ReceiverAlias: {
assert (Receiver);
Ted Kremenek
committed
RVal V = state.GetRVal(Receiver);
state = state.SetRVal(Ex, V, Eng.getCFG().isBlkExpr(Ex), false);
case RetEffect::OwnedAllocatedSymbol:
case RetEffect::OwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
Ted Kremenek
committed
state = state.set<RefBindings>(Sym, RefVal::makeOwned(RetT), RefBFactory);
state = state.SetRVal(Ex, lval::SymbolVal(Sym),
Eng.getCFG().isBlkExpr(Ex), false);
#if 0
RefBindings B = GetRefBindings(StImpl);
SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeOwned(RetT)));
Ted Kremenek
committed
#endif
// FIXME: Add a flag to the checker where allocations are allowed to fail.
if (RE.getKind() == RetEffect::OwnedAllocatedSymbol)
Ted Kremenek
committed
state = state.AddNE(Sym, Eng.getBasicVals().getZeroWithPtrWidth());
break;
}
case RetEffect::NotOwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
Ted Kremenek
committed
state = state.set<RefBindings>(Sym, RefVal::makeNotOwned(RetT), RefBFactory);
state = state.SetRVal(Ex, lval::SymbolVal(Sym),
Eng.getCFG().isBlkExpr(Ex), false);
break;
}
}
// Is this a sink?
if (IsEndPath(Summ))
Ted Kremenek
committed
Builder.MakeSinkNode(Dst, Ex, Pred, state);
Ted Kremenek
committed
Builder.MakeNode(Dst, Ex, Pred, state);
}
void CFRefCount::EvalCall(ExplodedNodeSet<GRState>& Dst,
GRExprEngine& Eng,
CallExpr* CE, RVal L,
Ted Kremenek
committed
RetainSummary* Summ = !isa<lval::FuncVal>(L) ? 0
: Summaries.getSummary(cast<lval::FuncVal>(L).getDecl());
EvalSummary(Dst, Eng, Builder, CE, 0, Summ,
CE->arg_begin(), CE->arg_end(), Pred);
Ted Kremenek
committed
}
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
Ted Kremenek
committed
ObjCMessageExpr* ME,
RetainSummary* Summ;
Ted Kremenek
committed
if (Expr* Receiver = ME->getReceiver()) {
// We need the type-information of the tracked receiver object
// Retrieve it from the state.
ObjCInterfaceDecl* ID = 0;
// FIXME: Wouldn't it be great if this code could be reduced? It's just
// a chain of lookups.
const GRState* St = Builder.GetState(Pred);
RVal V = Eng.getStateManager().GetRVal(St, Receiver );
if (isa<lval::SymbolVal>(V)) {
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
Ted Kremenek
committed
if (const RefVal* T = St->get<RefBindings>(Sym)) {
Ted Kremenek
committed
QualType Ty = T->getType();
if (const PointerType* PT = Ty->getAsPointerType()) {
QualType PointeeTy = PT->getPointeeType();
if (ObjCInterfaceType* IT = dyn_cast<ObjCInterfaceType>(PointeeTy))
ID = IT->getDecl();
}
}
}
Summ = Summaries.getMethodSummary(ME, ID);
}
else
Ted Kremenek
committed
Summ = Summaries.getClassMethodSummary(ME->getClassName(),
ME->getSelector());
EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), Summ,
ME->arg_begin(), ME->arg_end(), Pred);
Ted Kremenek
committed
}
Ted Kremenek
committed
// Stores.
void CFRefCount::EvalStore(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
GRStmtNodeBuilder<GRState>& Builder,
Expr* E, ExplodedNode<GRState>* Pred,
const GRState* St, RVal TargetLV, RVal Val) {
Ted Kremenek
committed
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
// Check if we have a binding for "Val" and if we are storing it to something
// we don't understand or otherwise the value "escapes" the function.
if (!isa<lval::SymbolVal>(Val))
return;
// Are we storing to something that causes the value to "escape"?
bool escapes = false;
if (!isa<lval::DeclVal>(TargetLV))
escapes = true;
else
escapes = cast<lval::DeclVal>(TargetLV).getDecl()->hasGlobalStorage();
if (!escapes)
return;
SymbolID Sym = cast<lval::SymbolVal>(Val).getSymbol();
Ted Kremenek
committed
GRStateRef state(St, Eng.getStateManager());
Ted Kremenek
committed
Ted Kremenek
committed
if (!state.get<RefBindings>(Sym))
return;
Ted Kremenek
committed
Ted Kremenek
committed
// Nuke the binding.
state = state.remove<RefBindings>(Sym, RefBFactory);
Ted Kremenek
committed
Ted Kremenek
committed
// Hand of the remaining logic to the parent implementation.
GRSimpleVals::EvalStore(Dst, Eng, Builder, E, Pred, state, TargetLV, Val);
}
Ted Kremenek
committed
// End-of-path.
const GRState* CFRefCount::HandleSymbolDeath(GRStateManager& VMgr,
const GRState* St, SymbolID sid,
RefVal V, bool& hasLeak) {
hasLeak = V.isOwned() ||
((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0);
Ted Kremenek
committed
GRStateRef state(St, VMgr);
if (!hasLeak)
Ted Kremenek
committed
return state.remove<RefBindings>(sid, RefBFactory);
Ted Kremenek
committed
return state.set<RefBindings>(sid, V ^ RefVal::ErrorLeak, RefBFactory);
}
void CFRefCount::EvalEndPath(GRExprEngine& Eng,
Ted Kremenek
committed
Ted Kremenek
committed
RefBindings B = St->get<RefBindings>();
Ted Kremenek
committed
llvm::SmallVector<SymbolID, 10> Leaked;
Ted Kremenek
committed
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
bool hasLeak = false;
Ted Kremenek
committed
St = HandleSymbolDeath(Eng.getStateManager(), St,
(*I).first, (*I).second, hasLeak);
if (hasLeak) Leaked.push_back((*I).first);
}
Ted Kremenek
committed
if (Leaked.empty())
return;
ExplodedNode<GRState>* N = Builder.MakeNode(St);
Ted Kremenek
committed
Ted Kremenek
committed
if (!N)
Ted Kremenek
committed
return;
std::vector<SymbolID>*& LeaksAtNode = Leaks[N];
assert (!LeaksAtNode);
LeaksAtNode = new std::vector<SymbolID>();
for (llvm::SmallVector<SymbolID, 10>::iterator I=Leaked.begin(),
E = Leaked.end(); I != E; ++I)
(*LeaksAtNode).push_back(*I);
Ted Kremenek
committed
}
Ted Kremenek
committed
// Dead symbols.
void CFRefCount::EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
GRStmtNodeBuilder<GRState>& Builder,
ExplodedNode<GRState>* Pred,
const GRState* St,
const GRStateManager::DeadSymbolsTy& Dead) {
Ted Kremenek
committed
// FIXME: a lot of copy-and-paste from EvalEndPath. Refactor.
Ted Kremenek
committed
RefBindings B = St->get<RefBindings>();
Ted Kremenek
committed
llvm::SmallVector<SymbolID, 10> Leaked;
for (GRStateManager::DeadSymbolsTy::const_iterator
Ted Kremenek
committed
I=Dead.begin(), E=Dead.end(); I!=E; ++I) {
Ted Kremenek
committed
const RefVal* T = B.lookup(*I);
Ted Kremenek
committed
if (!T)
continue;
bool hasLeak = false;
Ted Kremenek
committed
St = HandleSymbolDeath(Eng.getStateManager(), St, *I, *T, hasLeak);
Ted Kremenek
committed
Ted Kremenek
committed
if (hasLeak)
Leaked.push_back(*I);
Ted Kremenek
committed
}
if (Leaked.empty())
return;
ExplodedNode<GRState>* N = Builder.MakeNode(Dst, S, Pred, St);
Ted Kremenek
committed
if (!N)
return;
std::vector<SymbolID>*& LeaksAtNode = Leaks[N];
assert (!LeaksAtNode);
LeaksAtNode = new std::vector<SymbolID>();
for (llvm::SmallVector<SymbolID, 10>::iterator I=Leaked.begin(),
E = Leaked.end(); I != E; ++I)
(*LeaksAtNode).push_back(*I);
}
// Return statements.
void CFRefCount::EvalReturn(ExplodedNodeSet<GRState>& Dst,
GRExprEngine& Eng,
ReturnStmt* S,
Expr* RetE = S->getRetValue();
if (!RetE) return;
Ted Kremenek
committed
GRStateRef state(Builder.GetState(Pred), Eng.getStateManager());
RVal V = state.GetRVal(RetE);
if (!isa<lval::SymbolVal>(V))
return;
// Get the reference count binding (if any).
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
Ted Kremenek
committed
const RefVal* T = state.get<RefBindings>(Sym);
if (!T)
return;
Ted Kremenek
committed
// Change the reference count.
Ted Kremenek
committed
RefVal X = *T;
Ted Kremenek
committed
switch (X.getKind()) {
case RefVal::Owned: {
unsigned cnt = X.getCount();
Ted Kremenek
committed
assert (cnt > 0);
X = RefVal::makeReturnedOwned(cnt - 1);
break;
}
case RefVal::NotOwned: {
unsigned cnt = X.getCount();
X = cnt ? RefVal::makeReturnedOwned(cnt - 1)
: RefVal::makeReturnedNotOwned();
break;
}
default:
return;
}
// Update the binding.
Ted Kremenek
committed
state = state.set<RefBindings>(Sym, X, RefBFactory);
Builder.MakeNode(Dst, S, Pred, state);
}
// Assumptions.
const GRState* CFRefCount::EvalAssume(GRStateManager& VMgr,
const GRState* St,
RVal Cond, bool Assumption,
bool& isFeasible) {
// FIXME: We may add to the interface of EvalAssume the list of symbols
// whose assumptions have changed. For now we just iterate through the
// bindings and check if any of the tracked symbols are NULL. This isn't
// too bad since the number of symbols we will track in practice are
// probably small and EvalAssume is only called at branches and a few
// other places.
Ted Kremenek
committed
RefBindings B = St->get<RefBindings>();
if (B.isEmpty())
return St;
bool changed = false;
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
// Check if the symbol is null (or equal to any constant).
// If this is the case, stop tracking the symbol.
if (St->getSymVal(I.getKey())) {
changed = true;
B = RefBFactory.Remove(B, I.getKey());
}
}
if (!changed)
return St;
Ted Kremenek
committed
GRStateRef state(St, VMgr);
state = state.set<RefBindings>(B);
return state;
}
Ted Kremenek
committed
RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
RefVal V, ArgEffect E,
RefVal::Kind& hasErr) {
// FIXME: This dispatch can potentially be sped up by unifiying it into
// a single switch statement. Opt for simplicity for now.
switch (E) {
default:
assert (false && "Unhandled CFRef transition.");
Ted Kremenek
committed
case MayEscape:
if (V.getKind() == RefVal::Owned) {
Ted Kremenek
committed
break;
}
// Fall-through.
case DoNothing:
if (!isGCEnabled() && V.getKind() == RefVal::Released) {
hasErr = V.getKind();
Ted Kremenek
committed
}
return B;
Ted Kremenek
committed
Ted Kremenek
committed
case Autorelease:
case StopTracking:
return RefBFactory.Remove(B, sym);
Ted Kremenek
committed
case IncRef:
switch (V.getKind()) {
default:
assert(false);
case RefVal::Owned:
case RefVal::NotOwned:
Ted Kremenek
committed
break;
case RefVal::Released:
if (isGCEnabled())
break;
Ted Kremenek
committed
}
Ted Kremenek
committed
break;
Ted Kremenek
committed
// Fall-through.
case DecRef: