Newer
Older
// Create the "drain" selector.
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
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// RefBindings - State used to track object reference counts.
//===----------------------------------------------------------------------===//
typedef llvm::ImmutableMap<SymbolID, RefVal> RefBindings;
static int RefBIndex = 0;
namespace clang {
Ted Kremenek
committed
template<>
struct GRStateTrait<RefBindings> : public GRStatePartialTrait<RefBindings> {
static inline void* GDMIndex() { return &RefBIndex; }
};
}
Ted Kremenek
committed
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
Ted Kremenek
committed
UseAfterReleasesTy UseAfterReleases;
ReleasesNotOwnedTy ReleasesNotOwned;
LeaksTy Leaks;
RefBindings Update(RefBindings B, SymbolID sym, RefVal V, ArgEffect E,
Ted Kremenek
committed
RefVal::Kind& hasErr, RefBindings::Factory& RefBFactory);
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,
Ted Kremenek
committed
E, hasErr,
state.get_context<RefBindings>()));
Ted Kremenek
committed
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 RegisterPrinters(std::vector<GRState::Printer*>& Printers) {
Printers.push_back(new BindingsPrinter());
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();
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
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
Ted Kremenek
committed
if (lval::MemRegionVal* MR = dyn_cast<lval::MemRegionVal>(&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(*MR);
if (isa<lval::SymbolVal>(X)) {
SymbolID Sym = cast<lval::SymbolVal>(X).getSymbol();
Ted Kremenek
committed
state = state.remove<RefBindings>(Sym);
Ted Kremenek
committed
TypedRegion* R = dyn_cast<TypedRegion>(MR->getRegion());
if (R) {
// Set the value of the variable to be a conjured symbol.
unsigned Count = Builder.getCurrentBlockCount();
QualType T = R->getType();
SymbolID NewSym =
Eng.getSymbolManager().getConjuredSymbol(*I, T, Count);
state = state.SetRVal(*MR,
LVal::IsLValType(T)
? cast<RVal>(lval::SymbolVal(NewSym))
: cast<RVal>(nonlval::SymbolVal(NewSym)));
}
else
state = state.SetRVal(*MR, UnknownVal());
}
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
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));
break;
}
case RetEffect::ReceiverAlias: {
assert (Receiver);
Ted Kremenek
committed
RVal V = state.GetRVal(Receiver);
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));
Ted Kremenek
committed
#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));
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
// 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;
Ted Kremenek
committed
if (!isa<lval::MemRegionVal>(TargetLV))
Ted Kremenek
committed
escapes = true;
Ted Kremenek
committed
else {
MemRegion* R = cast<lval::MemRegionVal>(TargetLV).getRegion();
escapes = !Eng.getStateManager().hasStackStorage(R);
}
Ted Kremenek
committed
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.
Ted Kremenek
committed
state = state.remove<RefBindings>(Sym);
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);
Ted Kremenek
committed
return state.set<RefBindings>(sid, V ^ RefVal::ErrorLeak);
}
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);
Ted Kremenek
committed
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;
Ted Kremenek
committed
GRStateRef state(St, VMgr);
RefBindings::Factory& RefBFactory = state.get_context<RefBindings>();
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 (VMgr.getSymVal(St, I.getKey())) {
changed = true;
B = RefBFactory.Remove(B, I.getKey());
}
}
Ted Kremenek
committed
if (changed)
state = state.set<RefBindings>(B);
Ted Kremenek
committed
return state;
}
Ted Kremenek
committed
RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
RefVal V, ArgEffect E,
Ted Kremenek
committed
RefVal::Kind& hasErr,
RefBindings::Factory& RefBFactory) {
// 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:
switch (V.getKind()) {
default:
assert (false);
Ted Kremenek
committed
case RefVal::Owned:
V = V.getCount() > 1 ? V - 1 : V ^ RefVal::Released;
break;
case RefVal::NotOwned:
if (V.getCount() > 0)
V = V - 1;
hasErr = V.getKind();
Ted Kremenek
committed
}