Newer
Older
// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--//
Ted Kremenek
committed
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the methods for CFRefCount, which implements
Ted Kremenek
committed
// a reference count checker for Core Foundation (Mac OS X).
//
//===----------------------------------------------------------------------===//
#include "GRSimpleVals.h"
#include "clang/Basic/LangOptions.h"
Ted Kremenek
committed
#include "clang/Basic/SourceManager.h"
Ted Kremenek
committed
#include "clang/Analysis/PathSensitive/ValueState.h"
Ted Kremenek
committed
#include "clang/Analysis/PathDiagnostic.h"
Ted Kremenek
committed
#include "clang/Analysis/LocalCheckers.h"
Ted Kremenek
committed
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Analysis/PathSensitive/BugReporter.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableMap.h"
Ted Kremenek
committed
#include "llvm/Support/Compiler.h"
Ted Kremenek
committed
#include <ostream>
Ted Kremenek
committed
using namespace clang;
//===----------------------------------------------------------------------===//
// Utility functions.
//===----------------------------------------------------------------------===//
static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) {
IdentifierInfo* II = &Ctx.Idents.get(name);
return Ctx.Selectors.getSelector(0, &II);
}
static inline Selector GetUnarySelector(const char* name, ASTContext& Ctx) {
IdentifierInfo* II = &Ctx.Idents.get(name);
return Ctx.Selectors.getSelector(1, &II);
}
//===----------------------------------------------------------------------===//
// Symbolic Evaluation of Reference Counting Logic
//===----------------------------------------------------------------------===//
enum ArgEffect { IncRef, DecRef, DoNothing, StopTracking };
Ted Kremenek
committed
typedef std::vector<std::pair<unsigned,ArgEffect> > ArgEffects;
Ted Kremenek
committed
namespace llvm {
template <> struct FoldingSetTrait<ArgEffects> {
static void Profile(const ArgEffects& X, FoldingSetNodeID& ID) {
Ted Kremenek
committed
for (ArgEffects::const_iterator I = X.begin(), E = X.end(); I!= E; ++I) {
ID.AddInteger(I->first);
ID.AddInteger((unsigned) I->second);
}
};
} // end llvm namespace
namespace {
class RetEffect {
public:
Ted Kremenek
committed
enum Kind { NoRet = 0x0, Alias = 0x1, OwnedSymbol = 0x2,
NotOwnedSymbol = 0x3, ReceiverAlias=0x4 };
private:
unsigned Data;
RetEffect(Kind k, unsigned D) { Data = (D << 3) | (unsigned) k; }
Kind getKind() const { return (Kind) (Data & 0x7); }
unsigned getValue() const {
assert(getKind() == Alias);
return Data >> 3;
Ted Kremenek
committed
static RetEffect MakeAlias(unsigned Idx) { return RetEffect(Alias, Idx); }
static RetEffect MakeReceiverAlias() { return RetEffect(ReceiverAlias, 0); }
static RetEffect MakeOwned() { return RetEffect(OwnedSymbol, 0); }
static RetEffect MakeNotOwned() { return RetEffect(NotOwnedSymbol, 0); }
Ted Kremenek
committed
static RetEffect MakeNoRet() { return RetEffect(NoRet, 0); }
operator Kind() const { return getKind(); }
void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger(Data); }
};
class RetainSummary : public llvm::FoldingSetNode {
Ted Kremenek
committed
/// Args - an ordered vector of (index, ArgEffect) pairs, where index
/// specifies the argument (starting from 0). This can be sparsely
/// populated; arguments with no entry in Args use 'DefaultArgEffect'.
ArgEffects* Args;
Ted Kremenek
committed
/// DefaultArgEffect - The default ArgEffect to apply to arguments that
/// do not have an entry in Args.
ArgEffect DefaultArgEffect;
RetEffect Ret;
public:
Ted Kremenek
committed
RetainSummary(ArgEffects* A, RetEffect R, ArgEffect defaultEff,
ArgEffect ReceiverEff)
: Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R) {}
ArgEffect getArg(unsigned idx) const {
Ted Kremenek
committed
Ted Kremenek
committed
if (!Args)
Ted Kremenek
committed
return DefaultArgEffect;
Ted Kremenek
committed
// If Args is present, it is likely to contain only 1 element.
// Just do a linear search. Do it from the back because functions with
// large numbers of arguments will be tail heavy with respect to which
// argument they actually modify with respect to the reference count.
for (ArgEffects::reverse_iterator I=Args->rbegin(), E=Args->rend();
I!=E; ++I) {
if (idx > I->first)
Ted Kremenek
committed
return DefaultArgEffect;
Ted Kremenek
committed
if (idx == I->first)
return I->second;
}
Ted Kremenek
committed
return DefaultArgEffect;
RetEffect getRetEffect() const {
return Ret;
}
ArgEffect getReceiverEffect() const {
return Receiver;
}
typedef ArgEffects::const_iterator arg_iterator;
arg_iterator begin_args() const { return Args->begin(); }
arg_iterator end_args() const { return Args->end(); }
Ted Kremenek
committed
static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects* A,
Ted Kremenek
committed
RetEffect RetEff, ArgEffect DefaultEff,
ArgEffect ReceiverEff) {
ID.AddPointer(A);
Ted Kremenek
committed
ID.AddInteger((unsigned) DefaultEff);
ID.AddInteger((unsigned) ReceiverEff);
}
void Profile(llvm::FoldingSetNodeID& ID) const {
Ted Kremenek
committed
Profile(ID, Args, Ret, DefaultArgEffect, Receiver);
Ted Kremenek
committed
class RetainSummaryManager {
//==-----------------------------------------------------------------==//
// Typedefs.
//==-----------------------------------------------------------------==//
typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<ArgEffects> >
ArgEffectsSetTy;
typedef llvm::FoldingSet<RetainSummary>
SummarySetTy;
typedef llvm::DenseMap<FunctionDecl*, RetainSummary*>
FuncSummariesTy;
typedef llvm::DenseMap<Selector, RetainSummary*>
ObjCMethSummariesTy;
//==-----------------------------------------------------------------==//
// Data.
//==-----------------------------------------------------------------==//
// Ctx - The ASTContext object for the analyzed ASTs.
ASTContext& Ctx;
// GCEnabled - Records whether or not the analyzed code runs in GC mode.
const bool GCEnabled;
// SummarySet - A FoldingSet of uniqued summaries.
SummarySetTy SummarySet;
// FuncSummaries - A map from FunctionDecls to summaries.
FuncSummariesTy FuncSummaries;
// ObjCInstMethSummaries - A map from selectors (for instance methods)
// to summaries.
ObjCMethSummariesTy ObjCInstMethSummaries;
// ObjCMethSummaries - A map from selectors to summaries.
ObjCMethSummariesTy ObjCMethSummaries;
// ArgEffectsSet - A FoldingSet of uniqued ArgEffects.
ArgEffectsSetTy ArgEffectsSet;
// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects,
// and all other data used by the checker.
llvm::BumpPtrAllocator BPAlloc;
// ScratchArgs - A holding buffer for construct ArgEffects.
ArgEffects ScratchArgs;
RetainSummary* StopSummary;
//==-----------------------------------------------------------------==//
// Methods.
//==-----------------------------------------------------------------==//
// getArgEffects - Returns a persistent ArgEffects object based on the
// data in ScratchArgs.
ArgEffects* getArgEffects();
Ted Kremenek
committed
enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable };
RetainSummary* getUnarySummary(FunctionDecl* FD, UnaryFuncKind func);
RetainSummary* getNSSummary(FunctionDecl* FD, const char* FName);
RetainSummary* getCFSummary(FunctionDecl* FD, const char* FName);
RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD);
RetainSummary* getCFSummaryGetRule(FunctionDecl* FD);
RetainSummary* getPersistentSummary(ArgEffects* AE, RetEffect RetEff,
Ted Kremenek
committed
ArgEffect ReceiverEff = DoNothing,
ArgEffect DefaultEff = DoNothing);
RetainSummary* getPersistentSummary(RetEffect RE,
Ted Kremenek
committed
ArgEffect ReceiverEff = DoNothing,
ArgEffect DefaultEff = DoNothing) {
return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff);
Ted Kremenek
committed
RetainSummary* getPersistentStopSummary() {
if (StopSummary)
return StopSummary;
StopSummary = getPersistentSummary(RetEffect::MakeNoRet(),
StopTracking, StopTracking);
return StopSummary;
Ted Kremenek
committed
}
RetainSummary* getInitMethodSummary(Selector S);
Ted Kremenek
committed
void InitializeInstMethSummaries();
void InitializeMethSummaries();
RetainSummaryManager(ASTContext& ctx, bool gcenabled)
: Ctx(ctx), GCEnabled(gcenabled), StopSummary(0) {
Ted Kremenek
committed
InitializeInstMethSummaries();
InitializeMethSummaries();
~RetainSummaryManager();
RetainSummary* getSummary(FunctionDecl* FD, ASTContext& Ctx);
Ted Kremenek
committed
Ted Kremenek
committed
RetainSummary* getMethodSummary(ObjCMessageExpr* ME);
RetainSummary* getInstanceMethodSummary(IdentifierInfo* ClsName, Selector S);
bool isGCEnabled() const { return GCEnabled; }
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Implementation of checker data structures.
//===----------------------------------------------------------------------===//
RetainSummaryManager::~RetainSummaryManager() {
Ted Kremenek
committed
// FIXME: The ArgEffects could eventually be allocated from BPAlloc,
// mitigating the need to do explicit cleanup of the
// Argument-Effect summaries.
Ted Kremenek
committed
for (ArgEffectsSetTy::iterator I = ArgEffectsSet.begin(),
E = ArgEffectsSet.end(); I!=E; ++I)
I->getValue().~ArgEffects();
Ted Kremenek
committed
}
ArgEffects* RetainSummaryManager::getArgEffects() {
Ted Kremenek
committed
if (ScratchArgs.empty())
return NULL;
// Compute a profile for a non-empty ScratchArgs.
llvm::FoldingSetNodeID profile;
Ted Kremenek
committed
profile.Add(ScratchArgs);
void* InsertPos;
Ted Kremenek
committed
// Look up the uniqued copy, or create a new one.
llvm::FoldingSetNodeWrapper<ArgEffects>* E =
ArgEffectsSet.FindNodeOrInsertPos(profile, InsertPos);
Ted Kremenek
committed
if (E) {
ScratchArgs.clear();
return &E->getValue();
}
E = (llvm::FoldingSetNodeWrapper<ArgEffects>*)
BPAlloc.Allocate<llvm::FoldingSetNodeWrapper<ArgEffects> >();
new (E) llvm::FoldingSetNodeWrapper<ArgEffects>(ScratchArgs);
ArgEffectsSet.InsertNode(E, InsertPos);
ScratchArgs.clear();
return &E->getValue();
}
RetainSummary*
RetainSummaryManager::getPersistentSummary(ArgEffects* AE, RetEffect RetEff,
Ted Kremenek
committed
ArgEffect ReceiverEff,
ArgEffect DefaultEff) {
Ted Kremenek
committed
// Generate a profile for the summary.
llvm::FoldingSetNodeID profile;
Ted Kremenek
committed
RetainSummary::Profile(profile, AE, RetEff, DefaultEff, ReceiverEff);
Ted Kremenek
committed
// Look up the uniqued summary, or create one if it doesn't exist.
void* InsertPos;
RetainSummary* Summ = SummarySet.FindNodeOrInsertPos(profile, InsertPos);
if (Summ)
return Summ;
Ted Kremenek
committed
// Create the summary and return it.
Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
Ted Kremenek
committed
new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff);
SummarySet.InsertNode(Summ, InsertPos);
return Summ;
}
//===----------------------------------------------------------------------===//
// Summary creation for functions (largely uses of Core Foundation).
//===----------------------------------------------------------------------===//
RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD,
SourceLocation Loc = FD->getLocation();
if (!Loc.isFileID())
return NULL;
Ted Kremenek
committed
// Look up a summary in our cache of FunctionDecls -> Summaries.
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
Ted Kremenek
committed
if (I != FuncSummaries.end())
Ted Kremenek
committed
return I->second;
// No summary. Generate one.
const char* FName = FD->getIdentifier()->getName();
RetainSummary *S = 0;
Ted Kremenek
committed
if (FName[0] == 'C' && FName[1] == 'F')
S = getCFSummary(FD, FName);
else if (FName[0] == 'N' && FName[1] == 'S')
S = getNSSummary(FD, FName);
Ted Kremenek
committed
FuncSummaries[FD] = S;
Ted Kremenek
committed
return S;
Ted Kremenek
committed
}
RetainSummary* RetainSummaryManager::getNSSummary(FunctionDecl* FD,
Ted Kremenek
committed
FName += 2;
Ted Kremenek
committed
if (strcmp(FName, "MakeCollectable") == 0)
return getUnarySummary(FD, cfmakecollectable);
return 0;
}
RetainSummary* RetainSummaryManager::getCFSummary(FunctionDecl* FD,
Ted Kremenek
committed
FName += 2;
if (strcmp(FName, "Retain") == 0)
Ted Kremenek
committed
return getUnarySummary(FD, cfretain);
if (strcmp(FName, "Release") == 0)
Ted Kremenek
committed
return getUnarySummary(FD, cfrelease);
Ted Kremenek
committed
if (strcmp(FName, "MakeCollectable") == 0)
Ted Kremenek
committed
return getUnarySummary(FD, cfmakecollectable);
if (strstr(FName, "Create") || strstr(FName, "Copy"))
return getCFSummaryCreateRule(FD);
if (strstr(FName, "Get"))
Ted Kremenek
committed
return getCFSummaryGetRule(FD);
Ted Kremenek
committed
return 0;
RetainSummary*
RetainSummaryManager::getUnarySummary(FunctionDecl* FD, UnaryFuncKind func) {
Ted Kremenek
committed
FunctionTypeProto* FT =
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
Ted Kremenek
committed
if (FT) {
if (FT->getNumArgs() != 1)
return 0;
Ted Kremenek
committed
TypedefType* ArgT = dyn_cast<TypedefType>(FT->getArgType(0).getTypePtr());
Ted Kremenek
committed
if (!ArgT)
return 0;
if (!ArgT->isPointerType())
return NULL;
}
Ted Kremenek
committed
assert (ScratchArgs.empty());
switch (func) {
case cfretain: {
ScratchArgs.push_back(std::make_pair(0, IncRef));
return getPersistentSummary(RetEffect::MakeAlias(0));
}
case cfrelease: {
ScratchArgs.push_back(std::make_pair(0, DecRef));
return getPersistentSummary(RetEffect::MakeNoRet());
}
case cfmakecollectable: {
if (GCEnabled)
ScratchArgs.push_back(std::make_pair(0, DecRef));
return getPersistentSummary(RetEffect::MakeAlias(0));
}
default:
Ted Kremenek
committed
assert (false && "Not a supported unary function.");
Ted Kremenek
committed
}
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
}
static bool isCFRefType(QualType T) {
if (!T->isPointerType())
return false;
// Check the typedef for the name "CF" and the substring "Ref".
TypedefType* TD = dyn_cast<TypedefType>(T.getTypePtr());
if (!TD)
return false;
const char* TDName = TD->getDecl()->getIdentifier()->getName();
assert (TDName);
if (TDName[0] != 'C' || TDName[1] != 'F')
return false;
if (strstr(TDName, "Ref") == 0)
return false;
return true;
}
Ted Kremenek
committed
static bool isNSType(QualType T) {
if (!T->isPointerType())
return false;
// Check the typedef for the name "CF" and the substring "Ref".
TypedefType* TD = dyn_cast<TypedefType>(T.getTypePtr());
if (!TD)
return false;
const char* TDName = TD->getDecl()->getIdentifier()->getName();
assert (TDName);
if (TDName[0] != 'N' || TDName[1] != 'S')
return false;
return true;
}
Ted Kremenek
committed
RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) {
Ted Kremenek
committed
FunctionTypeProto* FT =
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
if (FT && !isCFRefType(FT->getResultType()))
return 0;
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
Ted Kremenek
committed
assert (ScratchArgs.empty());
return getPersistentSummary(RetEffect::MakeOwned());
RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) {
Ted Kremenek
committed
FunctionTypeProto* FT =
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
Ted Kremenek
committed
if (FT) {
QualType RetTy = FT->getResultType();
Ted Kremenek
committed
// FIXME: For now we assume that all pointer types returned are referenced
// counted. Since this is the "Get" rule, we assume non-ownership, which
// works fine for things that are not reference counted. We do this because
// some generic data structures return "void*". We need something better
// in the future.
if (!isCFRefType(RetTy) && !RetTy->isPointerType())
return 0;
}
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
Ted Kremenek
committed
assert (ScratchArgs.empty());
return getPersistentSummary(RetEffect::MakeNotOwned());
//===----------------------------------------------------------------------===//
// Summary creation for Selectors.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
RetainSummary*
RetainSummaryManager::getInitMethodSummary(Selector S) {
assert(ScratchArgs.empty());
RetainSummary* Summ =
getPersistentSummary(RetEffect::MakeReceiverAlias());
ObjCMethSummaries[S] = Summ;
Ted Kremenek
committed
RetainSummary*
RetainSummaryManager::getMethodSummary(ObjCMessageExpr* ME) {
Selector S = ME->getSelector();
// Look up a summary in our cache of Selectors -> Summaries.
ObjCMethSummariesTy::iterator I = ObjCMethSummaries.find(S);
if (I != ObjCMethSummaries.end())
Ted Kremenek
committed
// Only generate real summaries for methods involving
// NSxxxx objects.
Ted Kremenek
committed
if (!isNSType(ME->getReceiver()->getType())) {
RetainSummary* Summ = getPersistentStopSummary();
ObjCMethSummaries[S] = Summ;
return Summ;
}
// "initXXX": pass-through for receiver.
const char* s = S.getIdentifierInfoForSlot(0)->getName();
return getInitMethodSummary(S);
Ted Kremenek
committed
#if 0
// Generate a summary. For all "setYYY:" and "addXXX:" slots => StopTracking.
Ted Kremenek
committed
assert (ScratchArgs.empty());
if (S.isUnarySelector()) {
RetainSummary* Summ = getPersistentSummary(RetEffect::MakeNoRet());
return Summ;
}
for (unsigned i = 0, e = ME->getNumArgs(); i!=e; ++i) {
IdentifierInfo *II = S.getIdentifierInfoForSlot(i);
const char* s = II->getName();
if (strncmp(s, "set", 3) == 0 || strncmp(s, "add", 3) == 0)
ScratchArgs.push_back(std::make_pair(i, StopTracking));
}
#endif
RetainSummary*
RetainSummaryManager::getInstanceMethodSummary(IdentifierInfo* ClsName,
Selector S) {
// Look up a summary in our cache of Selectors -> Summaries.
ObjCMethSummariesTy::iterator I = ObjCInstMethSummaries.find(S);
if (I != ObjCInstMethSummaries.end())
return I->second;
return 0;
#if 0
return 0;
// Don't track anything if using GC.
if (isGCEnabled())
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
// Inspect the class name and selecrtor to determine if this method
// creates new objects.
const char* cls = ClsName->getName();
if (cls[0] == 'N' && cls[1] == 'S') // Ignore preceding "NS" (if present).
cls += 2;
// Heuristic: XXXwithYYY, where XXX is the class name with the "NS"
// stripped off is usually an allocation.
const char* s = S.getIdentifierInfoForSlot(0)->getName();
if (cls[0] == '\0' || s[0] == '\0' || tolower(cls[0]) != s[0])
return 0;
// Now look at the rest of the characters.
++cls; ++s;
unsigned len = strlen(cls);
// If the prefix doesn't match, don't generate a special summary.
if (strncmp(cls, s, len) != 0)
return 0;
// Look at the text after the prefix.
s += len;
if (!(s[0] == '\0' || strncmp(s, "With", 4) == 0))
return 0;
// Generate and return the summary.
assert (ScratchArgs.empty());
RetainSummary* Summ = getPersistentSummary(RetEffect::MakeOwned());
ObjCInstMethSummaries[S] = Summ;
return Summ;
Ted Kremenek
committed
void RetainSummaryManager::InitializeInstMethSummaries() {
assert (ScratchArgs.empty());
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet() : RetEffect::MakeOwned();
RetainSummary* Summ = getPersistentSummary(E);
// Create the "alloc" selector.
ObjCInstMethSummaries[ GetNullarySelector("alloc", Ctx) ] = Summ;
// Create the "new" selector.
ObjCInstMethSummaries[ GetNullarySelector("new", Ctx) ] = Summ;
// Create the "allocWithZone:" selector.
ObjCInstMethSummaries[ GetUnarySelector("allocWithZone", Ctx) ] = Summ;
// Create the "copyWithZone:" selector.
ObjCInstMethSummaries[ GetUnarySelector("copyWithZone", Ctx) ] = Summ;
// Create the "mutableCopyWithZone:" selector.
ObjCInstMethSummaries[ GetUnarySelector("mutableCopyWithZone", Ctx) ] = Summ;
// ** Special cases! **
//
// FIXME: It would be great if this one day was in a file, rather than
// hardcoded into the source code.
//
// NSProcessInfo::processInfo - This instance method does not return
// an owning reference.
ObjCInstMethSummaries[ GetNullarySelector("processInfo", Ctx) ] =
getPersistentSummary(RetEffect::MakeNoRet());
}
Ted Kremenek
committed
void RetainSummaryManager::InitializeMethSummaries() {
assert (ScratchArgs.empty());
// Create the "init" selector. It just acts as a pass-through for the
// receiver.
Ted Kremenek
committed
RetainSummary* Summ = getPersistentSummary(RetEffect::MakeReceiverAlias());
ObjCMethSummaries[ GetNullarySelector("init", Ctx) ] = Summ;
Ted Kremenek
committed
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet() : RetEffect::MakeOwned();
Summ = getPersistentSummary(E);
// Create the "copy" selector.
Ted Kremenek
committed
ObjCMethSummaries[ GetNullarySelector("copy", Ctx) ] = Summ;
// Create the "mutableCopy" selector.
ObjCMethSummaries[ GetNullarySelector("mutableCopy", Ctx) ] = Summ;
// Create the "retain" selector.
E = RetEffect::MakeReceiverAlias();
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : IncRef);
ObjCMethSummaries[ GetNullarySelector("retain", Ctx) ] = Summ;
// Create the "release" selector.
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef);
ObjCMethSummaries[ GetNullarySelector("release", Ctx) ] = Summ;
// Create the "autorelease" selector.
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : StopTracking);
ObjCMethSummaries[ GetNullarySelector("autorelease", Ctx) ] = Summ;
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;
RefVal(Kind k, unsigned cnt) : kind(k), Cnt(cnt) {}
RefVal(Kind k) : kind(k), Cnt(0) {}
public:
Kind getKind() const { return kind; }
unsigned getCount() const { return Cnt; }
// 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(unsigned Count = 0) {
return RefVal(Owned, Count);
}
static RefVal makeNotOwned(unsigned Count = 0) {
return RefVal(NotOwned, Count);
}
static RefVal makeReturnedOwned(unsigned Count) {
return RefVal(ReturnedOwned, Count);
}
static RefVal makeReturnedNotOwned() {
return RefVal(ReturnedNotOwned);
}
// State creation: errors.
static RefVal makeLeak(unsigned Count) { return RefVal(ErrorLeak, Count); }
static RefVal makeReleased() { return RefVal(Released); }
static RefVal makeUseAfterRelease() { return RefVal(ErrorUseAfterRelease); }
static RefVal makeReleaseNotOwned() { return RefVal(ErrorReleaseNotOwned); }
// Comparison, profiling, and pretty-printing.
bool operator==(const RefVal& X) const {
return kind == X.kind && Cnt == X.Cnt;
}
Ted Kremenek
committed
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 {
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;
}
}
static inline unsigned GetCount(RefVal V) {
switch (V.getKind()) {
default:
return V.getCount();
case RefVal::Owned:
return V.getCount()+1;
}
}
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Transfer functions.
//===----------------------------------------------------------------------===//
class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
public:
Ted Kremenek
committed
// Type definitions.
typedef llvm::ImmutableMap<SymbolID, RefVal> RefBindings;
typedef RefBindings::Factory RefBFactoryTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*,std::pair<Expr*, SymbolID> >
ReleasesNotOwnedTy;
typedef ReleasesNotOwnedTy UseAfterReleasesTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*, std::vector<SymbolID>*>
LeaksTy;
Ted Kremenek
committed
class BindingsPrinter : public ValueState::CheckerStatePrinter {
public:
virtual void PrintCheckerState(std::ostream& Out, void* State,
const char* nl, const char* sep);
};
private:
Ted Kremenek
committed
// Instance variables.
RetainSummaryManager Summaries;
const bool EmitStandardWarnings;
const LangOptions& LOpts;
RefBFactoryTy RefBFactory;
UseAfterReleasesTy UseAfterReleases;
ReleasesNotOwnedTy ReleasesNotOwned;
LeaksTy Leaks;
Ted Kremenek
committed
BindingsPrinter Printer;
Selector RetainSelector;
Selector ReleaseSelector;
Selector AutoreleaseSelector;
public:
static RefBindings GetRefBindings(ValueState& StImpl) {
return RefBindings((RefBindings::TreeTy*) StImpl.CheckerState);
}
private:
static void SetRefBindings(ValueState& StImpl, RefBindings B) {
StImpl.CheckerState = B.getRoot();
}
RefBindings Remove(RefBindings B, SymbolID sym) {
return RefBFactory.Remove(B, sym);
}
RefBindings Update(RefBindings B, SymbolID sym, RefVal V, ArgEffect E,
RefVal::Kind& hasErr);
void ProcessNonLeakError(ExplodedNodeSet<ValueState>& Dst,
GRStmtNodeBuilder<ValueState>& Builder,
Expr* NodeExpr, Expr* ErrorExpr,
ExplodedNode<ValueState>* Pred,
ValueState* St,
RefVal::Kind hasErr, SymbolID Sym);
ValueState* HandleSymbolDeath(ValueStateManager& VMgr, ValueState* St,
SymbolID sid, RefVal V, bool& hasLeak);
ValueState* NukeBinding(ValueStateManager& VMgr, ValueState* St,
SymbolID sid);
Ted Kremenek
committed
Ted Kremenek
committed
CFRefCount(ASTContext& Ctx, bool gcenabled, bool StandardWarnings,
const LangOptions& lopts)
: Summaries(Ctx, gcenabled),
Ted Kremenek
committed
EmitStandardWarnings(StandardWarnings),
LOpts(lopts),
RetainSelector(GetNullarySelector("retain", Ctx)),
ReleaseSelector(GetNullarySelector("release", Ctx)),
AutoreleaseSelector(GetNullarySelector("autorelease", Ctx)) {}
virtual ~CFRefCount() {
for (LeaksTy::iterator I = Leaks.begin(), E = Leaks.end(); I!=E; ++I)
delete I->second;
}