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) {
while (*s == '_') ++s;
return CStrInCStrNoCase(s, "copy")
|| CStrInCStrNoCase(s, "new") == s
|| CStrInCStrNoCase(s, "alloc") == s;
}
static bool followsReturnRule(const char* s) {
while (*s == '_') ++s;
return followsFundamentalRule(s) || CStrInCStrNoCase(s, "init") == 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 hasPrefix(const char* s, const char* prefix) {
if (!prefix)
return true;
while (c != '\0' && cP != '\0') {
if (c != cP) break;
c = *(++s);
cP = *(++prefix);
}
}
static bool hasSuffix(const char* s, const char* suffix) {
const char* loc = strstr(s, suffix);
return loc && strcmp(suffix, loc) == 0;
}
static bool isRefType(QualType RetTy, const char* prefix,
ASTContext* Ctx = 0, const char* name = 0) {
if (TypedefType* TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) {
const char* TDName = TD->getDecl()->getIdentifier()->getName();
return hasPrefix(TDName, prefix) && hasSuffix(TDName, "Ref");
}
if (!Ctx || !name)
return false;
// Is the type void*?
const PointerType* PT = RetTy->getAsPointerType();
if (!(PT->getPointeeType().getUnqualifiedType() == Ctx->VoidTy))
return false;
// Does the name start with the prefix?
return hasPrefix(name, prefix);
}
//===----------------------------------------------------------------------===//
// 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 {
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
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
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(FunctionType* FT, UnaryFuncKind func);
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);
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
bool isTrackedObjectType(QualType T);
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;
}
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
//===----------------------------------------------------------------------===//
// Predicates.
//===----------------------------------------------------------------------===//
bool RetainSummaryManager::isTrackedObjectType(QualType T) {
if (!Ctx.isObjCObjectPointerType(T))
return false;
// Does it subclass NSObject?
ObjCInterfaceType* OT = dyn_cast<ObjCInterfaceType>(T.getTypePtr());
// We assume that id<..>, id, and "Class" all represent tracked objects.
if (!OT)
return true;
// Does the object type subclass NSObject?
// FIXME: We can memoize here if this gets too expensive.
IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
ObjCInterfaceDecl* ID = OT->getDecl();
for ( ; ID ; ID = ID->getSuperClass())
if (ID->getIdentifier() == NSObjectII)
return true;
return false;
}
//===----------------------------------------------------------------------===//
// Summary creation for functions (largely uses of Core Foundation).
//===----------------------------------------------------------------------===//
static bool isRetain(FunctionDecl* FD, const char* FName) {
const char* loc = strstr(FName, "Retain");
return loc && loc[sizeof("Retain")-1] == '\0';
}
static bool isRelease(FunctionDecl* FD, const char* FName) {
const char* loc = strstr(FName, "Release");
return loc && loc[sizeof("Release")-1] == '\0';
}
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.
Ted Kremenek
committed
do {
// We generate "stop" summaries for implicitly defined functions.
if (FD->isImplicit()) {
S = getPersistentStopSummary();
break;
}
Ted Kremenek
committed
// [PR 3337] Use 'getDesugaredType' to strip away any typedefs on the
// function's type.
FunctionType* FT = cast<FunctionType>(FD->getType()->getDesugaredType());
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
const char* FName = FD->getIdentifier()->getName();
// Inspect the result type.
QualType RetTy = FT->getResultType();
// FIXME: This should all be refactored into a chain of "summary lookup"
// filters.
if (strcmp(FName, "IOServiceGetMatchingServices") == 0) {
// FIXES: <rdar://problem/6326900>
// This should be addressed using a API table. This strcmp is also
// a little gross, but there is no need to super optimize here.
assert (ScratchArgs.empty());
ScratchArgs.push_back(std::make_pair(1, DecRef));
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
break;
}
// Handle: id NSMakeCollectable(CFTypeRef)
if (strcmp(FName, "NSMakeCollectable") == 0) {
S = (RetTy == Ctx.getObjCIdType())
? getUnarySummary(FT, cfmakecollectable)
: getPersistentStopSummary();
break;
}
if (RetTy->isPointerType()) {
// For CoreFoundation ('CF') types.
if (isRefType(RetTy, "CF", &Ctx, FName)) {
if (isRetain(FD, FName))
S = getUnarySummary(FT, cfretain);
else if (strstr(FName, "MakeCollectable"))
S = getUnarySummary(FT, cfmakecollectable);
else
S = getCFCreateGetRuleSummary(FD, FName);
break;
}
// For CoreGraphics ('CG') types.
if (isRefType(RetTy, "CG", &Ctx, FName)) {
if (isRetain(FD, FName))
S = getUnarySummary(FT, cfretain);
else
S = getCFCreateGetRuleSummary(FD, FName);
break;
}
// For the Disk Arbitration API (DiskArbitration/DADisk.h)
if (isRefType(RetTy, "DADisk") ||
isRefType(RetTy, "DADissenter") ||
isRefType(RetTy, "DASessionRef")) {
S = getCFCreateGetRuleSummary(FD, FName);
// Check for release functions, the only kind of functions that we care
// about that don't return a pointer type.
if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G')) {
if (isRelease(FD, FName+2))
S = getUnarySummary(FT, cfrelease);
else {
// For CoreFoundation and CoreGraphics functions we assume they
// follow the ownership idiom strictly and thus do not cause
// ownership to "escape".
assert (ScratchArgs.empty());
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing,
DoNothing);
}
Ted Kremenek
committed
}
}
while (0);
Ted Kremenek
committed
FuncSummaries[FD] = S;
Ted Kremenek
committed
return S;
Ted Kremenek
committed
}
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(FunctionType* FT, UnaryFuncKind func) {
// Sanity check that this is *really* a unary function. This can
// happen if people do weird things.
FunctionTypeProto* FTP = dyn_cast<FunctionTypeProto>(FT);
if (!FTP || FTP->getNumArgs() != 1)
return getPersistentStopSummary();
Ted Kremenek
committed
assert (ScratchArgs.empty());
switch (func) {
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) {
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) {
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())
// "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)
return getInitMethodSummary(ME);
Ted Kremenek
committed
// Look for methods that return an owned object.
if (!isTrackedObjectType(Ctx.getCanonicalType(ME->getType())))
return 0;
Ted Kremenek
committed
if (followsFundamentalRule(s)) {
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet()
: RetEffect::MakeOwned(true);
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"),
GetNullarySelector("currentHandler", Ctx),
Ted Kremenek
committed
getPersistentSummary(RetEffect::MakeNotOwned()));
// Create the [NSAutoreleasePool addObject:] summary.
if (!isGCEnabled()) {
ScratchArgs.push_back(std::make_pair(0, Autorelease));
addClsMethSummary(&Ctx.Idents.get("NSAutoreleasePool"),
GetUnarySelector("addObject", Ctx),
getPersistentSummary(RetEffect::MakeNoRet(),
DoNothing, DoNothing));
}
}
Ted Kremenek
committed
void RetainSummaryManager::InitializeMethodSummaries() {
Ted Kremenek
committed
assert (ScratchArgs.empty());
// Create the "init" selector. It just acts as a pass-through for the
// receiver.
Ted Kremenek
committed
RetainSummary* InitSumm = getPersistentSummary(RetEffect::MakeReceiverAlias());
addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm);
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet()
: RetEffect::MakeOwned(true);
Ted Kremenek
committed
RetainSummary* Summ = getPersistentSummary(E);