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"
#include "clang/Analysis/PathSensitive/GRState.h"
Ted Kremenek
committed
#include "clang/Analysis/PathSensitive/GRStateTrait.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/ADT/ImmutableList.h"
Ted Kremenek
committed
#include "llvm/ADT/StringExtras.h"
Ted Kremenek
committed
#include "llvm/Support/Compiler.h"
#include "llvm/ADT/STLExtras.h"
Ted Kremenek
committed
#include <ostream>
Ted Kremenek
committed
using namespace clang;
//===----------------------------------------------------------------------===//
// Utility functions.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
using llvm::CStrInCStrNoCase;
Ted Kremenek
committed
// The "fundamental rule" for naming conventions of methods:
// (url broken into two lines)
// http://developer.apple.com/documentation/Cocoa/Conceptual/
// MemoryMgmt/Tasks/MemoryManagementRules.html
//
// "You take ownership of an object if you create it using a method whose name
// begins with “alloc” or “new” or contains “copy” (for example, alloc,
// newObject, or mutableCopy), or if you send it a retain message. You are
// responsible for relinquishing ownership of objects you own using release
// or autorelease. Any other time you receive an object, you must
// not release it."
//
static bool followsFundamentalRule(const char* s) {
return CStrInCStrNoCase(s, "create") || CStrInCStrNoCase(s, "copy") ||
CStrInCStrNoCase(s, "new") == s || CStrInCStrNoCase(s, "alloc") == s;
}
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
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);
}
//===----------------------------------------------------------------------===//
// Type querying functions.
//===----------------------------------------------------------------------===//
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;
}
static bool isCGRefType(QualType T) {
if (!T->isPointerType())
return false;
// Check the typedef for the name "CG" 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] != 'G')
return false;
if (strstr(TDName, "Ref") == 0)
return false;
return true;
}
static bool isNSType(QualType T) {
if (!T->isPointerType())
return false;
ObjCInterfaceType* OT = dyn_cast<ObjCInterfaceType>(T.getTypePtr());
if (!OT)
return false;
const char* ClsName = OT->getDecl()->getIdentifier()->getName();
assert (ClsName);
if (ClsName[0] != 'N' || ClsName[1] != 'S')
return false;
return true;
}
//===----------------------------------------------------------------------===//
// Primitives used for constructing summaries for function/method calls.
//===----------------------------------------------------------------------===//
namespace {
/// ArgEffect is used to summarize a function/method call's effect on a
/// particular argument.
enum ArgEffect { IncRef, DecRef, DoNothing, DoNothingByRef,
StopTracking, MayEscape, SelfOwn, Autorelease };
/// ArgEffects summarizes the effects of a function/method call on all of
/// its arguments.
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) {
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 {
/// RetEffect is used to summarize a function/method call's behavior with
/// respect to its return value.
class VISIBILITY_HIDDEN RetEffect {
enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol,
NotOwnedSymbol, ReceiverAlias };
private:
unsigned Data;
RetEffect(Kind k, unsigned D = 0) { Data = (D << 3) | (unsigned) k; }
Kind getKind() const { return (Kind) (Data & 0x7); }
assert(getKind() == Alias);
return Data >> 3;
static RetEffect MakeAlias(unsigned Idx) {
return RetEffect(Alias, Idx);
}
static RetEffect MakeReceiverAlias() {
return RetEffect(ReceiverAlias);
}
static RetEffect MakeOwned(bool isAllocated = false) {
return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol);
}
static RetEffect MakeNotOwned() {
return RetEffect(NotOwnedSymbol);
}
static RetEffect MakeNoRet() {
return RetEffect(NoRet);
}
void Profile(llvm::FoldingSetNodeID& ID) const {
ID.AddInteger(Data);
}
class VISIBILITY_HIDDEN 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;
/// Receiver - If this summary applies to an Objective-C message expression,
/// this is the effect applied to the state of the receiver.
/// Ret - The effect on the return value. Used to indicate if the
/// function/method call returns a new tracked symbol, returns an
/// alias of one of the arguments in the call, and so on.
/// EndPath - Indicates that execution of this method/function should
/// terminate the simulation of a path.
bool EndPath;
Ted Kremenek
committed
RetainSummary(ArgEffects* A, RetEffect R, ArgEffect defaultEff,
ArgEffect ReceiverEff, bool endpath = false)
: Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R),
EndPath(endpath) {}
/// getArg - Return the argument effect on the argument specified by
/// idx (starting from 0).
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.
Ted Kremenek
committed
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;
/// getRetEffect - Returns the effect on the return value of the call.
RetEffect getRetEffect() const {
return Ret;
}
/// isEndPath - Returns true if executing the given method/function should
/// terminate the path.
bool isEndPath() const { return EndPath; }
/// getReceiverEffect - Returns the effect on the receiver of the call.
/// This is only meaningful if the summary applies to an ObjCMessageExpr*.
ArgEffect getReceiverEffect() const {
return Receiver;
}
typedef ArgEffects::const_iterator ExprIterator;
ExprIterator begin_args() const { return Args->begin(); }
ExprIterator end_args() const { return Args->end(); }
Ted Kremenek
committed
static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects* A,
Ted Kremenek
committed
RetEffect RetEff, ArgEffect DefaultEff,
ID.AddPointer(A);
Ted Kremenek
committed
ID.AddInteger((unsigned) DefaultEff);
ID.AddInteger((unsigned) ReceiverEff);
}
void Profile(llvm::FoldingSetNodeID& ID) const {
Profile(ID, Args, Ret, DefaultArgEffect, Receiver, EndPath);
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Data structures for constructing summaries.
//===----------------------------------------------------------------------===//
namespace {
class VISIBILITY_HIDDEN ObjCSummaryKey {
IdentifierInfo* II;
Selector S;
public:
ObjCSummaryKey(IdentifierInfo* ii, Selector s)
: II(ii), S(s) {}
ObjCSummaryKey(ObjCInterfaceDecl* d, Selector s)
: II(d ? d->getIdentifier() : 0), S(s) {}
ObjCSummaryKey(Selector s)
: II(0), S(s) {}
IdentifierInfo* getIdentifier() const { return II; }
Selector getSelector() const { return S; }
};
}
namespace llvm {
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
template <> struct DenseMapInfo<ObjCSummaryKey> {
static inline ObjCSummaryKey getEmptyKey() {
return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getEmptyKey(),
DenseMapInfo<Selector>::getEmptyKey());
}
static inline ObjCSummaryKey getTombstoneKey() {
return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getTombstoneKey(),
DenseMapInfo<Selector>::getTombstoneKey());
}
static unsigned getHashValue(const ObjCSummaryKey &V) {
return (DenseMapInfo<IdentifierInfo*>::getHashValue(V.getIdentifier())
& 0x88888888)
| (DenseMapInfo<Selector>::getHashValue(V.getSelector())
& 0x55555555);
}
static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) {
return DenseMapInfo<IdentifierInfo*>::isEqual(LHS.getIdentifier(),
RHS.getIdentifier()) &&
DenseMapInfo<Selector>::isEqual(LHS.getSelector(),
RHS.getSelector());
}
static bool isPod() {
return DenseMapInfo<ObjCInterfaceDecl*>::isPod() &&
DenseMapInfo<Selector>::isPod();
}
};
} // end llvm namespace
namespace {
class VISIBILITY_HIDDEN ObjCSummaryCache {
typedef llvm::DenseMap<ObjCSummaryKey, RetainSummary*> MapTy;
MapTy M;
public:
ObjCSummaryCache() {}
typedef MapTy::iterator iterator;
iterator find(ObjCInterfaceDecl* D, Selector S) {
// Do a lookup with the (D,S) pair. If we find a match return
// the iterator.
ObjCSummaryKey K(D, S);
MapTy::iterator I = M.find(K);
if (I != M.end() || !D)
return I;
// Walk the super chain. If we find a hit with a parent, we'll end
// up returning that summary. We actually allow that key (null,S), as
// we cache summaries for the null ObjCInterfaceDecl* to allow us to
// generate initial summaries without having to worry about NSObject
// being declared.
// FIXME: We may change this at some point.
for (ObjCInterfaceDecl* C=D->getSuperClass() ;; C=C->getSuperClass()) {
if ((I = M.find(ObjCSummaryKey(C, S))) != M.end())
break;
}
// Cache the summary with original key to make the next lookup faster
// and return the iterator.
M[K] = I->second;
return I;
}
iterator find(Expr* Receiver, Selector S) {
return find(getReceiverDecl(Receiver), S);
}
iterator find(IdentifierInfo* II, Selector S) {
// FIXME: Class method lookup. Right now we dont' have a good way
// of going between IdentifierInfo* and the class hierarchy.
iterator I = M.find(ObjCSummaryKey(II, S));
return I == M.end() ? M.find(ObjCSummaryKey(S)) : I;
}
ObjCInterfaceDecl* getReceiverDecl(Expr* E) {
const PointerType* PT = E->getType()->getAsPointerType();
if (!PT) return 0;
ObjCInterfaceType* OI = dyn_cast<ObjCInterfaceType>(PT->getPointeeType());
if (!OI) return 0;
return OI ? OI->getDecl() : 0;
}
iterator end() { return M.end(); }
RetainSummary*& operator[](ObjCMessageExpr* ME) {
Selector S = ME->getSelector();
if (Expr* Receiver = ME->getReceiver()) {
ObjCInterfaceDecl* OD = getReceiverDecl(Receiver);
return OD ? M[ObjCSummaryKey(OD->getIdentifier(), S)] : M[S];
}
return M[ObjCSummaryKey(ME->getClassName(), S)];
}
RetainSummary*& operator[](ObjCSummaryKey K) {
return M[K];
}
Ted Kremenek
committed
RetainSummary*& operator[](Selector S) {
return M[ ObjCSummaryKey(S) ];
}
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Data structures for managing collections of summaries.
//===----------------------------------------------------------------------===//
namespace {
//==-----------------------------------------------------------------==//
// Typedefs.
//==-----------------------------------------------------------------==//
typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<ArgEffects> >
ArgEffectsSetTy;
typedef llvm::FoldingSet<RetainSummary>
SummarySetTy;
typedef llvm::DenseMap<FunctionDecl*, RetainSummary*>
FuncSummariesTy;
typedef ObjCSummaryCache ObjCMethodSummariesTy;
//==-----------------------------------------------------------------==//
// Data.
//==-----------------------------------------------------------------==//
ASTContext& Ctx;
Ted Kremenek
committed
/// CFDictionaryCreateII - An IdentifierInfo* representing the indentifier
/// "CFDictionaryCreate".
IdentifierInfo* CFDictionaryCreateII;
/// GCEnabled - Records whether or not the analyzed code runs in GC mode.
const bool GCEnabled;
SummarySetTy SummarySet;
/// FuncSummaries - A map from FunctionDecls to summaries.
FuncSummariesTy FuncSummaries;
/// ObjCClassMethodSummaries - A map from selectors (for instance methods)
/// to summaries.
Ted Kremenek
committed
ObjCMethodSummariesTy ObjCClassMethodSummaries;
/// ObjCMethodSummaries - A map from selectors to summaries.
Ted Kremenek
committed
ObjCMethodSummariesTy ObjCMethodSummaries;
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 };
Ted Kremenek
committed
public:
RetainSummary* getUnarySummary(FunctionDecl* FD, UnaryFuncKind func);
RetainSummary* getNSSummary(FunctionDecl* FD, const char* FName);
RetainSummary* getCFSummary(FunctionDecl* FD, const char* FName);
RetainSummary* getCGSummary(FunctionDecl* FD, const char* FName);
RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD);
RetainSummary* getCFSummaryGetRule(FunctionDecl* FD);
RetainSummary* getCFCreateGetRuleSummary(FunctionDecl* FD, const char* FName);
RetainSummary* getPersistentSummary(ArgEffects* AE, RetEffect RetEff,
Ted Kremenek
committed
ArgEffect ReceiverEff = DoNothing,
ArgEffect DefaultEff = MayEscape,
bool isEndPath = false);
Ted Kremenek
committed
RetainSummary* getPersistentSummary(RetEffect RE,
Ted Kremenek
committed
ArgEffect ReceiverEff = DoNothing,
Ted Kremenek
committed
ArgEffect DefaultEff = MayEscape) {
Ted Kremenek
committed
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(ObjCMessageExpr* ME);
Ted Kremenek
committed
void InitializeClassMethodSummaries();
void InitializeMethodSummaries();
Ted Kremenek
committed
private:
void addClsMethSummary(IdentifierInfo* ClsII, Selector S,
RetainSummary* Summ) {
ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
}
void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) {
ObjCClassMethodSummaries[S] = Summ;
}
void addNSObjectMethSummary(Selector S, RetainSummary *Summ) {
ObjCMethodSummaries[S] = Summ;
}
void addInstMethSummary(const char* Cls, RetainSummary* Summ, va_list argp) {
Ted Kremenek
committed
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
llvm::SmallVector<IdentifierInfo*, 10> II;
Ted Kremenek
committed
while (const char* s = va_arg(argp, const char*))
II.push_back(&Ctx.Idents.get(s));
Selector S = Ctx.Selectors.getSelector(II.size(), &II[0]);
ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
}
void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) {
va_list argp;
va_start(argp, Summ);
addInstMethSummary(Cls, Summ, argp);
va_end(argp);
}
Ted Kremenek
committed
void addPanicSummary(const char* Cls, ...) {
RetainSummary* Summ = getPersistentSummary(0, RetEffect::MakeNoRet(),
DoNothing, DoNothing, true);
va_list argp;
va_start (argp, Cls);
addInstMethSummary(Cls, Summ, argp);
Ted Kremenek
committed
va_end(argp);
}
RetainSummaryManager(ASTContext& ctx, bool gcenabled)
Ted Kremenek
committed
: Ctx(ctx),
CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")),
GCEnabled(gcenabled), StopSummary(0) {
InitializeClassMethodSummaries();
InitializeMethodSummaries();
}
~RetainSummaryManager();
RetainSummary* getSummary(FunctionDecl* FD);
RetainSummary* getMethodSummary(ObjCMessageExpr* ME, ObjCInterfaceDecl* ID);
Ted Kremenek
committed
RetainSummary* getClassMethodSummary(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;
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,
bool isEndPath) {
Ted Kremenek
committed
// Generate a profile for the summary.
llvm::FoldingSetNodeID profile;
RetainSummary::Profile(profile, AE, RetEff, DefaultEff, ReceiverEff,
isEndPath);
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>();
new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff, isEndPath);
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
FunctionType* FT = dyn_cast<FunctionType>(FD->getType());
do {
if (FT) {
QualType T = FT->getResultType();
if (isCFRefType(T)) {
S = getCFSummary(FD, FName);
break;
}
if (isCGRefType(T)) {
S = getCGSummary(FD, FName );
break;
}
}
Ted Kremenek
committed
if (FName[0] == 'C') {
if (FName[1] == 'F')
S = getCFSummary(FD, FName);
else if (FName[1] == 'G')
S = getCGSummary(FD, FName);
}
else if (FName[0] == 'N' && FName[1] == 'S')
S = getNSSummary(FD, FName);
}
while (0);
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;
}
static bool isRetain(FunctionDecl* FD, const char* FName) {
Ted Kremenek
committed
const char* loc = strstr(FName, "Retain");
return loc && loc[sizeof("Retain")-1] == '\0';
}
static bool isRelease(FunctionDecl* FD, const char* FName) {
Ted Kremenek
committed
const char* loc = strstr(FName, "Release");
return loc && loc[sizeof("Release")-1] == '\0';
}
RetainSummary* RetainSummaryManager::getCFSummary(FunctionDecl* FD,
Ted Kremenek
committed
if (FName[0] == 'C' && FName[1] == 'F')
FName += 2;
if (isRetain(FD, FName))
Ted Kremenek
committed
return getUnarySummary(FD, cfretain);
if (isRelease(FD, FName))
Ted Kremenek
committed
return getUnarySummary(FD, cfrelease);
if (strcmp(FName, "MakeCollectable") == 0)
Ted Kremenek
committed
return getUnarySummary(FD, cfmakecollectable);
return getCFCreateGetRuleSummary(FD, FName);
}
RetainSummary* RetainSummaryManager::getCGSummary(FunctionDecl* FD,
const char* FName) {
if (FName[0] == 'C' && FName[1] == 'G')
FName += 2;
if (isRelease(FD, FName))
return getUnarySummary(FD, cfrelease);
if (isRetain(FD, FName))
return getUnarySummary(FD, cfretain);
return getCFCreateGetRuleSummary(FD, FName);
}
RetainSummary*
RetainSummaryManager::getCFCreateGetRuleSummary(FunctionDecl* FD,
const char* FName) {
Ted Kremenek
committed
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));
Ted Kremenek
committed
return getPersistentSummary(RetEffect::MakeAlias(0),
DoNothing, DoNothing);
}
case cfrelease: {
ScratchArgs.push_back(std::make_pair(0, DecRef));
Ted Kremenek
committed
return getPersistentSummary(RetEffect::MakeNoRet(),
DoNothing, DoNothing);
}
case cfmakecollectable: {
if (GCEnabled)
ScratchArgs.push_back(std::make_pair(0, DecRef));
Ted Kremenek
committed
return getPersistentSummary(RetEffect::MakeAlias(0),
DoNothing, DoNothing);
}
default:
Ted Kremenek
committed
assert (false && "Not a supported unary function.");
Ted Kremenek
committed
}
RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) {
FunctionType* FT =
dyn_cast<FunctionType>(FD->getType().getTypePtr());
Ted Kremenek
committed
if (FT) {
QualType ResTy = FT->getResultType();
if (!isCFRefType(ResTy) && !isCGRefType(ResTy))
return getPersistentSummary(RetEffect::MakeNoRet());
}
Ted Kremenek
committed
assert (ScratchArgs.empty());
if (FD->getIdentifier() == CFDictionaryCreateII) {
ScratchArgs.push_back(std::make_pair(1, DoNothingByRef));
ScratchArgs.push_back(std::make_pair(2, DoNothingByRef));
}
return getPersistentSummary(RetEffect::MakeOwned(true));
RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) {
FunctionType* FT =
dyn_cast<FunctionType>(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())
Ted Kremenek
committed
return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
Ted Kremenek
committed
}
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
Ted Kremenek
committed
assert (ScratchArgs.empty());
Ted Kremenek
committed
return getPersistentSummary(RetEffect::MakeNotOwned(), DoNothing, DoNothing);
//===----------------------------------------------------------------------===//
// Summary creation for Selectors.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
RetainSummary*
RetainSummaryManager::getInitMethodSummary(ObjCMessageExpr* ME) {
assert(ScratchArgs.empty());
RetainSummary* Summ =
getPersistentSummary(RetEffect::MakeReceiverAlias());
Ted Kremenek
committed
RetainSummary*
RetainSummaryManager::getMethodSummary(ObjCMessageExpr* ME,
ObjCInterfaceDecl* ID) {
Ted Kremenek
committed
Selector S = ME->getSelector();
// Look up a summary in our summary cache.
ObjCMethodSummariesTy::iterator I = ObjCMethodSummaries.find(ID, S);
Ted Kremenek
committed
if (I != ObjCMethodSummaries.end())
if (!ME->getType()->isPointerType())
return 0;
// "initXXX": pass-through for receiver.
const char* s = S.getIdentifierInfoForSlot(0)->getName();
assert (ScratchArgs.empty());
Ted Kremenek
committed
if (strncmp(s, "init", 4) == 0 || strncmp(s, "_init", 5) == 0)
Ted Kremenek
committed
// "copyXXX", "createXXX", "newXXX": allocators.
if (!isNSType(ME->getReceiver()->getType()))
return 0;
Ted Kremenek
committed
if (followsFundamentalRule(s)) {
Ted Kremenek
committed
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet()
: RetEffect::MakeOwned(true);
Ted Kremenek
committed
RetainSummary* Summ = getPersistentSummary(E);
return Summ;
Ted Kremenek
committed
}
Ted Kremenek
committed
RetainSummaryManager::getClassMethodSummary(IdentifierInfo* ClsName,
Selector S) {
// FIXME: Eventually we should properly do class method summaries, but
// it requires us being able to walk the type hierarchy. Unfortunately,
// we cannot do this with just an IdentifierInfo* for the class name.
// Look up a summary in our cache of Selectors -> Summaries.
ObjCMethodSummariesTy::iterator I = ObjCClassMethodSummaries.find(ClsName, S);
Ted Kremenek
committed
if (I != ObjCClassMethodSummaries.end())
Ted Kremenek
committed
void RetainSummaryManager::InitializeClassMethodSummaries() {
assert (ScratchArgs.empty());
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet()
: RetEffect::MakeOwned(true);
RetainSummary* Summ = getPersistentSummary(E);
// Create the summaries for "alloc", "new", and "allocWithZone:" for
// NSObject and its derivatives.
addNSObjectClsMethSummary(GetNullarySelector("alloc", Ctx), Summ);
addNSObjectClsMethSummary(GetNullarySelector("new", Ctx), Summ);
addNSObjectClsMethSummary(GetUnarySelector("allocWithZone", Ctx), Summ);
// Create the [NSAssertionHandler currentHander] summary.
Ted Kremenek
committed
addClsMethSummary(&Ctx.Idents.get("NSAssertionHandler"),