2008-03-16 07:59:48 +08:00
|
|
|
// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--//
|
2008-03-06 08:08:09 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2008-03-06 18:40:09 +08:00
|
|
|
// This file defines the methods for CFRefCount, which implements
|
2008-03-06 08:08:09 +08:00
|
|
|
// a reference count checker for Core Foundation (Mac OS X).
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-05-01 07:47:44 +08:00
|
|
|
#include "clang/Basic/LangOptions.h"
|
2008-05-02 07:13:35 +08:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
2010-01-25 12:41:41 +08:00
|
|
|
#include "clang/Checker/PathSensitive/GRExprEngineBuilders.h"
|
|
|
|
#include "clang/Checker/PathSensitive/GRStateTrait.h"
|
2010-01-26 01:10:22 +08:00
|
|
|
#include "clang/Checker/BugReporter/PathDiagnostic.h"
|
2010-01-27 06:59:55 +08:00
|
|
|
#include "clang/Checker/Checkers/LocalCheckers.h"
|
2010-01-26 01:10:22 +08:00
|
|
|
#include "clang/Checker/BugReporter/PathDiagnostic.h"
|
|
|
|
#include "clang/Checker/BugReporter/BugReporter.h"
|
2010-01-25 12:41:41 +08:00
|
|
|
#include "clang/Checker/PathSensitive/SymbolManager.h"
|
|
|
|
#include "clang/Checker/PathSensitive/GRTransferFuncs.h"
|
|
|
|
#include "clang/Checker/PathSensitive/CheckerVisitor.h"
|
2010-01-27 14:13:48 +08:00
|
|
|
#include "clang/Checker/DomainSpecific/CocoaConventions.h"
|
2009-09-09 23:08:12 +08:00
|
|
|
#include "clang/AST/DeclObjC.h"
|
2009-11-26 10:38:19 +08:00
|
|
|
#include "clang/AST/StmtVisitor.h"
|
2008-03-11 14:39:11 +08:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/FoldingSet.h"
|
|
|
|
#include "llvm/ADT/ImmutableMap.h"
|
2008-10-21 23:53:15 +08:00
|
|
|
#include "llvm/ADT/ImmutableList.h"
|
2008-05-08 02:36:45 +08:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2008-05-17 02:33:44 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2008-08-13 04:41:56 +08:00
|
|
|
#include <stdarg.h>
|
2008-03-06 08:08:09 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-01-12 03:46:28 +08:00
|
|
|
using llvm::StringRef;
|
2010-01-27 14:13:48 +08:00
|
|
|
using llvm::StrInStrNoCase;
|
2008-11-06 00:54:44 +08:00
|
|
|
|
2009-04-30 07:03:22 +08:00
|
|
|
static const ObjCMethodDecl*
|
2009-09-09 23:08:12 +08:00
|
|
|
ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) {
|
2009-04-30 07:03:22 +08:00
|
|
|
ObjCInterfaceDecl *ID =
|
|
|
|
const_cast<ObjCInterfaceDecl*>(MD->getClassInterface());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 07:03:22 +08:00
|
|
|
return MD->isInstanceMethod()
|
2009-06-30 10:36:12 +08:00
|
|
|
? ID->lookupInstanceMethod(MD->getSelector())
|
|
|
|
: ID->lookupClassMethod(MD->getSelector());
|
2008-11-06 00:54:44 +08:00
|
|
|
}
|
2008-10-25 05:18:08 +08:00
|
|
|
|
2009-05-09 07:09:42 +08:00
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class GenericNodeBuilder {
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder *SNB;
|
2009-05-09 07:09:42 +08:00
|
|
|
Stmt *S;
|
|
|
|
const void *tag;
|
2009-08-06 20:48:26 +08:00
|
|
|
GREndPathNodeBuilder *ENB;
|
2009-05-09 07:09:42 +08:00
|
|
|
public:
|
2009-08-06 20:48:26 +08:00
|
|
|
GenericNodeBuilder(GRStmtNodeBuilder &snb, Stmt *s,
|
2009-05-09 07:09:42 +08:00
|
|
|
const void *t)
|
|
|
|
: SNB(&snb), S(s), tag(t), ENB(0) {}
|
2009-08-06 20:48:26 +08:00
|
|
|
|
|
|
|
GenericNodeBuilder(GREndPathNodeBuilder &enb)
|
2009-05-09 07:09:42 +08:00
|
|
|
: SNB(0), S(0), tag(0), ENB(&enb) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 20:48:26 +08:00
|
|
|
ExplodedNode *MakeNode(const GRState *state, ExplodedNode *Pred) {
|
2009-05-09 07:09:42 +08:00
|
|
|
if (SNB)
|
2009-09-09 23:08:12 +08:00
|
|
|
return SNB->generateNode(PostStmt(S, Pred->getLocationContext(), tag),
|
2009-08-15 11:17:38 +08:00
|
|
|
state, Pred);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 07:09:42 +08:00
|
|
|
assert(ENB);
|
2009-05-09 08:44:07 +08:00
|
|
|
return ENB->generateNode(state, Pred);
|
2009-05-09 07:09:42 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2008-04-10 07:49:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-06-26 05:21:56 +08:00
|
|
|
// Primitives used for constructing summaries for function/method calls.
|
2008-04-10 07:49:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// ArgEffect is used to summarize a function/method call's effect on a
|
|
|
|
/// particular argument.
|
2009-03-18 03:42:23 +08:00
|
|
|
enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing,
|
|
|
|
DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape,
|
|
|
|
NewAutoreleasePool, SelfOwn, StopTracking };
|
2008-06-26 05:21:56 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
namespace llvm {
|
2009-05-03 13:20:50 +08:00
|
|
|
template <> struct FoldingSetTrait<ArgEffect> {
|
|
|
|
static inline void Profile(const ArgEffect X, FoldingSetNodeID& ID) {
|
|
|
|
ID.AddInteger((unsigned) X);
|
|
|
|
}
|
2008-06-26 05:21:56 +08:00
|
|
|
};
|
2008-03-11 14:39:11 +08:00
|
|
|
} // end llvm namespace
|
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
/// ArgEffects summarizes the effects of a function/method call on all of
|
|
|
|
/// its arguments.
|
|
|
|
typedef llvm::ImmutableMap<unsigned,ArgEffect> ArgEffects;
|
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
namespace {
|
2008-06-26 05:21:56 +08:00
|
|
|
|
|
|
|
/// RetEffect is used to summarize a function/method call's behavior with
|
2009-09-09 23:08:12 +08:00
|
|
|
/// respect to its return value.
|
2009-11-28 14:07:30 +08:00
|
|
|
class RetEffect {
|
2008-03-11 14:39:11 +08:00
|
|
|
public:
|
2008-06-24 02:02:52 +08:00
|
|
|
enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol,
|
2009-05-13 04:06:54 +08:00
|
|
|
NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias,
|
|
|
|
OwnedWhenTrackedReceiver };
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
enum ObjKind { CF, ObjC, AnyObj };
|
2009-01-28 13:56:51 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
private:
|
2009-01-28 13:56:51 +08:00
|
|
|
Kind K;
|
|
|
|
ObjKind O;
|
|
|
|
unsigned index;
|
|
|
|
|
|
|
|
RetEffect(Kind k, unsigned idx = 0) : K(k), O(AnyObj), index(idx) {}
|
|
|
|
RetEffect(Kind k, ObjKind o) : K(k), O(o), index(0) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
public:
|
2009-01-28 13:56:51 +08:00
|
|
|
Kind getKind() const { return K; }
|
|
|
|
|
|
|
|
ObjKind getObjKind() const { return O; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
unsigned getIndex() const {
|
2008-03-11 14:39:11 +08:00
|
|
|
assert(getKind() == Alias);
|
2009-01-28 13:56:51 +08:00
|
|
|
return index;
|
2008-03-11 14:39:11 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 07:03:22 +08:00
|
|
|
bool isOwned() const {
|
2009-05-13 04:06:54 +08:00
|
|
|
return K == OwnedSymbol || K == OwnedAllocatedSymbol ||
|
|
|
|
K == OwnedWhenTrackedReceiver;
|
2009-04-30 07:03:22 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-13 04:06:54 +08:00
|
|
|
static RetEffect MakeOwnedWhenTrackedReceiver() {
|
|
|
|
return RetEffect(OwnedWhenTrackedReceiver, ObjC);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
static RetEffect MakeAlias(unsigned Idx) {
|
|
|
|
return RetEffect(Alias, Idx);
|
|
|
|
}
|
|
|
|
static RetEffect MakeReceiverAlias() {
|
|
|
|
return RetEffect(ReceiverAlias);
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-01-28 13:56:51 +08:00
|
|
|
static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) {
|
|
|
|
return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o);
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-01-28 13:56:51 +08:00
|
|
|
static RetEffect MakeNotOwned(ObjKind o) {
|
|
|
|
return RetEffect(NotOwnedSymbol, o);
|
2009-04-28 03:14:45 +08:00
|
|
|
}
|
|
|
|
static RetEffect MakeGCNotOwned() {
|
|
|
|
return RetEffect(GCNotOwnedSymbol, ObjC);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
static RetEffect MakeNoRet() {
|
|
|
|
return RetEffect(NoRet);
|
2008-06-24 02:02:52 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
2009-01-28 13:56:51 +08:00
|
|
|
ID.AddInteger((unsigned)K);
|
|
|
|
ID.AddInteger((unsigned)O);
|
|
|
|
ID.AddInteger(index);
|
2008-06-26 05:21:56 +08:00
|
|
|
}
|
2008-03-11 14:39:11 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Reference-counting logic (typestate + counts).
|
|
|
|
//===----------------------------------------------------------------------===//
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class RefVal {
|
2009-11-13 09:54:21 +08:00
|
|
|
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.
|
|
|
|
ERROR_START,
|
|
|
|
ErrorDeallocNotOwned, // -dealloc called on non-owned object.
|
|
|
|
ErrorDeallocGC, // Calling -dealloc with GC enabled.
|
|
|
|
ErrorUseAfterRelease, // Object used after released.
|
|
|
|
ErrorReleaseNotOwned, // Release of an object that was not owned.
|
|
|
|
ERROR_LEAK_START,
|
|
|
|
ErrorLeak, // A memory leak due to excessive reference counts.
|
|
|
|
ErrorLeakReturned, // A memory leak due to the returning method not having
|
|
|
|
// the correct naming conventions.
|
|
|
|
ErrorGCLeakReturned,
|
|
|
|
ErrorOverAutorelease,
|
|
|
|
ErrorReturnedNotOwned
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
Kind kind;
|
|
|
|
RetEffect::ObjKind okind;
|
|
|
|
unsigned Cnt;
|
|
|
|
unsigned ACnt;
|
|
|
|
QualType T;
|
|
|
|
|
|
|
|
RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t)
|
|
|
|
: kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {}
|
|
|
|
|
|
|
|
RefVal(Kind k, unsigned cnt = 0)
|
|
|
|
: kind(k), okind(RetEffect::AnyObj), Cnt(cnt), ACnt(0) {}
|
|
|
|
|
|
|
|
public:
|
|
|
|
Kind getKind() const { return kind; }
|
|
|
|
|
|
|
|
RetEffect::ObjKind getObjKind() const { return okind; }
|
|
|
|
|
|
|
|
unsigned getCount() const { return Cnt; }
|
|
|
|
unsigned getAutoreleaseCount() const { return ACnt; }
|
|
|
|
unsigned getCombinedCounts() const { return Cnt + ACnt; }
|
|
|
|
void clearCounts() { Cnt = 0; ACnt = 0; }
|
|
|
|
void setCount(unsigned i) { Cnt = i; }
|
|
|
|
void setAutoreleaseCount(unsigned i) { ACnt = i; }
|
|
|
|
|
|
|
|
QualType getType() const { return T; }
|
|
|
|
|
|
|
|
// Useful predicates.
|
|
|
|
|
|
|
|
static bool isError(Kind k) { return k >= ERROR_START; }
|
|
|
|
|
|
|
|
static bool isLeak(Kind k) { return k >= ERROR_LEAK_START; }
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static RefVal makeOwned(RetEffect::ObjKind o, QualType t,
|
|
|
|
unsigned Count = 1) {
|
|
|
|
return RefVal(Owned, o, Count, 0, t);
|
|
|
|
}
|
|
|
|
|
|
|
|
static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t,
|
|
|
|
unsigned Count = 0) {
|
|
|
|
return RefVal(NotOwned, o, Count, 0, t);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comparison, profiling, and pretty-printing.
|
|
|
|
|
|
|
|
bool operator==(const RefVal& X) const {
|
|
|
|
return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefVal operator-(size_t i) const {
|
|
|
|
return RefVal(getKind(), getObjKind(), getCount() - i,
|
|
|
|
getAutoreleaseCount(), getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
RefVal operator+(size_t i) const {
|
|
|
|
return RefVal(getKind(), getObjKind(), getCount() + i,
|
|
|
|
getAutoreleaseCount(), getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
RefVal operator^(Kind k) const {
|
|
|
|
return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(),
|
|
|
|
getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
RefVal autorelease() const {
|
|
|
|
return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1,
|
|
|
|
getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
|
|
|
ID.AddInteger((unsigned) kind);
|
|
|
|
ID.AddInteger(Cnt);
|
|
|
|
ID.AddInteger(ACnt);
|
|
|
|
ID.Add(T);
|
|
|
|
}
|
|
|
|
|
|
|
|
void print(llvm::raw_ostream& Out) const;
|
|
|
|
};
|
|
|
|
|
|
|
|
void RefVal::print(llvm::raw_ostream& Out) const {
|
|
|
|
if (!T.isNull())
|
|
|
|
Out << "Tracked Type:" << T.getAsString() << '\n';
|
|
|
|
|
|
|
|
switch (getKind()) {
|
|
|
|
default: assert(false);
|
|
|
|
case Owned: {
|
|
|
|
Out << "Owned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case NotOwned: {
|
|
|
|
Out << "NotOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ReturnedOwned: {
|
|
|
|
Out << "ReturnedOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ReturnedNotOwned: {
|
|
|
|
Out << "ReturnedNotOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Released:
|
|
|
|
Out << "Released";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ErrorDeallocGC:
|
|
|
|
Out << "-dealloc (GC)";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ErrorDeallocNotOwned:
|
|
|
|
Out << "-dealloc (not-owned)";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ErrorLeak:
|
|
|
|
Out << "Leaked";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ErrorLeakReturned:
|
|
|
|
Out << "Leaked (Bad naming)";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ErrorGCLeakReturned:
|
|
|
|
Out << "Leaked (GC-ed at return)";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ErrorUseAfterRelease:
|
|
|
|
Out << "Use-After-Release [ERROR]";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ErrorReleaseNotOwned:
|
|
|
|
Out << "Release of Not-Owned [ERROR]";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::ErrorOverAutorelease:
|
|
|
|
Out << "Over autoreleased";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::ErrorReturnedNotOwned:
|
|
|
|
Out << "Non-owned object returned instead of owned";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ACnt) {
|
|
|
|
Out << " [ARC +" << ACnt << ']';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} //end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// RefBindings - State used to track object reference counts.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings;
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
template<>
|
|
|
|
struct GRStateTrait<RefBindings> : public GRStatePartialTrait<RefBindings> {
|
2009-11-13 09:58:01 +08:00
|
|
|
static void* GDMIndex() {
|
|
|
|
static int RefBIndex = 0;
|
|
|
|
return &RefBIndex;
|
|
|
|
}
|
2009-11-13 09:54:21 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Summaries
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class RetainSummary {
|
2008-05-06 23:44:25 +08:00
|
|
|
/// 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'.
|
2009-05-03 13:20:50 +08:00
|
|
|
ArgEffects Args;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 23:44:25 +08:00
|
|
|
/// DefaultArgEffect - The default ArgEffect to apply to arguments that
|
|
|
|
/// do not have an entry in Args.
|
|
|
|
ArgEffect DefaultArgEffect;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// Receiver - If this summary applies to an Objective-C message expression,
|
|
|
|
/// this is the effect applied to the state of the receiver.
|
2008-05-06 10:26:56 +08:00
|
|
|
ArgEffect Receiver;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// 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.
|
2008-03-11 14:39:11 +08:00
|
|
|
RetEffect Ret;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-07-19 01:24:20 +08:00
|
|
|
/// EndPath - Indicates that execution of this method/function should
|
|
|
|
/// terminate the simulation of a path.
|
|
|
|
bool EndPath;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
public:
|
2009-05-03 13:20:50 +08:00
|
|
|
RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff,
|
2008-07-19 01:24:20 +08:00
|
|
|
ArgEffect ReceiverEff, bool endpath = false)
|
|
|
|
: Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R),
|
2009-09-09 23:08:12 +08:00
|
|
|
EndPath(endpath) {}
|
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// getArg - Return the argument effect on the argument specified by
|
|
|
|
/// idx (starting from 0).
|
2008-03-12 01:48:22 +08:00
|
|
|
ArgEffect getArg(unsigned idx) const {
|
2009-05-03 13:20:50 +08:00
|
|
|
if (const ArgEffect *AE = Args.lookup(idx))
|
|
|
|
return *AE;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 23:44:25 +08:00
|
|
|
return DefaultArgEffect;
|
2008-03-12 01:48:22 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 13:31:22 +08:00
|
|
|
/// setDefaultArgEffect - Set the default argument effect.
|
|
|
|
void setDefaultArgEffect(ArgEffect E) {
|
|
|
|
DefaultArgEffect = E;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 13:31:22 +08:00
|
|
|
/// setArg - Set the argument effect on the argument specified by idx.
|
|
|
|
void setArgEffect(ArgEffects::Factory& AF, unsigned idx, ArgEffect E) {
|
|
|
|
Args = AF.Add(Args, idx, E);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// getRetEffect - Returns the effect on the return value of the call.
|
2009-05-03 13:20:50 +08:00
|
|
|
RetEffect getRetEffect() const { return Ret; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 13:31:22 +08:00
|
|
|
/// setRetEffect - Set the effect of the return value of the call.
|
|
|
|
void setRetEffect(RetEffect E) { Ret = E; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-07-19 01:24:20 +08:00
|
|
|
/// isEndPath - Returns true if executing the given method/function should
|
|
|
|
/// terminate the path.
|
|
|
|
bool isEndPath() const { return EndPath; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// getReceiverEffect - Returns the effect on the receiver of the call.
|
|
|
|
/// This is only meaningful if the summary applies to an ObjCMessageExpr*.
|
2009-05-03 13:20:50 +08:00
|
|
|
ArgEffect getReceiverEffect() const { return Receiver; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 13:31:22 +08:00
|
|
|
/// setReceiverEffect - Set the effect on the receiver of the call.
|
|
|
|
void setReceiverEffect(ArgEffect E) { Receiver = E; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
typedef ArgEffects::iterator ExprIterator;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
ExprIterator begin_args() const { return Args.begin(); }
|
|
|
|
ExprIterator end_args() const { return Args.end(); }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects A,
|
2008-05-06 23:44:25 +08:00
|
|
|
RetEffect RetEff, ArgEffect DefaultEff,
|
2008-07-19 01:39:56 +08:00
|
|
|
ArgEffect ReceiverEff, bool EndPath) {
|
2009-05-03 13:20:50 +08:00
|
|
|
ID.Add(A);
|
2008-05-06 10:26:56 +08:00
|
|
|
ID.Add(RetEff);
|
2008-05-06 23:44:25 +08:00
|
|
|
ID.AddInteger((unsigned) DefaultEff);
|
2008-05-06 10:26:56 +08:00
|
|
|
ID.AddInteger((unsigned) ReceiverEff);
|
2008-07-19 01:39:56 +08:00
|
|
|
ID.AddInteger((unsigned) EndPath);
|
2008-03-11 14:39:11 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
2008-07-19 01:39:56 +08:00
|
|
|
Profile(ID, Args, Ret, DefaultArgEffect, Receiver, EndPath);
|
2008-03-11 14:39:11 +08:00
|
|
|
}
|
|
|
|
};
|
2008-06-24 07:30:29 +08:00
|
|
|
} // end anonymous namespace
|
2008-03-11 14:39:11 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Data structures for constructing summaries.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-06-24 07:30:29 +08:00
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class ObjCSummaryKey {
|
2008-06-26 05:21:56 +08:00
|
|
|
IdentifierInfo* II;
|
|
|
|
Selector S;
|
2009-09-09 23:08:12 +08:00
|
|
|
public:
|
2008-06-26 05:21:56 +08:00
|
|
|
ObjCSummaryKey(IdentifierInfo* ii, Selector s)
|
|
|
|
: II(ii), S(s) {}
|
|
|
|
|
2009-04-30 07:03:22 +08:00
|
|
|
ObjCSummaryKey(const ObjCInterfaceDecl* d, Selector s)
|
2008-06-26 05:21:56 +08:00
|
|
|
: II(d ? d->getIdentifier() : 0), S(s) {}
|
2009-05-14 02:16:01 +08:00
|
|
|
|
|
|
|
ObjCSummaryKey(const ObjCInterfaceDecl* d, IdentifierInfo *ii, Selector s)
|
|
|
|
: II(d ? d->getIdentifier() : ii), S(s) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
ObjCSummaryKey(Selector s)
|
|
|
|
: II(0), S(s) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
IdentifierInfo* getIdentifier() const { return II; }
|
|
|
|
Selector getSelector() const { return S; }
|
|
|
|
};
|
2008-06-24 07:30:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace llvm {
|
2008-06-26 05:21:56 +08:00
|
|
|
template <> struct DenseMapInfo<ObjCSummaryKey> {
|
|
|
|
static inline ObjCSummaryKey getEmptyKey() {
|
|
|
|
return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getEmptyKey(),
|
|
|
|
DenseMapInfo<Selector>::getEmptyKey());
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
static inline ObjCSummaryKey getTombstoneKey() {
|
|
|
|
return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getTombstoneKey(),
|
2009-09-09 23:08:12 +08:00
|
|
|
DenseMapInfo<Selector>::getTombstoneKey());
|
2008-06-26 05:21:56 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
static unsigned getHashValue(const ObjCSummaryKey &V) {
|
|
|
|
return (DenseMapInfo<IdentifierInfo*>::getHashValue(V.getIdentifier())
|
2009-09-09 23:08:12 +08:00
|
|
|
& 0x88888888)
|
2008-06-26 05:21:56 +08:00
|
|
|
| (DenseMapInfo<Selector>::getHashValue(V.getSelector())
|
|
|
|
& 0x55555555);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) {
|
|
|
|
return DenseMapInfo<IdentifierInfo*>::isEqual(LHS.getIdentifier(),
|
|
|
|
RHS.getIdentifier()) &&
|
|
|
|
DenseMapInfo<Selector>::isEqual(LHS.getSelector(),
|
|
|
|
RHS.getSelector());
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
};
|
2009-12-15 15:26:51 +08:00
|
|
|
template <>
|
|
|
|
struct isPodLike<ObjCSummaryKey> { static const bool value = true; };
|
2008-06-26 05:21:56 +08:00
|
|
|
} // end llvm namespace
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class ObjCSummaryCache {
|
2008-06-26 05:21:56 +08:00
|
|
|
typedef llvm::DenseMap<ObjCSummaryKey, RetainSummary*> MapTy;
|
|
|
|
MapTy M;
|
|
|
|
public:
|
|
|
|
ObjCSummaryCache() {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
RetainSummary* find(const ObjCInterfaceDecl* D, IdentifierInfo *ClsName,
|
2009-04-30 07:03:22 +08:00
|
|
|
Selector S) {
|
2009-04-29 13:04:30 +08:00
|
|
|
// Lookup the method using the decl for the class @interface. If we
|
|
|
|
// have no decl, lookup using the class name.
|
|
|
|
return D ? find(D, S) : find(ClsName, S);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
RetainSummary* find(const ObjCInterfaceDecl* D, Selector S) {
|
2008-06-26 05:21:56 +08:00
|
|
|
// 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);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
if (I != M.end() || !D)
|
2009-07-22 07:27:57 +08:00
|
|
|
return I->second;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
// 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;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
if (!C)
|
2009-07-22 07:27:57 +08:00
|
|
|
return NULL;
|
2008-06-24 07:30:29 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Cache the summary with original key to make the next lookup faster
|
2008-06-26 05:21:56 +08:00
|
|
|
// and return the iterator.
|
2009-07-22 07:27:57 +08:00
|
|
|
RetainSummary *Summ = I->second;
|
|
|
|
M[K] = Summ;
|
|
|
|
return Summ;
|
2008-06-26 05:21:56 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-13 04:41:56 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
RetainSummary* find(Expr* Receiver, Selector S) {
|
2008-06-26 05:21:56 +08:00
|
|
|
return find(getReceiverDecl(Receiver), S);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
RetainSummary* find(IdentifierInfo* II, Selector S) {
|
2008-06-26 05:21:56 +08:00
|
|
|
// FIXME: Class method lookup. Right now we dont' have a good way
|
|
|
|
// of going between IdentifierInfo* and the class hierarchy.
|
2009-07-22 07:27:57 +08:00
|
|
|
MapTy::iterator I = M.find(ObjCSummaryKey(II, S));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
if (I == M.end())
|
|
|
|
I = M.find(ObjCSummaryKey(S));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
return I == M.end() ? NULL : I->second;
|
2008-06-26 05:21:56 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
const ObjCInterfaceDecl* getReceiverDecl(Expr* E) {
|
2009-07-11 07:34:53 +08:00
|
|
|
if (const ObjCObjectPointerType* PT =
|
2009-09-22 07:43:11 +08:00
|
|
|
E->getType()->getAs<ObjCObjectPointerType>())
|
2009-07-11 07:34:53 +08:00
|
|
|
return PT->getInterfaceDecl();
|
|
|
|
|
|
|
|
return NULL;
|
2008-06-26 05:21:56 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
RetainSummary*& operator[](ObjCMessageExpr* ME) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
Selector S = ME->getSelector();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
if (Expr* Receiver = ME->getReceiver()) {
|
2009-07-11 07:34:53 +08:00
|
|
|
const ObjCInterfaceDecl* OD = getReceiverDecl(Receiver);
|
2008-06-26 05:21:56 +08:00
|
|
|
return OD ? M[ObjCSummaryKey(OD->getIdentifier(), S)] : M[S];
|
2008-06-24 07:30:29 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
return M[ObjCSummaryKey(ME->getClassName(), S)];
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
RetainSummary*& operator[](ObjCSummaryKey K) {
|
|
|
|
return M[K];
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
RetainSummary*& operator[](Selector S) {
|
|
|
|
return M[ ObjCSummaryKey(S) ];
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
};
|
2008-06-26 05:21:56 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Data structures for managing collections of summaries.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-06-24 07:30:29 +08:00
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class RetainSummaryManager {
|
2008-05-06 06:11:16 +08:00
|
|
|
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Typedefs.
|
|
|
|
//==-----------------------------------------------------------------==//
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
typedef llvm::DenseMap<FunctionDecl*, RetainSummary*>
|
|
|
|
FuncSummariesTy;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-24 07:30:29 +08:00
|
|
|
typedef ObjCSummaryCache ObjCMethodSummariesTy;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Data.
|
|
|
|
//==-----------------------------------------------------------------==//
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// Ctx - The ASTContext object for the analyzed ASTs.
|
2008-04-29 13:33:51 +08:00
|
|
|
ASTContext& Ctx;
|
2008-07-02 01:21:27 +08:00
|
|
|
|
2008-07-10 02:11:16 +08:00
|
|
|
/// CFDictionaryCreateII - An IdentifierInfo* representing the indentifier
|
|
|
|
/// "CFDictionaryCreate".
|
|
|
|
IdentifierInfo* CFDictionaryCreateII;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// GCEnabled - Records whether or not the analyzed code runs in GC mode.
|
2008-04-29 13:33:51 +08:00
|
|
|
const bool GCEnabled;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// FuncSummaries - A map from FunctionDecls to summaries.
|
2009-09-09 23:08:12 +08:00
|
|
|
FuncSummariesTy FuncSummaries;
|
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// ObjCClassMethodSummaries - A map from selectors (for instance methods)
|
|
|
|
/// to summaries.
|
2008-06-24 06:21:20 +08:00
|
|
|
ObjCMethodSummariesTy ObjCClassMethodSummaries;
|
2008-05-06 06:11:16 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// ObjCMethodSummaries - A map from selectors to summaries.
|
2008-06-24 06:21:20 +08:00
|
|
|
ObjCMethodSummariesTy ObjCMethodSummaries;
|
2008-05-06 06:11:16 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects,
|
|
|
|
/// and all other data used by the checker.
|
2008-05-06 06:11:16 +08:00
|
|
|
llvm::BumpPtrAllocator BPAlloc;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
/// AF - A factory for ArgEffects objects.
|
2009-09-09 23:08:12 +08:00
|
|
|
ArgEffects::Factory AF;
|
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// ScratchArgs - A holding buffer for construct ArgEffects.
|
2008-05-06 06:11:16 +08:00
|
|
|
ArgEffects ScratchArgs;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-08 07:40:42 +08:00
|
|
|
/// ObjCAllocRetE - Default return effect for methods returning Objective-C
|
|
|
|
/// objects.
|
|
|
|
RetEffect ObjCAllocRetE;
|
2009-06-06 07:18:01 +08:00
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
/// ObjCInitRetE - Default return effect for init methods returning
|
2009-08-20 13:13:36 +08:00
|
|
|
/// Objective-C objects.
|
2009-06-06 07:18:01 +08:00
|
|
|
RetEffect ObjCInitRetE;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 12:57:00 +08:00
|
|
|
RetainSummary DefaultSummary;
|
2008-05-07 02:11:36 +08:00
|
|
|
RetainSummary* StopSummary;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Methods.
|
|
|
|
//==-----------------------------------------------------------------==//
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// getArgEffects - Returns a persistent ArgEffects object based on the
|
|
|
|
/// data in ScratchArgs.
|
2009-05-03 13:20:50 +08:00
|
|
|
ArgEffects getArgEffects();
|
2008-03-12 09:21:45 +08:00
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable };
|
|
|
|
|
2008-10-23 09:56:15 +08:00
|
|
|
public:
|
2009-05-13 04:06:54 +08:00
|
|
|
RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
|
|
|
|
|
2009-05-04 13:31:22 +08:00
|
|
|
RetainSummary *getDefaultSummary() {
|
|
|
|
RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
|
|
|
|
return new (Summ) RetainSummary(DefaultSummary);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-24 00:51:39 +08:00
|
|
|
RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD);
|
2009-09-09 23:08:12 +08:00
|
|
|
RetainSummary* getCFSummaryGetRule(FunctionDecl* FD);
|
2010-02-09 02:38:55 +08:00
|
|
|
RetainSummary* getCFCreateGetRuleSummary(FunctionDecl* FD, StringRef FName);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff,
|
2008-05-06 23:44:25 +08:00
|
|
|
ArgEffect ReceiverEff = DoNothing,
|
2008-07-19 01:24:20 +08:00
|
|
|
ArgEffect DefaultEff = MayEscape,
|
|
|
|
bool isEndPath = false);
|
2008-10-29 12:07:07 +08:00
|
|
|
|
2008-05-06 10:26:56 +08:00
|
|
|
RetainSummary* getPersistentSummary(RetEffect RE,
|
2008-05-06 23:44:25 +08:00
|
|
|
ArgEffect ReceiverEff = DoNothing,
|
2008-05-23 01:31:13 +08:00
|
|
|
ArgEffect DefaultEff = MayEscape) {
|
2008-05-06 23:44:25 +08:00
|
|
|
return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff);
|
2008-05-06 08:30:21 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-29 13:04:30 +08:00
|
|
|
RetainSummary *getPersistentStopSummary() {
|
2008-05-07 02:11:36 +08:00
|
|
|
if (StopSummary)
|
|
|
|
return StopSummary;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-07 02:11:36 +08:00
|
|
|
StopSummary = getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
StopTracking, StopTracking);
|
2008-10-29 12:07:07 +08:00
|
|
|
|
2008-05-07 02:11:36 +08:00
|
|
|
return StopSummary;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2008-05-06 12:20:12 +08:00
|
|
|
|
2009-04-29 13:04:30 +08:00
|
|
|
RetainSummary *getInitMethodSummary(QualType RetTy);
|
2008-05-06 07:55:01 +08:00
|
|
|
|
2008-06-24 06:21:20 +08:00
|
|
|
void InitializeClassMethodSummaries();
|
|
|
|
void InitializeMethodSummaries();
|
2008-10-23 09:56:15 +08:00
|
|
|
private:
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-07-19 01:24:20 +08:00
|
|
|
void addClsMethSummary(IdentifierInfo* ClsII, Selector S,
|
|
|
|
RetainSummary* Summ) {
|
|
|
|
ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) {
|
|
|
|
ObjCClassMethodSummaries[S] = Summ;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
void addNSObjectMethSummary(Selector S, RetainSummary *Summ) {
|
|
|
|
ObjCMethodSummaries[S] = Summ;
|
|
|
|
}
|
2009-03-05 07:30:42 +08:00
|
|
|
|
|
|
|
void addClassMethSummary(const char* Cls, const char* nullaryName,
|
|
|
|
RetainSummary *Summ) {
|
|
|
|
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
|
|
|
|
Selector S = GetNullarySelector(nullaryName, Ctx);
|
|
|
|
ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-25 10:54:57 +08:00
|
|
|
void addInstMethSummary(const char* Cls, const char* nullaryName,
|
|
|
|
RetainSummary *Summ) {
|
|
|
|
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
|
|
|
|
Selector S = GetNullarySelector(nullaryName, Ctx);
|
|
|
|
ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-25 01:50:11 +08:00
|
|
|
Selector generateSelector(va_list argp) {
|
2008-08-13 02:30:56 +08:00
|
|
|
llvm::SmallVector<IdentifierInfo*, 10> II;
|
2009-04-25 01:50:11 +08:00
|
|
|
|
2008-08-13 02:30:56 +08:00
|
|
|
while (const char* s = va_arg(argp, const char*))
|
|
|
|
II.push_back(&Ctx.Idents.get(s));
|
2009-04-25 01:50:11 +08:00
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
return Ctx.Selectors.getSelector(II.size(), &II[0]);
|
2009-04-25 01:50:11 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-25 01:50:11 +08:00
|
|
|
void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries,
|
|
|
|
RetainSummary* Summ, va_list argp) {
|
|
|
|
Selector S = generateSelector(argp);
|
|
|
|
Summaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
2008-07-19 01:24:20 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-13 02:48:50 +08:00
|
|
|
void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) {
|
|
|
|
va_list argp;
|
|
|
|
va_start(argp, Summ);
|
2009-04-25 01:50:11 +08:00
|
|
|
addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp);
|
2009-09-09 23:08:12 +08:00
|
|
|
va_end(argp);
|
2008-08-13 02:48:50 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-25 01:50:11 +08:00
|
|
|
void addClsMethSummary(const char* Cls, RetainSummary* Summ, ...) {
|
|
|
|
va_list argp;
|
|
|
|
va_start(argp, Summ);
|
|
|
|
addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp);
|
|
|
|
va_end(argp);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-25 01:50:11 +08:00
|
|
|
void addClsMethSummary(IdentifierInfo *II, RetainSummary* Summ, ...) {
|
|
|
|
va_list argp;
|
|
|
|
va_start(argp, Summ);
|
|
|
|
addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp);
|
|
|
|
va_end(argp);
|
|
|
|
}
|
|
|
|
|
2008-08-13 02:30:56 +08:00
|
|
|
void addPanicSummary(const char* Cls, ...) {
|
2009-05-03 13:20:50 +08:00
|
|
|
RetainSummary* Summ = getPersistentSummary(AF.GetEmptyMap(),
|
|
|
|
RetEffect::MakeNoRet(),
|
2008-08-13 02:30:56 +08:00
|
|
|
DoNothing, DoNothing, true);
|
|
|
|
va_list argp;
|
|
|
|
va_start (argp, Cls);
|
2009-04-25 01:50:11 +08:00
|
|
|
addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp);
|
2008-08-13 02:30:56 +08:00
|
|
|
va_end(argp);
|
2009-04-25 01:50:11 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
public:
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
RetainSummaryManager(ASTContext& ctx, bool gcenabled)
|
2008-07-02 01:21:27 +08:00
|
|
|
: Ctx(ctx),
|
2008-07-10 02:11:16 +08:00
|
|
|
CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")),
|
2009-05-03 13:20:50 +08:00
|
|
|
GCEnabled(gcenabled), AF(BPAlloc), ScratchArgs(AF.GetEmptyMap()),
|
2009-05-08 07:40:42 +08:00
|
|
|
ObjCAllocRetE(gcenabled ? RetEffect::MakeGCNotOwned()
|
|
|
|
: RetEffect::MakeOwned(RetEffect::ObjC, true)),
|
2009-06-12 02:17:24 +08:00
|
|
|
ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned()
|
|
|
|
: RetEffect::MakeOwnedWhenTrackedReceiver()),
|
2009-05-04 12:57:00 +08:00
|
|
|
DefaultSummary(AF.GetEmptyMap() /* per-argument effects (none) */,
|
|
|
|
RetEffect::MakeNoRet() /* return effect */,
|
2009-05-12 02:30:24 +08:00
|
|
|
MayEscape, /* default argument effect */
|
|
|
|
DoNothing /* receiver effect */),
|
2009-05-03 13:20:50 +08:00
|
|
|
StopSummary(0) {
|
2008-06-26 05:21:56 +08:00
|
|
|
|
|
|
|
InitializeClassMethodSummaries();
|
|
|
|
InitializeMethodSummaries();
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
~RetainSummaryManager();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
RetainSummary* getSummary(FunctionDecl* FD);
|
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RetainSummary *getInstanceMethodSummary(const ObjCMessageExpr *ME,
|
|
|
|
const GRState *state,
|
|
|
|
const LocationContext *LC);
|
|
|
|
|
|
|
|
RetainSummary* getInstanceMethodSummary(const ObjCMessageExpr* ME,
|
2009-04-30 07:03:22 +08:00
|
|
|
const ObjCInterfaceDecl* ID) {
|
2009-04-30 01:09:14 +08:00
|
|
|
return getInstanceMethodSummary(ME->getSelector(), ME->getClassName(),
|
2009-09-09 23:08:12 +08:00
|
|
|
ID, ME->getMethodDecl(), ME->getType());
|
2009-04-29 13:04:30 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 01:09:14 +08:00
|
|
|
RetainSummary* getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName,
|
2009-04-30 07:03:22 +08:00
|
|
|
const ObjCInterfaceDecl* ID,
|
|
|
|
const ObjCMethodDecl *MD,
|
|
|
|
QualType RetTy);
|
2009-04-29 08:42:39 +08:00
|
|
|
|
|
|
|
RetainSummary *getClassMethodSummary(Selector S, IdentifierInfo *ClsName,
|
2009-04-30 07:03:22 +08:00
|
|
|
const ObjCInterfaceDecl *ID,
|
|
|
|
const ObjCMethodDecl *MD,
|
|
|
|
QualType RetTy);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RetainSummary *getClassMethodSummary(const ObjCMessageExpr *ME) {
|
2009-04-29 08:42:39 +08:00
|
|
|
return getClassMethodSummary(ME->getSelector(), ME->getClassName(),
|
|
|
|
ME->getClassInfo().first,
|
|
|
|
ME->getMethodDecl(), ME->getType());
|
|
|
|
}
|
2009-04-30 01:17:48 +08:00
|
|
|
|
|
|
|
/// getMethodSummary - This version of getMethodSummary is used to query
|
|
|
|
/// the summary for the current method being analyzed.
|
2009-04-30 07:03:22 +08:00
|
|
|
RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) {
|
|
|
|
// FIXME: Eventually this should be unneeded.
|
|
|
|
const ObjCInterfaceDecl *ID = MD->getClassInterface();
|
2009-04-30 13:41:14 +08:00
|
|
|
Selector S = MD->getSelector();
|
2009-04-30 01:17:48 +08:00
|
|
|
IdentifierInfo *ClsName = ID->getIdentifier();
|
|
|
|
QualType ResultTy = MD->getResultType();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Resolve the method decl last.
|
2009-06-30 10:36:12 +08:00
|
|
|
if (const ObjCMethodDecl *InterfaceMD = ResolveToInterfaceMethodDecl(MD))
|
2009-04-30 13:47:23 +08:00
|
|
|
MD = InterfaceMD;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 01:17:48 +08:00
|
|
|
if (MD->isInstanceMethod())
|
|
|
|
return getInstanceMethodSummary(S, ClsName, ID, MD, ResultTy);
|
|
|
|
else
|
|
|
|
return getClassMethodSummary(S, ClsName, ID, MD, ResultTy);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 07:03:22 +08:00
|
|
|
RetainSummary* getCommonMethodSummary(const ObjCMethodDecl* MD,
|
|
|
|
Selector S, QualType RetTy);
|
|
|
|
|
2009-05-09 10:58:13 +08:00
|
|
|
void updateSummaryFromAnnotations(RetainSummary &Summ,
|
|
|
|
const ObjCMethodDecl *MD);
|
|
|
|
|
|
|
|
void updateSummaryFromAnnotations(RetainSummary &Summ,
|
|
|
|
const FunctionDecl *FD);
|
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
bool isGCEnabled() const { return GCEnabled; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 13:31:22 +08:00
|
|
|
RetainSummary *copySummary(RetainSummary *OldSumm) {
|
|
|
|
RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
|
|
|
|
new (Summ) RetainSummary(*OldSumm);
|
|
|
|
return Summ;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2008-03-11 14:39:11 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Implementation of checker data structures.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
RetainSummaryManager::~RetainSummaryManager() {}
|
2008-03-12 09:21:45 +08:00
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
ArgEffects RetainSummaryManager::getArgEffects() {
|
|
|
|
ArgEffects AE = ScratchArgs;
|
|
|
|
ScratchArgs = AF.GetEmptyMap();
|
|
|
|
return AE;
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
|
|
|
|
2008-05-06 10:26:56 +08:00
|
|
|
RetainSummary*
|
2009-05-03 13:20:50 +08:00
|
|
|
RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff,
|
2008-05-06 23:44:25 +08:00
|
|
|
ArgEffect ReceiverEff,
|
2008-07-19 01:24:20 +08:00
|
|
|
ArgEffect DefaultEff,
|
2009-09-09 23:08:12 +08:00
|
|
|
bool isEndPath) {
|
2008-04-25 01:22:33 +08:00
|
|
|
// Create the summary and return it.
|
2009-05-04 12:30:18 +08:00
|
|
|
RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
|
2008-07-19 01:24:20 +08:00
|
|
|
new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff, isEndPath);
|
2008-03-12 09:21:45 +08:00
|
|
|
return Summ;
|
|
|
|
}
|
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Summary creation for functions (largely uses of Core Foundation).
|
|
|
|
//===----------------------------------------------------------------------===//
|
2008-03-12 09:21:45 +08:00
|
|
|
|
2010-02-09 02:38:55 +08:00
|
|
|
static bool isRetain(FunctionDecl* FD, StringRef FName) {
|
|
|
|
return FName.endswith("Retain");
|
2009-01-13 05:45:02 +08:00
|
|
|
}
|
|
|
|
|
2010-02-09 02:38:55 +08:00
|
|
|
static bool isRelease(FunctionDecl* FD, StringRef FName) {
|
|
|
|
return FName.endswith("Release");
|
2009-01-13 05:45:02 +08:00
|
|
|
}
|
|
|
|
|
2008-06-24 11:56:45 +08:00
|
|
|
RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) {
|
2008-04-25 01:22:33 +08:00
|
|
|
// Look up a summary in our cache of FunctionDecls -> Summaries.
|
2008-05-06 06:11:16 +08:00
|
|
|
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
|
|
|
|
if (I != FuncSummaries.end())
|
2008-04-25 01:22:33 +08:00
|
|
|
return I->second;
|
|
|
|
|
2009-05-04 23:34:07 +08:00
|
|
|
// No summary? Generate one.
|
2009-01-13 05:45:02 +08:00
|
|
|
RetainSummary *S = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-07-16 00:50:12 +08:00
|
|
|
do {
|
2009-01-13 05:45:02 +08:00
|
|
|
// We generate "stop" summaries for implicitly defined functions.
|
|
|
|
if (FD->isImplicit()) {
|
|
|
|
S = getPersistentStopSummary();
|
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-09-22 07:43:11 +08:00
|
|
|
// [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the
|
2009-01-17 02:40:33 +08:00
|
|
|
// function's type.
|
2009-09-22 07:43:11 +08:00
|
|
|
const FunctionType* FT = FD->getType()->getAs<FunctionType>();
|
2009-12-16 14:06:43 +08:00
|
|
|
const IdentifierInfo *II = FD->getIdentifier();
|
|
|
|
if (!II)
|
|
|
|
break;
|
2010-02-09 02:38:55 +08:00
|
|
|
|
|
|
|
StringRef FName = II->getName();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-06 06:11:14 +08:00
|
|
|
// Strip away preceding '_'. Doing this here will effect all the checks
|
|
|
|
// down below.
|
2010-02-09 02:38:55 +08:00
|
|
|
FName = FName.substr(FName.find_first_not_of('_'));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-13 05:45:02 +08:00
|
|
|
// Inspect the result type.
|
|
|
|
QualType RetTy = FT->getResultType();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-13 05:45:02 +08:00
|
|
|
// FIXME: This should all be refactored into a chain of "summary lookup"
|
|
|
|
// filters.
|
2009-10-14 08:27:24 +08:00
|
|
|
assert(ScratchArgs.isEmpty());
|
2010-02-09 00:45:01 +08:00
|
|
|
|
2010-02-09 02:38:55 +08:00
|
|
|
if (FName == "pthread_create") {
|
|
|
|
// Part of: <rdar://problem/7299394>. This will be addressed
|
|
|
|
// better with IPA.
|
|
|
|
S = getPersistentStopSummary();
|
|
|
|
} else if (FName == "NSMakeCollectable") {
|
|
|
|
// Handle: id NSMakeCollectable(CFTypeRef)
|
|
|
|
S = (RetTy->isObjCIdType())
|
|
|
|
? getUnarySummary(FT, cfmakecollectable)
|
|
|
|
: getPersistentStopSummary();
|
|
|
|
} else if (FName == "IOBSDNameMatching" ||
|
|
|
|
FName == "IOServiceMatching" ||
|
|
|
|
FName == "IOServiceNameMatching" ||
|
|
|
|
FName == "IORegistryEntryIDMatching" ||
|
|
|
|
FName == "IOOpenFirmwarePathMatching") {
|
|
|
|
// Part of <rdar://problem/6961230>. (IOKit)
|
|
|
|
// This should be addressed using a API table.
|
|
|
|
S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true),
|
|
|
|
DoNothing, DoNothing);
|
|
|
|
} else if (FName == "IOServiceGetMatchingService" ||
|
|
|
|
FName == "IOServiceGetMatchingServices") {
|
|
|
|
// 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.
|
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 1, DecRef);
|
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
|
|
|
} else if (FName == "IOServiceAddNotification" ||
|
|
|
|
FName == "IOServiceAddMatchingNotification") {
|
|
|
|
// Part of <rdar://problem/6961230>. (IOKit)
|
|
|
|
// This should be addressed using a API table.
|
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 2, DecRef);
|
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
|
|
|
} else if (FName == "CVPixelBufferCreateWithBytes") {
|
|
|
|
// FIXES: <rdar://problem/7283567>
|
|
|
|
// Eventually this can be improved by recognizing that the pixel
|
|
|
|
// buffer passed to CVPixelBufferCreateWithBytes is released via
|
|
|
|
// a callback and doing full IPA to make sure this is done correctly.
|
|
|
|
// FIXME: This function has an out parameter that returns an
|
|
|
|
// allocated object.
|
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 7, StopTracking);
|
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
|
|
|
} else if (FName == "CGBitmapContextCreateWithData") {
|
|
|
|
// FIXES: <rdar://problem/7358899>
|
|
|
|
// Eventually this can be improved by recognizing that 'releaseInfo'
|
|
|
|
// passed to CGBitmapContextCreateWithData is released via
|
|
|
|
// a callback and doing full IPA to make sure this is done correctly.
|
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 8, StopTracking);
|
|
|
|
S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true),
|
|
|
|
DoNothing, DoNothing);
|
|
|
|
} else if (FName == "CVPixelBufferCreateWithPlanarBytes") {
|
|
|
|
// FIXES: <rdar://problem/7283567>
|
|
|
|
// Eventually this can be improved by recognizing that the pixel
|
|
|
|
// buffer passed to CVPixelBufferCreateWithPlanarBytes is released
|
|
|
|
// via a callback and doing full IPA to make sure this is done
|
|
|
|
// correctly.
|
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 12, StopTracking);
|
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
2009-06-12 02:17:24 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-12 02:17:24 +08:00
|
|
|
// Did we get a summary?
|
|
|
|
if (S)
|
|
|
|
break;
|
2009-03-18 06:43:44 +08:00
|
|
|
|
|
|
|
// Enable this code once the semantics of NSDeallocateObject are resolved
|
|
|
|
// for GC. <rdar://problem/6619988>
|
|
|
|
#if 0
|
|
|
|
// Handle: NSDeallocateObject(id anObject);
|
|
|
|
// This method does allow 'nil' (although we don't check it now).
|
2009-09-09 23:08:12 +08:00
|
|
|
if (strcmp(FName, "NSDeallocateObject") == 0) {
|
2009-03-18 06:43:44 +08:00
|
|
|
return RetTy == Ctx.VoidTy
|
|
|
|
? getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Dealloc)
|
|
|
|
: getPersistentStopSummary();
|
|
|
|
}
|
|
|
|
#endif
|
2009-01-13 05:45:02 +08:00
|
|
|
|
|
|
|
if (RetTy->isPointerType()) {
|
|
|
|
// For CoreFoundation ('CF') types.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isRefType(RetTy, "CF", FName)) {
|
2009-01-13 05:45:02 +08:00
|
|
|
if (isRetain(FD, FName))
|
|
|
|
S = getUnarySummary(FT, cfretain);
|
2010-02-09 02:38:55 +08:00
|
|
|
else if (FName.find("MakeCollectable") != StringRef::npos)
|
2009-01-13 05:45:02 +08:00
|
|
|
S = getUnarySummary(FT, cfmakecollectable);
|
2009-09-09 23:08:12 +08:00
|
|
|
else
|
2009-01-13 05:45:02 +08:00
|
|
|
S = getCFCreateGetRuleSummary(FD, FName);
|
|
|
|
|
2008-07-16 00:50:12 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-01-13 05:45:02 +08:00
|
|
|
|
|
|
|
// For CoreGraphics ('CG') types.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isRefType(RetTy, "CG", FName)) {
|
2009-01-13 05:45:02 +08:00
|
|
|
if (isRetain(FD, FName))
|
|
|
|
S = getUnarySummary(FT, cfretain);
|
|
|
|
else
|
|
|
|
S = getCFCreateGetRuleSummary(FD, FName);
|
|
|
|
|
2008-07-16 00:50:12 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-01-13 05:45:02 +08:00
|
|
|
|
|
|
|
// For the Disk Arbitration API (DiskArbitration/DADisk.h)
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isRefType(RetTy, "DADisk") ||
|
|
|
|
cocoa::isRefType(RetTy, "DADissenter") ||
|
|
|
|
cocoa::isRefType(RetTy, "DASessionRef")) {
|
2009-01-13 05:45:02 +08:00
|
|
|
S = getCFCreateGetRuleSummary(FD, FName);
|
2008-10-29 12:07:07 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-13 05:45:02 +08:00
|
|
|
break;
|
2008-07-16 00:50:12 +08:00
|
|
|
}
|
2009-01-13 05:45:02 +08:00
|
|
|
|
|
|
|
// 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')) {
|
2010-02-09 00:45:01 +08:00
|
|
|
// Test for 'CGCF'.
|
2010-02-09 02:38:55 +08:00
|
|
|
FName = FName.substr(FName.startswith("CGCF") ? 4 : 2);
|
2010-02-09 00:45:01 +08:00
|
|
|
|
2009-03-06 06:11:14 +08:00
|
|
|
if (isRelease(FD, FName))
|
2009-01-13 05:45:02 +08:00
|
|
|
S = getUnarySummary(FT, cfrelease);
|
|
|
|
else {
|
2009-05-03 13:20:50 +08:00
|
|
|
assert (ScratchArgs.isEmpty());
|
2009-01-30 06:45:13 +08:00
|
|
|
// Remaining CoreFoundation and CoreGraphics functions.
|
|
|
|
// We use to assume that they all strictly followed the ownership idiom
|
|
|
|
// and that ownership cannot be transferred. While this is technically
|
|
|
|
// correct, many methods allow a tracked object to escape. For example:
|
|
|
|
//
|
2009-09-09 23:08:12 +08:00
|
|
|
// CFMutableDictionaryRef x = CFDictionaryCreateMutable(...);
|
2009-01-30 06:45:13 +08:00
|
|
|
// CFDictionaryAddValue(y, key, x);
|
2009-09-09 23:08:12 +08:00
|
|
|
// CFRelease(x);
|
2009-01-30 06:45:13 +08:00
|
|
|
// ... it is okay to use 'x' since 'y' has a reference to it
|
|
|
|
//
|
|
|
|
// We handle this and similar cases with the follow heuristic. If the
|
2009-08-20 08:57:22 +08:00
|
|
|
// function name contains "InsertValue", "SetValue", "AddValue",
|
|
|
|
// "AppendValue", or "SetAttribute", then we assume that arguments may
|
|
|
|
// "escape." This means that something else holds on to the object,
|
|
|
|
// allowing it be used even after its local retain count drops to 0.
|
2010-01-12 03:46:28 +08:00
|
|
|
ArgEffect E = (StrInStrNoCase(FName, "InsertValue") != StringRef::npos||
|
|
|
|
StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
|
|
|
|
StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
|
|
|
|
StrInStrNoCase(FName, "AppendValue") != StringRef::npos||
|
2010-01-12 04:15:06 +08:00
|
|
|
StrInStrNoCase(FName, "SetAttribute") != StringRef::npos)
|
2009-01-30 06:45:13 +08:00
|
|
|
? MayEscape : DoNothing;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-30 06:45:13 +08:00
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, E);
|
2009-01-13 05:45:02 +08:00
|
|
|
}
|
2008-10-23 04:54:52 +08:00
|
|
|
}
|
2008-07-16 00:50:12 +08:00
|
|
|
}
|
|
|
|
while (0);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 13:31:22 +08:00
|
|
|
if (!S)
|
|
|
|
S = getDefaultSummary();
|
2008-04-25 01:22:33 +08:00
|
|
|
|
2009-05-09 10:58:13 +08:00
|
|
|
// Annotations override defaults.
|
|
|
|
assert(S);
|
|
|
|
updateSummaryFromAnnotations(*S, FD);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
FuncSummaries[FD] = S;
|
2009-09-09 23:08:12 +08:00
|
|
|
return S;
|
2008-03-06 08:08:09 +08:00
|
|
|
}
|
|
|
|
|
2008-07-16 00:50:12 +08:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getCFCreateGetRuleSummary(FunctionDecl* FD,
|
2010-02-09 02:38:55 +08:00
|
|
|
StringRef FName) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-02-09 02:38:55 +08:00
|
|
|
if (FName.find("Create") != StringRef::npos ||
|
|
|
|
FName.find("Copy") != StringRef::npos)
|
2008-05-06 00:51:50 +08:00
|
|
|
return getCFSummaryCreateRule(FD);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-02-09 02:38:55 +08:00
|
|
|
if (FName.find("Get") != StringRef::npos)
|
2008-05-06 00:51:50 +08:00
|
|
|
return getCFSummaryGetRule(FD);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 12:57:00 +08:00
|
|
|
return getDefaultSummary();
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
RetainSummary*
|
2009-02-24 00:51:39 +08:00
|
|
|
RetainSummaryManager::getUnarySummary(const FunctionType* FT,
|
|
|
|
UnaryFuncKind func) {
|
|
|
|
|
2009-01-13 05:45:02 +08:00
|
|
|
// Sanity check that this is *really* a unary function. This can
|
|
|
|
// happen if people do weird things.
|
2009-02-27 07:50:07 +08:00
|
|
|
const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT);
|
2009-01-13 05:45:02 +08:00
|
|
|
if (!FTP || FTP->getNumArgs() != 1)
|
|
|
|
return getPersistentStopSummary();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
assert (ScratchArgs.isEmpty());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-29 13:33:51 +08:00
|
|
|
switch (func) {
|
2009-05-03 13:20:50 +08:00
|
|
|
case cfretain: {
|
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 0, IncRef);
|
2008-05-23 01:31:13 +08:00
|
|
|
return getPersistentSummary(RetEffect::MakeAlias(0),
|
|
|
|
DoNothing, DoNothing);
|
2008-04-29 13:33:51 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-29 13:33:51 +08:00
|
|
|
case cfrelease: {
|
2009-05-03 13:20:50 +08:00
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 0, DecRef);
|
2008-05-23 01:31:13 +08:00
|
|
|
return getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
DoNothing, DoNothing);
|
2008-04-29 13:33:51 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-29 13:33:51 +08:00
|
|
|
case cfmakecollectable: {
|
2009-05-03 13:20:50 +08:00
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 0, MakeCollectable);
|
2009-09-09 23:08:12 +08:00
|
|
|
return getPersistentSummary(RetEffect::MakeAlias(0),DoNothing, DoNothing);
|
2008-04-29 13:33:51 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-29 13:33:51 +08:00
|
|
|
default:
|
2008-05-06 00:51:50 +08:00
|
|
|
assert (false && "Not a supported unary function.");
|
2009-05-04 12:57:00 +08:00
|
|
|
return getDefaultSummary();
|
2008-04-11 07:44:06 +08:00
|
|
|
}
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) {
|
2009-05-03 13:20:50 +08:00
|
|
|
assert (ScratchArgs.isEmpty());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-07-10 02:11:16 +08:00
|
|
|
if (FD->getIdentifier() == CFDictionaryCreateII) {
|
2009-05-03 13:20:50 +08:00
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 1, DoNothingByRef);
|
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 2, DoNothingByRef);
|
2008-07-10 02:11:16 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-28 13:56:51 +08:00
|
|
|
return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) {
|
2009-09-09 23:08:12 +08:00
|
|
|
assert (ScratchArgs.isEmpty());
|
2009-01-28 13:56:51 +08:00
|
|
|
return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF),
|
|
|
|
DoNothing, DoNothing);
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Summary creation for Selectors.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-05-06 23:44:25 +08:00
|
|
|
RetainSummary*
|
2009-04-29 13:04:30 +08:00
|
|
|
RetainSummaryManager::getInitMethodSummary(QualType RetTy) {
|
2009-09-09 23:08:12 +08:00
|
|
|
assert(ScratchArgs.isEmpty());
|
2009-05-13 04:06:54 +08:00
|
|
|
// 'init' methods conceptually return a newly allocated object and claim
|
2009-09-09 23:08:12 +08:00
|
|
|
// the receiver.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isCocoaObjectRef(RetTy) || cocoa::isCFObjectRef(RetTy))
|
2009-06-06 07:18:01 +08:00
|
|
|
return getPersistentSummary(ObjCInitRetE, DecRefMsg);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-13 04:06:54 +08:00
|
|
|
return getDefaultSummary();
|
2008-05-06 07:55:01 +08:00
|
|
|
}
|
2009-05-09 10:58:13 +08:00
|
|
|
|
|
|
|
void
|
|
|
|
RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ,
|
|
|
|
const FunctionDecl *FD) {
|
|
|
|
if (!FD)
|
|
|
|
return;
|
|
|
|
|
2009-06-12 02:17:24 +08:00
|
|
|
QualType RetTy = FD->getResultType();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 10:58:13 +08:00
|
|
|
// Determine if there is a special return effect for this method.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isCocoaObjectRef(RetTy)) {
|
2009-06-30 10:34:44 +08:00
|
|
|
if (FD->getAttr<NSReturnsRetainedAttr>()) {
|
2009-05-09 10:58:13 +08:00
|
|
|
Summ.setRetEffect(ObjCAllocRetE);
|
|
|
|
}
|
2009-06-30 10:34:44 +08:00
|
|
|
else if (FD->getAttr<CFReturnsRetainedAttr>()) {
|
2009-06-06 07:00:33 +08:00
|
|
|
Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
|
2009-06-12 02:17:24 +08:00
|
|
|
}
|
|
|
|
}
|
2009-07-30 05:53:49 +08:00
|
|
|
else if (RetTy->getAs<PointerType>()) {
|
2009-06-30 10:34:44 +08:00
|
|
|
if (FD->getAttr<CFReturnsRetainedAttr>()) {
|
2009-05-09 10:58:13 +08:00
|
|
|
Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ,
|
|
|
|
const ObjCMethodDecl *MD) {
|
|
|
|
if (!MD)
|
|
|
|
return;
|
|
|
|
|
2009-07-07 02:30:43 +08:00
|
|
|
bool isTrackedLoc = false;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 10:58:13 +08:00
|
|
|
// Determine if there is a special return effect for this method.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isCocoaObjectRef(MD->getResultType())) {
|
2009-06-30 10:34:44 +08:00
|
|
|
if (MD->getAttr<NSReturnsRetainedAttr>()) {
|
2009-05-09 10:58:13 +08:00
|
|
|
Summ.setRetEffect(ObjCAllocRetE);
|
2009-07-07 02:30:43 +08:00
|
|
|
return;
|
2009-05-09 10:58:13 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-07 02:30:43 +08:00
|
|
|
isTrackedLoc = true;
|
2009-05-09 10:58:13 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-07 02:30:43 +08:00
|
|
|
if (!isTrackedLoc)
|
2009-07-30 05:53:49 +08:00
|
|
|
isTrackedLoc = MD->getResultType()->getAs<PointerType>() != NULL;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-07 02:30:43 +08:00
|
|
|
if (isTrackedLoc && MD->getAttr<CFReturnsRetainedAttr>())
|
|
|
|
Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
|
2009-05-09 10:58:13 +08:00
|
|
|
}
|
|
|
|
|
2009-04-24 07:08:22 +08:00
|
|
|
RetainSummary*
|
2009-04-30 07:03:22 +08:00
|
|
|
RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD,
|
|
|
|
Selector S, QualType RetTy) {
|
2009-04-25 05:56:17 +08:00
|
|
|
|
2009-04-29 08:42:39 +08:00
|
|
|
if (MD) {
|
2009-04-25 02:00:17 +08:00
|
|
|
// Scan the method decl for 'void*' arguments. These should be treated
|
|
|
|
// as 'StopTracking' because they are often used with delegates.
|
|
|
|
// Delegates are a frequent form of false positives with the retain
|
|
|
|
// count checker.
|
|
|
|
unsigned i = 0;
|
|
|
|
for (ObjCMethodDecl::param_iterator I = MD->param_begin(),
|
|
|
|
E = MD->param_end(); I != E; ++I, ++i)
|
|
|
|
if (ParmVarDecl *PD = *I) {
|
|
|
|
QualType Ty = Ctx.getCanonicalType(PD->getType());
|
First part of changes to eliminate problems with cv-qualifiers and
sugared types. The basic problem is that our qualifier accessors
(getQualifiers, getCVRQualifiers, isConstQualified, etc.) only look at
the current QualType and not at any qualifiers that come from sugared
types, meaning that we won't see these qualifiers through, e.g.,
typedefs:
typedef const int CInt;
typedef CInt Self;
Self.isConstQualified() currently returns false!
Various bugs (e.g., PR5383) have cropped up all over the front end due
to such problems. I'm addressing this problem by splitting each
qualifier accessor into two versions:
- the "local" version only returns qualifiers on this particular
QualType instance
- the "normal" version that will eventually combine qualifiers from this
QualType instance with the qualifiers on the canonical type to
produce the full set of qualifiers.
This commit adds the local versions and switches a few callers from
the "normal" version (e.g., isConstQualified) over to the "local"
version (e.g., isLocalConstQualified) when that is the right thing to
do, e.g., because we're printing or serializing the qualifiers. Also,
switch a bunch of
Context.getCanonicalType(T1).getUnqualifiedType() == Context.getCanonicalType(T2).getQualifiedType()
expressions over to
Context.hasSameUnqualifiedType(T1, T2)
llvm-svn: 88969
2009-11-17 05:35:15 +08:00
|
|
|
if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy)
|
2009-05-03 13:20:50 +08:00
|
|
|
ScratchArgs = AF.Add(ScratchArgs, i, StopTracking);
|
2009-04-25 02:00:17 +08:00
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-25 05:56:17 +08:00
|
|
|
// Any special effect for the receiver?
|
|
|
|
ArgEffect ReceiverEff = DoNothing;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-25 05:56:17 +08:00
|
|
|
// If one of the arguments in the selector has the keyword 'delegate' we
|
|
|
|
// should stop tracking the reference count for the receiver. This is
|
|
|
|
// because the reference count is quite possibly handled by a delegate
|
|
|
|
// method.
|
|
|
|
if (S.isKeywordSelector()) {
|
|
|
|
const std::string &str = S.getAsString();
|
|
|
|
assert(!str.empty());
|
2010-01-12 03:46:28 +08:00
|
|
|
if (StrInStrNoCase(str, "delegate:") != StringRef::npos)
|
|
|
|
ReceiverEff = StopTracking;
|
2009-04-25 05:56:17 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-24 07:08:22 +08:00
|
|
|
// Look for methods that return an owned object.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isCocoaObjectRef(RetTy)) {
|
2009-05-03 14:08:32 +08:00
|
|
|
// EXPERIMENTAL: Assume the Cocoa conventions for all objects returned
|
|
|
|
// by instance methods.
|
2010-01-27 14:13:48 +08:00
|
|
|
RetEffect E = cocoa::followsFundamentalRule(S)
|
2009-05-15 23:49:00 +08:00
|
|
|
? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
return getPersistentSummary(E, ReceiverEff, MayEscape);
|
2009-04-25 02:00:17 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 14:08:32 +08:00
|
|
|
// Look for methods that return an owned core foundation object.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isCFObjectRef(RetTy)) {
|
2010-01-27 14:13:48 +08:00
|
|
|
RetEffect E = cocoa::followsFundamentalRule(S)
|
2009-05-15 23:49:00 +08:00
|
|
|
? RetEffect::MakeOwned(RetEffect::CF, true)
|
|
|
|
: RetEffect::MakeNotOwned(RetEffect::CF);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 14:08:32 +08:00
|
|
|
return getPersistentSummary(E, ReceiverEff, MayEscape);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 14:08:32 +08:00
|
|
|
if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing)
|
2009-05-04 12:57:00 +08:00
|
|
|
return getDefaultSummary();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 13:31:22 +08:00
|
|
|
return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, MayEscape);
|
2009-04-24 07:08:22 +08:00
|
|
|
}
|
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getInstanceMethodSummary(const ObjCMessageExpr *ME,
|
|
|
|
const GRState *state,
|
|
|
|
const LocationContext *LC) {
|
|
|
|
|
|
|
|
// We need the type-information of the tracked receiver object
|
|
|
|
// Retrieve it from the state.
|
|
|
|
const Expr *Receiver = ME->getReceiver();
|
|
|
|
const ObjCInterfaceDecl* ID = 0;
|
|
|
|
|
|
|
|
// FIXME: Is this really working as expected? There are cases where
|
|
|
|
// we just use the 'ID' from the message expression.
|
2010-02-09 00:18:51 +08:00
|
|
|
SVal receiverV = state->getSValAsScalarOrLoc(Receiver);
|
2009-11-13 09:54:21 +08:00
|
|
|
|
|
|
|
// FIXME: Eventually replace the use of state->get<RefBindings> with
|
|
|
|
// a generic API for reasoning about the Objective-C types of symbolic
|
|
|
|
// objects.
|
|
|
|
if (SymbolRef Sym = receiverV.getAsLocSymbol())
|
|
|
|
if (const RefVal *T = state->get<RefBindings>(Sym))
|
|
|
|
if (const ObjCObjectPointerType* PT =
|
|
|
|
T->getType()->getAs<ObjCObjectPointerType>())
|
|
|
|
ID = PT->getInterfaceDecl();
|
|
|
|
|
|
|
|
// FIXME: this is a hack. This may or may not be the actual method
|
|
|
|
// that is called.
|
|
|
|
if (!ID) {
|
|
|
|
if (const ObjCObjectPointerType *PT =
|
|
|
|
Receiver->getType()->getAs<ObjCObjectPointerType>())
|
|
|
|
ID = PT->getInterfaceDecl();
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: The receiver could be a reference to a class, meaning that
|
|
|
|
// we should use the class method.
|
|
|
|
RetainSummary *Summ = getInstanceMethodSummary(ME, ID);
|
|
|
|
|
|
|
|
// Special-case: are we sending a mesage to "self"?
|
|
|
|
// This is a hack. When we have full-IP this should be removed.
|
|
|
|
if (isa<ObjCMethodDecl>(LC->getDecl())) {
|
|
|
|
if (const loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&receiverV)) {
|
|
|
|
// Get the region associated with 'self'.
|
|
|
|
if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl()) {
|
2010-02-09 00:18:51 +08:00
|
|
|
SVal SelfVal = state->getSVal(state->getRegion(SelfDecl, LC));
|
2009-11-13 09:54:21 +08:00
|
|
|
if (L->StripCasts() == SelfVal.getAsRegion()) {
|
|
|
|
// Update the summary to make the default argument effect
|
|
|
|
// 'StopTracking'.
|
|
|
|
Summ = copySummary(Summ);
|
|
|
|
Summ->setDefaultArgEffect(StopTracking);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Summ ? Summ : getDefaultSummary();
|
|
|
|
}
|
|
|
|
|
2008-05-06 23:44:25 +08:00
|
|
|
RetainSummary*
|
2009-04-30 01:09:14 +08:00
|
|
|
RetainSummaryManager::getInstanceMethodSummary(Selector S,
|
|
|
|
IdentifierInfo *ClsName,
|
2009-04-30 07:03:22 +08:00
|
|
|
const ObjCInterfaceDecl* ID,
|
|
|
|
const ObjCMethodDecl *MD,
|
2009-04-30 01:09:14 +08:00
|
|
|
QualType RetTy) {
|
2008-05-06 23:44:25 +08:00
|
|
|
|
2009-04-29 13:04:30 +08:00
|
|
|
// Look up a summary in our summary cache.
|
2009-07-22 07:27:57 +08:00
|
|
|
RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
if (!Summ) {
|
|
|
|
assert(ScratchArgs.isEmpty());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
// "initXXX": pass-through for receiver.
|
2010-01-27 14:13:48 +08:00
|
|
|
if (cocoa::deriveNamingConvention(S) == cocoa::InitRule)
|
2009-07-22 07:27:57 +08:00
|
|
|
Summ = getInitMethodSummary(RetTy);
|
|
|
|
else
|
|
|
|
Summ = getCommonMethodSummary(MD, S, RetTy);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
// Annotations override defaults.
|
|
|
|
updateSummaryFromAnnotations(*Summ, MD);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
// Memoize the summary.
|
|
|
|
ObjCMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-24 03:11:35 +08:00
|
|
|
return Summ;
|
2008-05-06 07:55:01 +08:00
|
|
|
}
|
|
|
|
|
2008-05-07 05:26:51 +08:00
|
|
|
RetainSummary*
|
2009-04-29 08:42:39 +08:00
|
|
|
RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName,
|
2009-04-30 07:03:22 +08:00
|
|
|
const ObjCInterfaceDecl *ID,
|
|
|
|
const ObjCMethodDecl *MD,
|
|
|
|
QualType RetTy) {
|
2009-04-25 01:50:11 +08:00
|
|
|
|
2009-04-29 08:42:39 +08:00
|
|
|
assert(ClsName && "Class name must be specified.");
|
2009-09-09 23:08:12 +08:00
|
|
|
RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S);
|
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
if (!Summ) {
|
|
|
|
Summ = getCommonMethodSummary(MD, S, RetTy);
|
|
|
|
// Annotations override defaults.
|
|
|
|
updateSummaryFromAnnotations(*Summ, MD);
|
|
|
|
// Memoize the summary.
|
|
|
|
ObjCClassMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-24 03:11:35 +08:00
|
|
|
return Summ;
|
2008-05-07 05:26:51 +08:00
|
|
|
}
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
void RetainSummaryManager::InitializeClassMethodSummaries() {
|
2009-05-08 07:40:42 +08:00
|
|
|
assert(ScratchArgs.isEmpty());
|
|
|
|
RetainSummary* Summ = getPersistentSummary(ObjCAllocRetE);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
// 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);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Create the [NSAssertionHandler currentHander] summary.
|
2009-10-16 06:25:12 +08:00
|
|
|
addClassMethSummary("NSAssertionHandler", "currentHandler",
|
2009-01-28 13:56:51 +08:00
|
|
|
getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC)));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-10-21 23:53:15 +08:00
|
|
|
// Create the [NSAutoreleasePool addObject:] summary.
|
2009-05-03 13:20:50 +08:00
|
|
|
ScratchArgs = AF.Add(ScratchArgs, 0, Autorelease);
|
2009-10-16 06:25:12 +08:00
|
|
|
addClassMethSummary("NSAutoreleasePool", "addObject",
|
|
|
|
getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
DoNothing, Autorelease));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-10-16 06:26:21 +08:00
|
|
|
// Create a summary for [NSCursor dragCopyCursor].
|
|
|
|
addClassMethSummary("NSCursor", "dragCopyCursor",
|
|
|
|
getPersistentSummary(RetEffect::MakeNoRet(), DoNothing,
|
|
|
|
DoNothing));
|
|
|
|
|
2009-04-25 01:50:11 +08:00
|
|
|
// Create the summaries for [NSObject performSelector...]. We treat
|
|
|
|
// these as 'stop tracking' for the arguments because they are often
|
|
|
|
// used for delegates that can release the object. When we have better
|
|
|
|
// inter-procedural analysis we can potentially do something better. This
|
|
|
|
// workaround is to remove false positives.
|
|
|
|
Summ = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking);
|
|
|
|
IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject");
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
|
|
|
|
"afterDelay", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
|
|
|
|
"afterDelay", "inModes", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread",
|
|
|
|
"withObject", "waitUntilDone", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread",
|
|
|
|
"withObject", "waitUntilDone", "modes", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread",
|
|
|
|
"withObject", "waitUntilDone", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread",
|
|
|
|
"withObject", "waitUntilDone", "modes", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground",
|
|
|
|
"withObject", NULL);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-15 05:29:16 +08:00
|
|
|
// Specially handle NSData.
|
|
|
|
RetainSummary *dataWithBytesNoCopySumm =
|
|
|
|
getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC), DoNothing,
|
|
|
|
DoNothing);
|
|
|
|
addClsMethSummary("NSData", dataWithBytesNoCopySumm,
|
|
|
|
"dataWithBytesNoCopy", "length", NULL);
|
|
|
|
addClsMethSummary("NSData", dataWithBytesNoCopySumm,
|
|
|
|
"dataWithBytesNoCopy", "length", "freeWhenDone", NULL);
|
2008-05-06 08:30:21 +08:00
|
|
|
}
|
|
|
|
|
2008-06-24 06:21:20 +08:00
|
|
|
void RetainSummaryManager::InitializeMethodSummaries() {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
assert (ScratchArgs.isEmpty());
|
|
|
|
|
2008-05-07 05:26:51 +08:00
|
|
|
// Create the "init" selector. It just acts as a pass-through for the
|
|
|
|
// receiver.
|
2009-09-09 23:08:12 +08:00
|
|
|
RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg);
|
2009-08-20 13:13:36 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm);
|
|
|
|
|
|
|
|
// awakeAfterUsingCoder: behaves basically like an 'init' method. It
|
|
|
|
// claims the receiver and returns a retained object.
|
|
|
|
addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx),
|
|
|
|
InitSumm);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-07 05:26:51 +08:00
|
|
|
// The next methods are allocators.
|
2009-08-29 03:52:12 +08:00
|
|
|
RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE);
|
2009-09-09 23:08:12 +08:00
|
|
|
RetainSummary *CFAllocSumm =
|
2009-08-29 03:52:12 +08:00
|
|
|
getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Create the "copy" selector.
|
|
|
|
addNSObjectMethSummary(GetNullarySelector("copy", Ctx), AllocSumm);
|
2008-08-13 04:41:56 +08:00
|
|
|
|
2008-05-06 08:38:54 +08:00
|
|
|
// Create the "mutableCopy" selector.
|
2009-05-21 06:39:57 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("mutableCopy", Ctx), AllocSumm);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 10:26:56 +08:00
|
|
|
// Create the "retain" selector.
|
2009-05-08 07:40:42 +08:00
|
|
|
RetEffect E = RetEffect::MakeReceiverAlias();
|
2009-05-21 06:39:57 +08:00
|
|
|
RetainSummary *Summ = getPersistentSummary(E, IncRefMsg);
|
2008-06-26 05:21:56 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 10:26:56 +08:00
|
|
|
// Create the "release" selector.
|
2009-02-19 02:54:33 +08:00
|
|
|
Summ = getPersistentSummary(E, DecRefMsg);
|
2008-06-26 05:21:56 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-08 05:17:39 +08:00
|
|
|
// Create the "drain" selector.
|
|
|
|
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef);
|
2008-06-26 05:21:56 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-18 03:42:23 +08:00
|
|
|
// Create the -dealloc summary.
|
|
|
|
Summ = getPersistentSummary(RetEffect::MakeNoRet(), Dealloc);
|
|
|
|
addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ);
|
2008-05-06 10:26:56 +08:00
|
|
|
|
|
|
|
// Create the "autorelease" selector.
|
2009-01-29 05:44:40 +08:00
|
|
|
Summ = getPersistentSummary(E, Autorelease);
|
2008-06-26 05:21:56 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-24 01:45:03 +08:00
|
|
|
// Specially handle NSAutoreleasePool.
|
2009-02-25 10:54:57 +08:00
|
|
|
addInstMethSummary("NSAutoreleasePool", "init",
|
2009-02-24 01:45:03 +08:00
|
|
|
getPersistentSummary(RetEffect::MakeReceiverAlias(),
|
2009-02-25 10:54:57 +08:00
|
|
|
NewAutoreleasePool));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// For NSWindow, allocated objects are (initially) self-owned.
|
2009-02-23 10:51:29 +08:00
|
|
|
// FIXME: For now we opt for false negatives with NSWindow, as these objects
|
|
|
|
// self-own themselves. However, they only do this once they are displayed.
|
|
|
|
// Thus, we need to track an NSWindow's display status.
|
|
|
|
// This is tracked in <rdar://problem/6062711>.
|
2009-03-05 07:30:42 +08:00
|
|
|
// See also http://llvm.org/bugs/show_bug.cgi?id=3714.
|
2009-05-13 04:06:54 +08:00
|
|
|
RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
StopTracking,
|
|
|
|
StopTracking);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-04 03:02:51 +08:00
|
|
|
addClassMethSummary("NSWindow", "alloc", NoTrackYet);
|
|
|
|
|
2009-03-05 07:30:42 +08:00
|
|
|
#if 0
|
2009-05-13 04:06:54 +08:00
|
|
|
addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect",
|
2008-08-13 02:48:50 +08:00
|
|
|
"styleMask", "backing", "defer", NULL);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-13 04:06:54 +08:00
|
|
|
addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect",
|
2008-08-13 02:48:50 +08:00
|
|
|
"styleMask", "backing", "defer", "screen", NULL);
|
2009-03-05 07:30:42 +08:00
|
|
|
#endif
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-07-02 01:21:27 +08:00
|
|
|
// For NSPanel (which subclasses NSWindow), allocated objects are not
|
|
|
|
// self-owned.
|
2009-04-04 03:02:51 +08:00
|
|
|
// FIXME: For now we don't track NSPanels. object for the same reason
|
|
|
|
// as for NSWindow objects.
|
|
|
|
addClassMethSummary("NSPanel", "alloc", NoTrackYet);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-13 04:06:54 +08:00
|
|
|
#if 0
|
|
|
|
addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect",
|
2008-08-13 02:48:50 +08:00
|
|
|
"styleMask", "backing", "defer", NULL);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-13 04:06:54 +08:00
|
|
|
addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect",
|
2008-08-13 02:48:50 +08:00
|
|
|
"styleMask", "backing", "defer", "screen", NULL);
|
2009-05-13 04:06:54 +08:00
|
|
|
#endif
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-19 07:14:34 +08:00
|
|
|
// Don't track allocated autorelease pools yet, as it is okay to prematurely
|
|
|
|
// exit a method.
|
|
|
|
addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet);
|
2008-06-26 05:21:56 +08:00
|
|
|
|
2008-07-19 01:24:20 +08:00
|
|
|
// Create NSAssertionHandler summaries.
|
2008-08-13 02:30:56 +08:00
|
|
|
addPanicSummary("NSAssertionHandler", "handleFailureInFunction", "file",
|
2009-09-09 23:08:12 +08:00
|
|
|
"lineNumber", "description", NULL);
|
|
|
|
|
2008-08-13 02:30:56 +08:00
|
|
|
addPanicSummary("NSAssertionHandler", "handleFailureInMethod", "object",
|
|
|
|
"file", "lineNumber", "description", NULL);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-21 06:39:57 +08:00
|
|
|
// Create summaries QCRenderer/QCView -createSnapShotImageOfType:
|
|
|
|
addInstMethSummary("QCRenderer", AllocSumm,
|
|
|
|
"createSnapshotImageOfType", NULL);
|
|
|
|
addInstMethSummary("QCView", AllocSumm,
|
|
|
|
"createSnapshotImageOfType", NULL);
|
|
|
|
|
2009-06-16 04:58:58 +08:00
|
|
|
// Create summaries for CIContext, 'createCGImage' and
|
2009-08-29 03:52:12 +08:00
|
|
|
// 'createCGLayerWithSize'. These objects are CF objects, and are not
|
|
|
|
// automatically garbage collected.
|
|
|
|
addInstMethSummary("CIContext", CFAllocSumm,
|
2009-05-21 06:39:57 +08:00
|
|
|
"createCGImage", "fromRect", NULL);
|
2009-08-29 03:52:12 +08:00
|
|
|
addInstMethSummary("CIContext", CFAllocSumm,
|
2009-09-09 23:08:12 +08:00
|
|
|
"createCGImage", "fromRect", "format", "colorSpace", NULL);
|
2009-08-29 03:52:12 +08:00
|
|
|
addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize",
|
2009-06-16 04:58:58 +08:00
|
|
|
"info", NULL);
|
2008-05-06 08:38:54 +08:00
|
|
|
}
|
|
|
|
|
2008-10-21 23:53:15 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-02-25 03:15:11 +08:00
|
|
|
// AutoreleaseBindings - State used to track objects in autorelease pools.
|
2008-10-21 23:53:15 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-02-25 03:15:11 +08:00
|
|
|
typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts;
|
|
|
|
typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents;
|
|
|
|
typedef llvm::ImmutableList<SymbolRef> ARStack;
|
2009-02-24 01:45:03 +08:00
|
|
|
|
2009-02-25 03:15:11 +08:00
|
|
|
static int AutoRCIndex = 0;
|
2008-10-21 23:53:15 +08:00
|
|
|
static int AutoRBIndex = 0;
|
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
namespace { class AutoreleasePoolContents {}; }
|
|
|
|
namespace { class AutoreleaseStack {}; }
|
2009-02-25 03:15:11 +08:00
|
|
|
|
2008-10-21 23:53:15 +08:00
|
|
|
namespace clang {
|
2009-02-25 10:54:57 +08:00
|
|
|
template<> struct GRStateTrait<AutoreleaseStack>
|
2009-02-25 03:15:11 +08:00
|
|
|
: public GRStatePartialTrait<ARStack> {
|
2009-09-09 23:08:12 +08:00
|
|
|
static inline void* GDMIndex() { return &AutoRBIndex; }
|
2009-02-25 03:15:11 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
template<> struct GRStateTrait<AutoreleasePoolContents>
|
|
|
|
: public GRStatePartialTrait<ARPoolContents> {
|
2009-09-09 23:08:12 +08:00
|
|
|
static inline void* GDMIndex() { return &AutoRCIndex; }
|
2009-02-25 03:15:11 +08:00
|
|
|
};
|
|
|
|
} // end clang namespace
|
2008-10-21 23:53:15 +08:00
|
|
|
|
2009-03-21 01:34:15 +08:00
|
|
|
static SymbolRef GetCurrentAutoreleasePool(const GRState* state) {
|
|
|
|
ARStack stack = state->get<AutoreleaseStack>();
|
|
|
|
return stack.isEmpty() ? SymbolRef() : stack.getHead();
|
|
|
|
}
|
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
static const GRState * SendAutorelease(const GRState *state,
|
|
|
|
ARCounts::Factory &F, SymbolRef sym) {
|
2009-03-21 01:34:15 +08:00
|
|
|
|
|
|
|
SymbolRef pool = GetCurrentAutoreleasePool(state);
|
2009-06-18 09:23:53 +08:00
|
|
|
const ARCounts *cnts = state->get<AutoreleasePoolContents>(pool);
|
2009-03-21 01:34:15 +08:00
|
|
|
ARCounts newCnts(0);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-21 01:34:15 +08:00
|
|
|
if (cnts) {
|
|
|
|
const unsigned *cnt = (*cnts).lookup(sym);
|
|
|
|
newCnts = F.Add(*cnts, sym, cnt ? *cnt + 1 : 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
newCnts = F.Add(F.GetEmptyMap(), sym, 1);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
return state->set<AutoreleasePoolContents>(pool, newCnts);
|
2009-03-21 01:34:15 +08:00
|
|
|
}
|
|
|
|
|
2008-04-17 04:40:59 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Transfer functions.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-08-15 05:16:54 +08:00
|
|
|
namespace {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class CFRefCount : public GRTransferFuncs {
|
2008-04-18 11:39:05 +08:00
|
|
|
public:
|
2008-08-14 05:24:49 +08:00
|
|
|
class BindingsPrinter : public GRState::Printer {
|
2008-03-12 03:44:10 +08:00
|
|
|
public:
|
2009-06-25 07:06:47 +08:00
|
|
|
virtual void Print(llvm::raw_ostream& Out, const GRState* state,
|
2008-08-14 05:24:49 +08:00
|
|
|
const char* nl, const char* sep);
|
2008-03-12 03:44:10 +08:00
|
|
|
};
|
2008-04-18 11:39:05 +08:00
|
|
|
|
|
|
|
private:
|
2009-08-06 20:48:26 +08:00
|
|
|
typedef llvm::DenseMap<const ExplodedNode*, const RetainSummary*>
|
2009-09-09 23:08:12 +08:00
|
|
|
SummaryLogTy;
|
2009-02-18 11:48:14 +08:00
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
RetainSummaryManager Summaries;
|
2009-02-18 11:48:14 +08:00
|
|
|
SummaryLogTy SummaryLog;
|
2008-05-06 06:11:16 +08:00
|
|
|
const LangOptions& LOpts;
|
2009-02-25 03:15:11 +08:00
|
|
|
ARCounts::Factory ARCountFactory;
|
2008-08-17 11:20:02 +08:00
|
|
|
|
2009-02-05 14:50:21 +08:00
|
|
|
BugType *useAfterRelease, *releaseNotOwned;
|
2009-03-18 03:42:23 +08:00
|
|
|
BugType *deallocGC, *deallocNotOwned;
|
2009-02-05 14:50:21 +08:00
|
|
|
BugType *leakWithinFunction, *leakAtReturn;
|
2009-05-09 08:10:05 +08:00
|
|
|
BugType *overAutorelease;
|
2009-05-10 14:25:57 +08:00
|
|
|
BugType *returnNotOwnedForOwned;
|
2009-02-05 14:50:21 +08:00
|
|
|
BugReporter *BR;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
const GRState * Update(const GRState * state, SymbolRef sym, RefVal V, ArgEffect E,
|
2009-02-25 03:15:11 +08:00
|
|
|
RefVal::Kind& hasErr);
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
void ProcessNonLeakError(ExplodedNodeSet& Dst,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2009-05-12 18:10:00 +08:00
|
|
|
Expr* NodeExpr, Expr* ErrorExpr,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred,
|
2008-08-13 12:27:00 +08:00
|
|
|
const GRState* St,
|
2008-12-05 10:27:51 +08:00
|
|
|
RefVal::Kind hasErr, SymbolRef Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
const GRState * HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V,
|
2009-05-09 07:09:42 +08:00
|
|
|
llvm::SmallVectorImpl<SymbolRef> &Leaked);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* ProcessLeaks(const GRState * state,
|
2009-05-09 07:09:42 +08:00
|
|
|
llvm::SmallVectorImpl<SymbolRef> &Leaked,
|
|
|
|
GenericNodeBuilder &Builder,
|
|
|
|
GRExprEngine &Eng,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode *Pred = 0);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
public:
|
2008-07-23 00:21:24 +08:00
|
|
|
CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts)
|
2008-04-29 13:33:51 +08:00
|
|
|
: Summaries(Ctx, gcenabled),
|
2009-03-18 03:42:23 +08:00
|
|
|
LOpts(lopts), useAfterRelease(0), releaseNotOwned(0),
|
|
|
|
deallocGC(0), deallocNotOwned(0),
|
2009-05-10 14:25:57 +08:00
|
|
|
leakWithinFunction(0), leakAtReturn(0), overAutorelease(0),
|
|
|
|
returnNotOwnedForOwned(0), BR(0) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-05 14:50:21 +08:00
|
|
|
virtual ~CFRefCount() {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-04 07:30:34 +08:00
|
|
|
void RegisterChecks(GRExprEngine &Eng);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-16 08:49:49 +08:00
|
|
|
virtual void RegisterPrinters(std::vector<GRState::Printer*>& Printers) {
|
|
|
|
Printers.push_back(new BindingsPrinter());
|
2008-03-12 03:44:10 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
bool isGCEnabled() const { return Summaries.isGCEnabled(); }
|
2008-05-01 07:47:44 +08:00
|
|
|
const LangOptions& getLangOptions() const { return LOpts; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const {
|
2009-02-18 11:48:14 +08:00
|
|
|
SummaryLogTy::const_iterator I = SummaryLog.find(N);
|
|
|
|
return I == SummaryLog.end() ? 0 : I->second;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
// Calls.
|
2008-05-06 06:11:16 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
void EvalSummary(ExplodedNodeSet& Dst,
|
2008-05-06 06:11:16 +08:00
|
|
|
GRExprEngine& Eng,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2008-05-06 06:11:16 +08:00
|
|
|
Expr* Ex,
|
|
|
|
Expr* Receiver,
|
2009-05-04 12:57:00 +08:00
|
|
|
const RetainSummary& Summ,
|
2009-12-03 16:25:47 +08:00
|
|
|
const MemRegion *Callee,
|
2009-05-12 18:10:00 +08:00
|
|
|
ExprIterator arg_beg, ExprIterator arg_end,
|
2009-12-02 13:49:12 +08:00
|
|
|
ExplodedNode* Pred, const GRState *state);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
virtual void EvalCall(ExplodedNodeSet& Dst,
|
2008-03-13 05:06:49 +08:00
|
|
|
GRExprEngine& Eng,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2008-10-17 13:57:07 +08:00
|
|
|
CallExpr* CE, SVal L,
|
2009-09-09 23:08:12 +08:00
|
|
|
ExplodedNode* Pred);
|
|
|
|
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
virtual void EvalObjCMessageExpr(ExplodedNodeSet& Dst,
|
2008-04-16 07:44:31 +08:00
|
|
|
GRExprEngine& Engine,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2008-04-16 07:44:31 +08:00
|
|
|
ObjCMessageExpr* ME,
|
2009-12-02 13:49:12 +08:00
|
|
|
ExplodedNode* Pred,
|
|
|
|
const GRState *state);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
bool EvalObjCMessageExprAux(ExplodedNodeSet& Dst,
|
2008-04-16 07:44:31 +08:00
|
|
|
GRExprEngine& Engine,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2008-04-16 07:44:31 +08:00
|
|
|
ObjCMessageExpr* ME,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred);
|
2008-04-16 07:44:31 +08:00
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
// Stores.
|
2009-02-14 09:43:44 +08:00
|
|
|
virtual void EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val);
|
|
|
|
|
2008-04-12 06:25:11 +08:00
|
|
|
// End-of-path.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-12 06:25:11 +08:00
|
|
|
virtual void EvalEndPath(GRExprEngine& Engine,
|
2009-08-06 20:48:26 +08:00
|
|
|
GREndPathNodeBuilder& Builder);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
virtual void EvalDeadSymbols(ExplodedNodeSet& Dst,
|
2008-04-25 07:57:27 +08:00
|
|
|
GRExprEngine& Engine,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred,
|
2009-01-22 06:26:05 +08:00
|
|
|
Stmt* S, const GRState* state,
|
|
|
|
SymbolReaper& SymReaper);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
std::pair<ExplodedNode*, const GRState *>
|
2009-06-18 09:23:53 +08:00
|
|
|
HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred, GRExprEngine &Eng,
|
2009-05-09 08:10:05 +08:00
|
|
|
SymbolRef Sym, RefVal V, bool &stop);
|
2008-04-18 02:12:53 +08:00
|
|
|
// Return statements.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
virtual void EvalReturn(ExplodedNodeSet& Dst,
|
2008-04-18 02:12:53 +08:00
|
|
|
GRExprEngine& Engine,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2008-04-18 02:12:53 +08:00
|
|
|
ReturnStmt* S,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred);
|
2008-04-19 03:23:43 +08:00
|
|
|
|
|
|
|
// Assumptions.
|
|
|
|
|
2009-06-19 06:57:13 +08:00
|
|
|
virtual const GRState *EvalAssume(const GRState* state, SVal condition,
|
|
|
|
bool assumption);
|
2008-03-11 14:39:11 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2009-06-25 07:06:47 +08:00
|
|
|
static void PrintPool(llvm::raw_ostream &Out, SymbolRef Sym,
|
|
|
|
const GRState *state) {
|
2009-03-21 01:34:15 +08:00
|
|
|
Out << ' ';
|
2009-03-26 11:35:11 +08:00
|
|
|
if (Sym)
|
|
|
|
Out << Sym->getSymbolID();
|
2009-03-21 01:34:15 +08:00
|
|
|
else
|
|
|
|
Out << "<pool>";
|
|
|
|
Out << ":{";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-21 01:34:15 +08:00
|
|
|
// Get the contents of the pool.
|
|
|
|
if (const ARCounts *cnts = state->get<AutoreleasePoolContents>(Sym))
|
|
|
|
for (ARCounts::iterator J=cnts->begin(), EJ=cnts->end(); J != EJ; ++J)
|
|
|
|
Out << '(' << J.getKey() << ',' << J.getData() << ')';
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
Out << '}';
|
2009-03-21 01:34:15 +08:00
|
|
|
}
|
2008-04-18 11:39:05 +08:00
|
|
|
|
2009-06-25 07:06:47 +08:00
|
|
|
void CFRefCount::BindingsPrinter::Print(llvm::raw_ostream& Out,
|
|
|
|
const GRState* state,
|
2008-08-14 05:24:49 +08:00
|
|
|
const char* nl, const char* sep) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-15 05:16:54 +08:00
|
|
|
RefBindings B = state->get<RefBindings>();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-14 05:24:49 +08:00
|
|
|
if (!B.isEmpty())
|
2008-03-12 03:44:10 +08:00
|
|
|
Out << sep << nl;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-12 03:44:10 +08:00
|
|
|
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
|
|
|
|
Out << (*I).first << " : ";
|
|
|
|
(*I).second.print(Out);
|
|
|
|
Out << nl;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-25 10:54:57 +08:00
|
|
|
// Print the autorelease stack.
|
2009-03-21 01:34:15 +08:00
|
|
|
Out << sep << nl << "AR pool stack:";
|
2009-02-25 10:54:57 +08:00
|
|
|
ARStack stack = state->get<AutoreleaseStack>();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-21 01:34:15 +08:00
|
|
|
PrintPool(Out, SymbolRef(), state); // Print the caller's pool.
|
|
|
|
for (ARStack::iterator I=stack.begin(), E=stack.end(); I!=E; ++I)
|
|
|
|
PrintPool(Out, *I, state);
|
|
|
|
|
|
|
|
Out << nl;
|
2008-03-12 03:44:10 +08:00
|
|
|
}
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Error reporting.
|
|
|
|
//===----------------------------------------------------------------------===//
|
2008-06-26 05:21:56 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
namespace {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
//===-------------===//
|
|
|
|
// Bug Descriptions. //
|
2009-09-09 23:08:12 +08:00
|
|
|
//===-------------===//
|
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class CFRefBug : public BugType {
|
2009-04-30 02:50:19 +08:00
|
|
|
protected:
|
|
|
|
CFRefCount& TF;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-30 02:27:55 +08:00
|
|
|
CFRefBug(CFRefCount* tf, llvm::StringRef name)
|
2009-09-09 23:08:12 +08:00
|
|
|
: BugType(name, "Memory (Core Foundation/Objective-C)"), TF(*tf) {}
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
CFRefCount& getTF() { return TF; }
|
|
|
|
const CFRefCount& getTF() const { return TF; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// FIXME: Eventually remove.
|
|
|
|
virtual const char* getDescription() const = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
virtual bool isLeak() const { return false; }
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class UseAfterRelease : public CFRefBug {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
|
|
|
UseAfterRelease(CFRefCount* tf)
|
|
|
|
: CFRefBug(tf, "Use-after-release") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
const char* getDescription() const {
|
|
|
|
return "Reference-counted object is used after it is released";
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class BadRelease : public CFRefBug {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
|
|
|
BadRelease(CFRefCount* tf) : CFRefBug(tf, "Bad release") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
const char* getDescription() const {
|
2009-10-02 01:31:50 +08:00
|
|
|
return "Incorrect decrement of the reference count of an object that is "
|
|
|
|
"not owned at this point by the caller";
|
2008-04-12 04:51:02 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class DeallocGC : public CFRefBug {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2009-05-09 08:10:05 +08:00
|
|
|
DeallocGC(CFRefCount *tf)
|
|
|
|
: CFRefBug(tf, "-dealloc called while using garbage collection") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
const char *getDescription() const {
|
2009-05-09 08:10:05 +08:00
|
|
|
return "-dealloc called while using garbage collection";
|
2008-05-06 10:41:27 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class DeallocNotOwned : public CFRefBug {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2009-05-09 08:10:05 +08:00
|
|
|
DeallocNotOwned(CFRefCount *tf)
|
|
|
|
: CFRefBug(tf, "-dealloc sent to non-exclusively owned object") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
const char *getDescription() const {
|
|
|
|
return "-dealloc sent to object that may be referenced elsewhere";
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
};
|
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class OverAutorelease : public CFRefBug {
|
2009-05-09 08:10:05 +08:00
|
|
|
public:
|
2009-09-09 23:08:12 +08:00
|
|
|
OverAutorelease(CFRefCount *tf) :
|
2009-05-09 08:10:05 +08:00
|
|
|
CFRefBug(tf, "Object sent -autorelease too many times") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 08:10:05 +08:00
|
|
|
const char *getDescription() const {
|
2009-05-10 13:11:21 +08:00
|
|
|
return "Object sent -autorelease too many times";
|
2009-05-09 08:10:05 +08:00
|
|
|
}
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class ReturnedNotOwnedForOwned : public CFRefBug {
|
2009-05-10 14:25:57 +08:00
|
|
|
public:
|
|
|
|
ReturnedNotOwnedForOwned(CFRefCount *tf) :
|
|
|
|
CFRefBug(tf, "Method should return an owned object") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-10 14:25:57 +08:00
|
|
|
const char *getDescription() const {
|
|
|
|
return "Object with +0 retain counts returned to caller where a +1 "
|
|
|
|
"(owning) retain count is expected";
|
|
|
|
}
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class Leak : public CFRefBug {
|
2009-04-30 02:50:19 +08:00
|
|
|
const bool isReturn;
|
|
|
|
protected:
|
2009-11-30 02:27:55 +08:00
|
|
|
Leak(CFRefCount* tf, llvm::StringRef name, bool isRet)
|
2009-04-30 02:50:19 +08:00
|
|
|
: CFRefBug(tf, name), isReturn(isRet) {}
|
|
|
|
public:
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
const char* getDescription() const { return ""; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
bool isLeak() const { return true; }
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class LeakAtReturn : public Leak {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2009-11-30 02:27:55 +08:00
|
|
|
LeakAtReturn(CFRefCount* tf, llvm::StringRef name)
|
2009-04-30 02:50:19 +08:00
|
|
|
: Leak(tf, name, true) {}
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class LeakWithinFunction : public Leak {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2009-11-30 02:27:55 +08:00
|
|
|
LeakWithinFunction(CFRefCount* tf, llvm::StringRef name)
|
2009-04-30 02:50:19 +08:00
|
|
|
: Leak(tf, name, false) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
};
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
//===---------===//
|
|
|
|
// Bug Reports. //
|
|
|
|
//===---------===//
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class CFRefReport : public RangedBugReport {
|
2009-04-30 02:50:19 +08:00
|
|
|
protected:
|
|
|
|
SymbolRef Sym;
|
|
|
|
const CFRefCount &TF;
|
|
|
|
public:
|
|
|
|
CFRefReport(CFRefBug& D, const CFRefCount &tf,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode *n, SymbolRef sym)
|
2009-05-10 13:11:21 +08:00
|
|
|
: RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {}
|
|
|
|
|
|
|
|
CFRefReport(CFRefBug& D, const CFRefCount &tf,
|
2009-11-30 02:27:55 +08:00
|
|
|
ExplodedNode *n, SymbolRef sym, llvm::StringRef endText)
|
2009-05-12 18:10:00 +08:00
|
|
|
: RangedBugReport(D, D.getDescription(), endText, n), Sym(sym), TF(tf) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
virtual ~CFRefReport() {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
CFRefBug& getBugType() {
|
|
|
|
return (CFRefBug&) RangedBugReport::getBugType();
|
|
|
|
}
|
|
|
|
const CFRefBug& getBugType() const {
|
|
|
|
return (const CFRefBug&) RangedBugReport::getBugType();
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-18 16:58:41 +08:00
|
|
|
virtual void getRanges(const SourceRange*& beg, const SourceRange*& end) {
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!getBugType().isLeak())
|
2009-08-18 16:58:41 +08:00
|
|
|
RangedBugReport::getRanges(beg, end);
|
2009-04-30 02:50:19 +08:00
|
|
|
else
|
|
|
|
beg = end = 0;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
SymbolRef getSymbol() const { return Sym; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
PathDiagnosticPiece* getEndPath(BugReporterContext& BRC,
|
2009-08-06 09:32:16 +08:00
|
|
|
const ExplodedNode* N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
std::pair<const char**,const char**> getExtraDescriptiveText();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
PathDiagnosticPiece* VisitNode(const ExplodedNode* N,
|
|
|
|
const ExplodedNode* PrevN,
|
2009-05-07 05:39:49 +08:00
|
|
|
BugReporterContext& BRC);
|
2009-04-30 02:50:19 +08:00
|
|
|
};
|
2009-05-10 13:11:21 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class CFRefLeakReport : public CFRefReport {
|
2009-04-30 02:50:19 +08:00
|
|
|
SourceLocation AllocSite;
|
|
|
|
const MemRegion* AllocBinding;
|
|
|
|
public:
|
|
|
|
CFRefLeakReport(CFRefBug& D, const CFRefCount &tf,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode *n, SymbolRef sym,
|
2009-04-30 02:50:19 +08:00
|
|
|
GRExprEngine& Eng);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
PathDiagnosticPiece* getEndPath(BugReporterContext& BRC,
|
2009-08-06 09:32:16 +08:00
|
|
|
const ExplodedNode* N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
SourceLocation getLocation() const { return AllocSite; }
|
2009-09-09 23:08:12 +08:00
|
|
|
};
|
2009-02-14 11:16:10 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-17 06:32:20 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
static const char* Msgs[] = {
|
|
|
|
// GC only
|
2009-09-09 23:08:12 +08:00
|
|
|
"Code is compiled to only use garbage collection",
|
2009-04-30 02:50:19 +08:00
|
|
|
// No GC.
|
|
|
|
"Code is compiled to use reference counts",
|
|
|
|
// Hybrid, with GC.
|
|
|
|
"Code is compiled to use either garbage collection (GC) or reference counts"
|
2009-09-09 23:08:12 +08:00
|
|
|
" (non-GC). The bug occurs with GC enabled",
|
2009-04-30 02:50:19 +08:00
|
|
|
// Hybrid, without GC
|
|
|
|
"Code is compiled to use either garbage collection (GC) or reference counts"
|
|
|
|
" (non-GC). The bug occurs in non-GC mode"
|
|
|
|
};
|
2008-04-25 07:57:27 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
std::pair<const char**,const char**> CFRefReport::getExtraDescriptiveText() {
|
|
|
|
CFRefCount& TF = static_cast<CFRefBug&>(getBugType()).getTF();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
switch (TF.getLangOptions().getGCMode()) {
|
|
|
|
default:
|
|
|
|
assert(false);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case LangOptions::GCOnly:
|
|
|
|
assert (TF.isGCEnabled());
|
2009-09-09 23:08:12 +08:00
|
|
|
return std::make_pair(&Msgs[0], &Msgs[0]+1);
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case LangOptions::NonGC:
|
|
|
|
assert (!TF.isGCEnabled());
|
|
|
|
return std::make_pair(&Msgs[1], &Msgs[1]+1);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case LangOptions::HybridGC:
|
|
|
|
if (TF.isGCEnabled())
|
|
|
|
return std::make_pair(&Msgs[2], &Msgs[2]+1);
|
|
|
|
else
|
|
|
|
return std::make_pair(&Msgs[3], &Msgs[3]+1);
|
|
|
|
}
|
|
|
|
}
|
2008-04-12 06:25:11 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
static inline bool contains(const llvm::SmallVectorImpl<ArgEffect>& V,
|
|
|
|
ArgEffect X) {
|
|
|
|
for (llvm::SmallVectorImpl<ArgEffect>::const_iterator I=V.begin(), E=V.end();
|
|
|
|
I!=E; ++I)
|
|
|
|
if (*I == X) return true;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
|
|
|
|
const ExplodedNode* PrevN,
|
2009-05-07 05:39:49 +08:00
|
|
|
BugReporterContext& BRC) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-13 15:12:33 +08:00
|
|
|
if (!isa<PostStmt>(N->getLocation()))
|
|
|
|
return NULL;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
// Check if the type state has changed.
|
2009-06-18 09:23:53 +08:00
|
|
|
const GRState *PrevSt = PrevN->getState();
|
|
|
|
const GRState *CurrSt = N->getState();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
const RefVal* CurrT = CurrSt->get<RefBindings>(Sym);
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!CurrT) return NULL;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
const RefVal &CurrV = *CurrT;
|
|
|
|
const RefVal *PrevT = PrevSt->get<RefBindings>(Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Create a string buffer to constain all the useful things we want
|
|
|
|
// to tell the user.
|
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// This is the allocation site since the previous node had no bindings
|
|
|
|
// for this symbol.
|
|
|
|
if (!PrevT) {
|
2009-07-23 06:35:28 +08:00
|
|
|
const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-23 06:35:28 +08:00
|
|
|
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// Get the name of the callee (if it is available).
|
2010-02-09 00:18:51 +08:00
|
|
|
SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee());
|
2009-04-30 02:50:19 +08:00
|
|
|
if (const FunctionDecl* FD = X.getAsFunctionDecl())
|
|
|
|
os << "Call to function '" << FD->getNameAsString() <<'\'';
|
|
|
|
else
|
2009-09-09 23:08:12 +08:00
|
|
|
os << "function call";
|
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
else {
|
|
|
|
assert (isa<ObjCMessageExpr>(S));
|
|
|
|
os << "Method";
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (CurrV.getObjKind() == RetEffect::CF) {
|
|
|
|
os << " returns a Core Foundation object with a ";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert (CurrV.getObjKind() == RetEffect::ObjC);
|
|
|
|
os << " returns an Objective-C object with a ";
|
2008-04-18 02:12:53 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (CurrV.isOwned()) {
|
|
|
|
os << "+1 retain count (owning reference).";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) {
|
|
|
|
assert(CurrV.getObjKind() == RetEffect::CF);
|
|
|
|
os << " "
|
|
|
|
"Core Foundation objects are not automatically garbage collected.";
|
|
|
|
}
|
2008-04-18 02:12:53 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
else {
|
|
|
|
assert (CurrV.isNotOwned());
|
|
|
|
os << "+0 retain count (non-owning reference).";
|
2008-04-19 03:23:43 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
PathDiagnosticLocation Pos(S, BRC.getSourceManager());
|
2009-04-30 02:50:19 +08:00
|
|
|
return new PathDiagnosticEventPiece(Pos, os.str());
|
2008-04-19 03:23:43 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Gather up the effects that were performed on the object at this
|
|
|
|
// program point
|
|
|
|
llvm::SmallVector<ArgEffect, 2> AEffects;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
if (const RetainSummary *Summ =
|
|
|
|
TF.getSummaryOfNode(BRC.getNodeResolver().getOriginalNode(N))) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// We only have summaries attached to nodes after evaluating CallExpr and
|
|
|
|
// ObjCMessageExprs.
|
2009-07-23 06:35:28 +08:00
|
|
|
const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-23 06:35:28 +08:00
|
|
|
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// Iterate through the parameter expressions and see if the symbol
|
|
|
|
// was ever passed as an argument.
|
|
|
|
unsigned i = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-23 06:35:28 +08:00
|
|
|
for (CallExpr::const_arg_iterator AI=CE->arg_begin(), AE=CE->arg_end();
|
2009-04-30 02:50:19 +08:00
|
|
|
AI!=AE; ++AI, ++i) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Retrieve the value of the argument. Is it the symbol
|
|
|
|
// we are interested in?
|
2010-02-09 00:18:51 +08:00
|
|
|
if (CurrSt->getSValAsScalarOrLoc(*AI).getAsLocSymbol() != Sym)
|
2009-04-30 02:50:19 +08:00
|
|
|
continue;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// We have an argument. Get the effect!
|
|
|
|
AEffects.push_back(Summ->getArg(i));
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
|
2009-07-23 06:35:28 +08:00
|
|
|
if (const Expr *receiver = ME->getReceiver())
|
2010-02-09 00:18:51 +08:00
|
|
|
if (CurrSt->getSValAsScalarOrLoc(receiver).getAsLocSymbol() == Sym) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// The symbol we are tracking is the receiver.
|
|
|
|
AEffects.push_back(Summ->getReceiverEffect());
|
|
|
|
}
|
|
|
|
}
|
2009-02-19 02:54:33 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
do {
|
|
|
|
// Get the previous type state.
|
|
|
|
RefVal PrevV = *PrevT;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Specially handle -dealloc.
|
|
|
|
if (!TF.isGCEnabled() && contains(AEffects, Dealloc)) {
|
|
|
|
// Determine if the object's reference count was pushed to zero.
|
|
|
|
assert(!(PrevV == CurrV) && "The typestate *must* have changed.");
|
|
|
|
// We may not have transitioned to 'release' if we hit an error.
|
|
|
|
// This case is handled elsewhere.
|
|
|
|
if (CurrV.getKind() == RefVal::Released) {
|
2009-05-09 04:01:42 +08:00
|
|
|
assert(CurrV.getCombinedCounts() == 0);
|
2009-04-30 02:50:19 +08:00
|
|
|
os << "Object released by directly sending the '-dealloc' message";
|
2009-03-18 03:42:23 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Specially handle CFMakeCollectable and friends.
|
|
|
|
if (contains(AEffects, MakeCollectable)) {
|
|
|
|
// Get the name of the function.
|
2009-07-23 06:35:28 +08:00
|
|
|
const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
2010-02-09 00:18:51 +08:00
|
|
|
SVal X = CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee());
|
2009-04-30 02:50:19 +08:00
|
|
|
const FunctionDecl* FD = X.getAsFunctionDecl();
|
|
|
|
const std::string& FName = FD->getNameAsString();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (TF.isGCEnabled()) {
|
|
|
|
// Determine if the object's reference count was pushed to zero.
|
|
|
|
assert(!(PrevV == CurrV) && "The typestate *must* have changed.");
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
os << "In GC mode a call to '" << FName
|
|
|
|
<< "' decrements an object's retain count and registers the "
|
|
|
|
"object with the garbage collector. ";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (CurrV.getKind() == RefVal::Released) {
|
|
|
|
assert(CurrV.getCount() == 0);
|
|
|
|
os << "Since it now has a 0 retain count the object can be "
|
|
|
|
"automatically collected by the garbage collector.";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
os << "An object must have a 0 retain count to be garbage collected. "
|
|
|
|
"After this call its retain count is +" << CurrV.getCount()
|
|
|
|
<< '.';
|
2008-05-23 01:31:13 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
else
|
2009-04-30 02:50:19 +08:00
|
|
|
os << "When GC is not enabled a call to '" << FName
|
|
|
|
<< "' has no effect on its argument.";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Nothing more to say.
|
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Determine if the typestate has changed.
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!(PrevV == CurrV))
|
|
|
|
switch (CurrV.getKind()) {
|
2008-03-12 01:48:22 +08:00
|
|
|
case RefVal::Owned:
|
|
|
|
case RefVal::NotOwned:
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 04:01:42 +08:00
|
|
|
if (PrevV.getCount() == CurrV.getCount()) {
|
|
|
|
// Did an autorelease message get sent?
|
|
|
|
if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
|
|
|
|
return 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-12 18:10:00 +08:00
|
|
|
assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
|
2009-05-10 13:11:21 +08:00
|
|
|
os << "Object sent -autorelease message";
|
2009-05-09 04:01:42 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (PrevV.getCount() > CurrV.getCount())
|
|
|
|
os << "Reference count decremented.";
|
|
|
|
else
|
|
|
|
os << "Reference count incremented.";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (unsigned Count = CurrV.getCount())
|
|
|
|
os << " The object now has a +" << Count << " retain count.";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (PrevV.getKind() == RefVal::Released) {
|
|
|
|
assert(TF.isGCEnabled() && CurrV.getCount() > 0);
|
|
|
|
os << " The object is not eligible for garbage collection until the "
|
|
|
|
"retain count reaches 0 again.";
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-12 01:48:22 +08:00
|
|
|
case RefVal::Released:
|
2009-04-30 02:50:19 +08:00
|
|
|
os << "Object released.";
|
2008-03-12 01:48:22 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RefVal::ReturnedOwned:
|
|
|
|
os << "Object returned to caller as an owning reference (single retain "
|
|
|
|
"count transferred to caller).";
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RefVal::ReturnedNotOwned:
|
|
|
|
os << "Object returned to caller with a +0 (non-owning) retain count.";
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Emit any remaining diagnostics for the argument effects (if any).
|
|
|
|
for (llvm::SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(),
|
|
|
|
E=AEffects.end(); I != E; ++I) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// A bunch of things have alternate behavior under GC.
|
|
|
|
if (TF.isGCEnabled())
|
|
|
|
switch (*I) {
|
|
|
|
default: break;
|
|
|
|
case Autorelease:
|
|
|
|
os << "In GC mode an 'autorelease' has no effect.";
|
|
|
|
continue;
|
|
|
|
case IncRefMsg:
|
|
|
|
os << "In GC mode the 'retain' message has no effect.";
|
|
|
|
continue;
|
|
|
|
case DecRefMsg:
|
|
|
|
os << "In GC mode the 'release' message has no effect.";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
} while (0);
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (os.str().empty())
|
|
|
|
return 0; // We have nothing to say!
|
2009-05-13 15:12:33 +08:00
|
|
|
|
2009-07-23 06:35:28 +08:00
|
|
|
const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
2009-05-07 05:39:49 +08:00
|
|
|
PathDiagnosticLocation Pos(S, BRC.getSourceManager());
|
2009-04-30 02:50:19 +08:00
|
|
|
PathDiagnosticPiece* P = new PathDiagnosticEventPiece(Pos, os.str());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Add the range by scanning the children of the statement for any bindings
|
|
|
|
// to Sym.
|
2009-09-09 23:08:12 +08:00
|
|
|
for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end();
|
2009-07-23 06:35:28 +08:00
|
|
|
I!=E; ++I)
|
|
|
|
if (const Expr* Exp = dyn_cast_or_null<Expr>(*I))
|
2010-02-09 00:18:51 +08:00
|
|
|
if (CurrSt->getSValAsScalarOrLoc(Exp).getAsLocSymbol() == Sym) {
|
2009-04-30 02:50:19 +08:00
|
|
|
P->addRange(Exp->getSourceRange());
|
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
return P;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class FindUniqueBinding :
|
2009-04-30 02:50:19 +08:00
|
|
|
public StoreManager::BindingsHandler {
|
|
|
|
SymbolRef Sym;
|
|
|
|
const MemRegion* Binding;
|
|
|
|
bool First;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
|
|
|
FindUniqueBinding(SymbolRef sym) : Sym(sym), Binding(0), First(true) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R,
|
|
|
|
SVal val) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
SymbolRef SymV = val.getAsSymbol();
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!SymV || SymV != Sym)
|
|
|
|
return true;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (Binding) {
|
|
|
|
First = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Binding = R;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
return true;
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
operator bool() { return First && Binding; }
|
|
|
|
const MemRegion* getRegion() { return Binding; }
|
2009-09-09 23:08:12 +08:00
|
|
|
};
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2008-08-13 02:30:56 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
static std::pair<const ExplodedNode*,const MemRegion*>
|
|
|
|
GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode* N,
|
2009-04-30 02:50:19 +08:00
|
|
|
SymbolRef Sym) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Find both first node that referred to the tracked symbol and the
|
|
|
|
// memory location that value was store to.
|
2009-08-06 09:32:16 +08:00
|
|
|
const ExplodedNode* Last = N;
|
2009-09-09 23:08:12 +08:00
|
|
|
const MemRegion* FirstBinding = 0;
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
while (N) {
|
|
|
|
const GRState* St = N->getState();
|
|
|
|
RefBindings B = St->get<RefBindings>();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!B.lookup(Sym))
|
2008-04-11 07:44:06 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
FindUniqueBinding FB(Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
StateMgr.iterBindings(St, FB);
|
|
|
|
if (FB) FirstBinding = FB.getRegion();
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
Last = N;
|
2009-09-09 23:08:12 +08:00
|
|
|
N = N->pred_empty() ? NULL : *(N->pred_begin());
|
2008-03-12 01:48:22 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
return std::make_pair(Last, FirstBinding);
|
2008-03-11 14:39:11 +08:00
|
|
|
}
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
PathDiagnosticPiece*
|
2009-05-07 05:39:49 +08:00
|
|
|
CFRefReport::getEndPath(BugReporterContext& BRC,
|
2009-08-06 09:32:16 +08:00
|
|
|
const ExplodedNode* EndN) {
|
2009-05-07 05:39:49 +08:00
|
|
|
// Tell the BugReporterContext to report cases when the tracked symbol is
|
2009-04-30 02:50:19 +08:00
|
|
|
// assigned to different variables, etc.
|
2009-05-07 05:39:49 +08:00
|
|
|
BRC.addNotableSymbol(Sym);
|
|
|
|
return RangedBugReport::getEndPath(BRC, EndN);
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2008-04-09 09:10:13 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
PathDiagnosticPiece*
|
2009-05-07 05:39:49 +08:00
|
|
|
CFRefLeakReport::getEndPath(BugReporterContext& BRC,
|
2009-08-06 09:32:16 +08:00
|
|
|
const ExplodedNode* EndN){
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
// Tell the BugReporterContext to report cases when the tracked symbol is
|
2009-04-30 02:50:19 +08:00
|
|
|
// assigned to different variables, etc.
|
2009-05-07 05:39:49 +08:00
|
|
|
BRC.addNotableSymbol(Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// We are reporting a leak. Walk up the graph to get to the first node where
|
|
|
|
// the symbol appeared, and also get the first VarDecl that tracked object
|
|
|
|
// is stored to.
|
2009-08-06 09:32:16 +08:00
|
|
|
const ExplodedNode* AllocNode = 0;
|
2009-04-30 02:50:19 +08:00
|
|
|
const MemRegion* FirstBinding = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
llvm::tie(AllocNode, FirstBinding) =
|
2009-05-09 07:32:51 +08:00
|
|
|
GetAllocationSite(BRC.getStateManager(), EndN, Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Get the allocate site.
|
2009-04-30 02:50:19 +08:00
|
|
|
assert(AllocNode);
|
2009-07-23 06:35:28 +08:00
|
|
|
const Stmt* FirstStmt = cast<PostStmt>(AllocNode->getLocation()).getStmt();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
SourceManager& SMgr = BRC.getSourceManager();
|
2009-04-30 02:50:19 +08:00
|
|
|
unsigned AllocLine =SMgr.getInstantiationLineNumber(FirstStmt->getLocStart());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Compute an actual location for the leak. Sometimes a leak doesn't
|
|
|
|
// occur at an actual statement (e.g., transition between blocks; end
|
|
|
|
// of function) so we need to walk the graph and compute a real location.
|
2009-08-06 09:32:16 +08:00
|
|
|
const ExplodedNode* LeakN = EndN;
|
2009-04-30 02:50:19 +08:00
|
|
|
PathDiagnosticLocation L;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
while (LeakN) {
|
|
|
|
ProgramPoint P = LeakN->getLocation();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
|
|
|
|
L = PathDiagnosticLocation(PS->getStmt()->getLocStart(), SMgr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
|
|
|
|
if (const Stmt* Term = BE->getSrc()->getTerminator()) {
|
|
|
|
L = PathDiagnosticLocation(Term->getLocStart(), SMgr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
LeakN = LeakN->succ_empty() ? 0 : *(LeakN->succ_begin());
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!L.isValid()) {
|
2009-09-10 13:44:00 +08:00
|
|
|
const Decl &D = EndN->getCodeDecl();
|
2009-06-30 10:35:26 +08:00
|
|
|
L = PathDiagnosticLocation(D.getBodyRBrace(), SMgr);
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
os << "Object allocated on line " << AllocLine;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (FirstBinding)
|
2009-09-09 23:08:12 +08:00
|
|
|
os << " and stored into '" << FirstBinding->getString() << '\'';
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Get the retain count.
|
|
|
|
const RefVal* RV = EndN->getState()->get<RefBindings>(Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (RV->getKind() == RefVal::ErrorLeakReturned) {
|
|
|
|
// FIXME: Per comments in rdar://6320065, "create" only applies to CF
|
|
|
|
// ojbects. Only "copy", "alloc", "retain" and "new" transfer ownership
|
|
|
|
// to the caller for NS objects.
|
2009-09-10 13:44:00 +08:00
|
|
|
ObjCMethodDecl& MD = cast<ObjCMethodDecl>(EndN->getCodeDecl());
|
2009-04-30 02:50:19 +08:00
|
|
|
os << " is returned from a method whose name ('"
|
2009-04-30 07:03:22 +08:00
|
|
|
<< MD.getSelector().getAsString()
|
2009-04-30 02:50:19 +08:00
|
|
|
<< "') does not contain 'copy' or otherwise starts with"
|
|
|
|
" 'new' or 'alloc'. This violates the naming convention rules given"
|
2009-04-30 06:25:52 +08:00
|
|
|
" in the Memory Management Guide for Cocoa (object leaked)";
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-05-10 14:25:57 +08:00
|
|
|
else if (RV->getKind() == RefVal::ErrorGCLeakReturned) {
|
2009-09-10 13:44:00 +08:00
|
|
|
ObjCMethodDecl& MD = cast<ObjCMethodDecl>(EndN->getCodeDecl());
|
2009-05-10 14:25:57 +08:00
|
|
|
os << " and returned from method '" << MD.getSelector().getAsString()
|
2009-05-11 00:52:15 +08:00
|
|
|
<< "' is potentially leaked when using garbage collection. Callers "
|
|
|
|
"of this method do not expect a returned object with a +1 retain "
|
|
|
|
"count since they expect the object to be managed by the garbage "
|
|
|
|
"collector";
|
2009-05-10 14:25:57 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
else
|
|
|
|
os << " is no longer referenced after this point and has a retain count of"
|
2009-04-30 06:25:52 +08:00
|
|
|
" +" << RV->getCount() << " (object leaked)";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
return new PathDiagnosticEventPiece(L, os.str());
|
|
|
|
}
|
2009-02-05 07:49:09 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode *n,
|
2009-04-30 02:50:19 +08:00
|
|
|
SymbolRef sym, GRExprEngine& Eng)
|
2009-09-09 23:08:12 +08:00
|
|
|
: CFRefReport(D, tf, n, sym) {
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Most bug reports are cached at the location where they occured.
|
|
|
|
// With leaks, we want to unique them by the location where they were
|
|
|
|
// allocated, and only report a single path. To do this, we need to find
|
|
|
|
// the allocation site of a piece of tracked memory, which we do via a
|
|
|
|
// call to GetAllocationSite. This will walk the ExplodedGraph backwards.
|
|
|
|
// Note that this is *not* the trimmed graph; we are guaranteed, however,
|
|
|
|
// that all ancestor nodes that represent the allocation site have the
|
|
|
|
// same SourceLocation.
|
2009-08-06 09:32:16 +08:00
|
|
|
const ExplodedNode* AllocNode = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding.
|
2009-05-09 07:32:51 +08:00
|
|
|
GetAllocationSite(Eng.getStateManager(), getEndNode(), getSymbol());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Get the SourceLocation for the allocation site.
|
|
|
|
ProgramPoint P = AllocNode->getLocation();
|
|
|
|
AllocSite = cast<PostStmt>(P).getStmt()->getLocStart();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Fill in the description of the bug.
|
|
|
|
Description.clear();
|
|
|
|
llvm::raw_string_ostream os(Description);
|
|
|
|
SourceManager& SMgr = Eng.getContext().getSourceManager();
|
|
|
|
unsigned AllocLine = SMgr.getInstantiationLineNumber(AllocSite);
|
2009-05-03 03:05:19 +08:00
|
|
|
os << "Potential leak ";
|
|
|
|
if (tf.isGCEnabled()) {
|
|
|
|
os << "(when using garbage collection) ";
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-05-03 03:05:19 +08:00
|
|
|
os << "of an object allocated on line " << AllocLine;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// FIXME: AllocBinding doesn't get populated for RegionStore yet.
|
|
|
|
if (AllocBinding)
|
|
|
|
os << " and stored into '" << AllocBinding->getString() << '\'';
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Main checker logic.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// 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).
|
2009-07-11 07:34:53 +08:00
|
|
|
static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) {
|
2009-04-30 02:50:19 +08:00
|
|
|
QualType RetTy = RetE->getType();
|
2009-07-11 07:34:53 +08:00
|
|
|
// If RetE is not a message expression just return its type.
|
|
|
|
// If RetE is a message expression, return its types if it is something
|
2009-04-30 02:50:19 +08:00
|
|
|
/// more specific than id.
|
2009-07-11 07:34:53 +08:00
|
|
|
if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RetE))
|
2009-09-22 07:43:11 +08:00
|
|
|
if (const ObjCObjectPointerType *PT = RetTy->getAs<ObjCObjectPointerType>())
|
2009-09-09 23:08:12 +08:00
|
|
|
if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() ||
|
2009-07-11 07:34:53 +08:00
|
|
|
PT->isObjCClassType()) {
|
|
|
|
// At this point we know the return type of the message expression is
|
|
|
|
// id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this
|
|
|
|
// is a call to a class method whose type we can resolve. In such
|
|
|
|
// cases, promote the return type to XXX* (where XXX is the class).
|
2009-09-09 23:08:12 +08:00
|
|
|
const ObjCInterfaceDecl *D = ME->getClassInfo().first;
|
2009-07-11 07:34:53 +08:00
|
|
|
return !D ? RetTy : Ctx.getPointerType(Ctx.getObjCInterfaceType(D));
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-11 07:34:53 +08:00
|
|
|
return RetTy;
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-02-08 06:04:05 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
void CFRefCount::EvalSummary(ExplodedNodeSet& Dst,
|
2009-04-30 02:50:19 +08:00
|
|
|
GRExprEngine& Eng,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2009-04-30 02:50:19 +08:00
|
|
|
Expr* Ex,
|
|
|
|
Expr* Receiver,
|
2009-05-04 12:57:00 +08:00
|
|
|
const RetainSummary& Summ,
|
2009-12-03 16:25:47 +08:00
|
|
|
const MemRegion *Callee,
|
2009-04-30 02:50:19 +08:00
|
|
|
ExprIterator arg_beg, ExprIterator arg_end,
|
2009-12-02 13:49:12 +08:00
|
|
|
ExplodedNode* Pred, const GRState *state) {
|
2009-04-30 02:50:19 +08:00
|
|
|
|
|
|
|
// Evaluate the effect of the arguments.
|
|
|
|
RefVal::Kind hasErr = (RefVal::Kind) 0;
|
|
|
|
unsigned idx = 0;
|
|
|
|
Expr* ErrorExpr = NULL;
|
2009-09-09 23:08:12 +08:00
|
|
|
SymbolRef ErrorSym = 0;
|
|
|
|
|
2009-12-03 11:27:11 +08:00
|
|
|
llvm::SmallVector<const MemRegion*, 10> RegionsToInvalidate;
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) {
|
2010-02-09 00:18:51 +08:00
|
|
|
SVal V = state->getSValAsScalarOrLoc(*I);
|
2009-04-30 02:50:19 +08:00
|
|
|
SymbolRef Sym = V.getAsLocSymbol();
|
|
|
|
|
|
|
|
if (Sym)
|
2009-06-18 09:23:53 +08:00
|
|
|
if (RefBindings::data_type* T = state->get<RefBindings>(Sym)) {
|
2009-05-04 12:57:00 +08:00
|
|
|
state = Update(state, Sym, *T, Summ.getArg(idx), hasErr);
|
2009-04-30 02:50:19 +08:00
|
|
|
if (hasErr) {
|
|
|
|
ErrorExpr = *I;
|
|
|
|
ErrorSym = Sym;
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-09-22 12:48:39 +08:00
|
|
|
tryAgain:
|
2009-04-30 02:50:19 +08:00
|
|
|
if (isa<Loc>(V)) {
|
|
|
|
if (loc::MemRegionVal* MR = dyn_cast<loc::MemRegionVal>(&V)) {
|
2009-05-04 12:57:00 +08:00
|
|
|
if (Summ.getArg(idx) == DoNothingByRef)
|
2009-04-30 02:50:19 +08:00
|
|
|
continue;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Invalidate the value of the variable passed by reference.
|
2009-07-06 14:01:24 +08:00
|
|
|
const MemRegion *R = MR->getRegion();
|
2009-12-03 11:27:11 +08:00
|
|
|
|
2009-07-06 14:01:24 +08:00
|
|
|
// Are we dealing with an ElementRegion? If the element type is
|
|
|
|
// a basic integer type (e.g., char, int) and the underying region
|
|
|
|
// is a variable region then strip off the ElementRegion.
|
|
|
|
// FIXME: We really need to think about this for the general case
|
|
|
|
// as sometimes we are reasoning about arrays and other times
|
|
|
|
// about (char*), etc., is just a form of passing raw bytes.
|
|
|
|
// e.g., void *p = alloca(); foo((char*)p);
|
|
|
|
if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
|
|
|
|
// Checking for 'integral type' is probably too promiscuous, but
|
|
|
|
// we'll leave it in for now until we have a systematic way of
|
|
|
|
// handling all of these cases. Eventually we need to come up
|
|
|
|
// with an interface to StoreManager so that this logic can be
|
|
|
|
// approriately delegated to the respective StoreManagers while
|
|
|
|
// still allowing us to do checker-specific logic (e.g.,
|
|
|
|
// invalidating reference counts), probably via callbacks.
|
|
|
|
if (ER->getElementType()->isIntegralType()) {
|
|
|
|
const MemRegion *superReg = ER->getSuperRegion();
|
|
|
|
if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) ||
|
|
|
|
isa<ObjCIvarRegion>(superReg))
|
|
|
|
R = cast<TypedRegion>(superReg);
|
2009-05-07 02:19:24 +08:00
|
|
|
}
|
2009-07-06 14:01:24 +08:00
|
|
|
// FIXME: What about layers of ElementRegions?
|
2009-06-29 14:43:40 +08:00
|
|
|
}
|
2009-12-03 11:27:11 +08:00
|
|
|
|
|
|
|
// Mark this region for invalidation. We batch invalidate regions
|
|
|
|
// below for efficiency.
|
|
|
|
RegionsToInvalidate.push_back(R);
|
|
|
|
continue;
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Nuke all other arguments passed by reference.
|
2009-09-22 12:48:39 +08:00
|
|
|
// FIXME: is this necessary or correct? This handles the non-Region
|
|
|
|
// cases. Is it ever valid to store to these?
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->unbindLoc(cast<Loc>(V));
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
|
|
|
}
|
2009-09-22 12:48:39 +08:00
|
|
|
else if (isa<nonloc::LocAsInteger>(V)) {
|
|
|
|
// If we are passing a location wrapped as an integer, unwrap it and
|
|
|
|
// invalidate the values referred by the location.
|
|
|
|
V = cast<nonloc::LocAsInteger>(V).getLoc();
|
|
|
|
goto tryAgain;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-12-03 11:27:11 +08:00
|
|
|
|
2009-12-03 16:25:47 +08:00
|
|
|
// Block calls result in all captured values passed-via-reference to be
|
|
|
|
// invalidated.
|
|
|
|
if (const BlockDataRegion *BR = dyn_cast_or_null<BlockDataRegion>(Callee)) {
|
|
|
|
RegionsToInvalidate.push_back(BR);
|
|
|
|
}
|
|
|
|
|
2009-12-03 11:27:11 +08:00
|
|
|
// Invalidate regions we designed for invalidation use the batch invalidation
|
|
|
|
// API.
|
|
|
|
if (!RegionsToInvalidate.empty()) {
|
|
|
|
// 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.
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
|
|
|
StoreManager& StoreMgr = Eng.getStateManager().getStoreManager();
|
|
|
|
|
|
|
|
|
|
|
|
StoreManager::InvalidatedSymbols IS;
|
2010-02-05 13:06:13 +08:00
|
|
|
Store store = state->getStore();
|
|
|
|
store = StoreMgr.InvalidateRegions(store, RegionsToInvalidate.data(),
|
2009-12-03 11:27:11 +08:00
|
|
|
RegionsToInvalidate.data() +
|
|
|
|
RegionsToInvalidate.size(),
|
|
|
|
Ex, Count, &IS);
|
2010-02-05 13:06:13 +08:00
|
|
|
state = state->makeWithStore(store);
|
2009-12-03 11:27:11 +08:00
|
|
|
for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(),
|
|
|
|
E = IS.end(); I!=E; ++I) {
|
|
|
|
// Remove any existing reference-count binding.
|
|
|
|
state = state->remove<RefBindings>(*I);
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Evaluate the effect on the message receiver.
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!ErrorExpr && Receiver) {
|
2010-02-09 00:18:51 +08:00
|
|
|
SymbolRef Sym = state->getSValAsScalarOrLoc(Receiver).getAsLocSymbol();
|
2009-04-30 02:50:19 +08:00
|
|
|
if (Sym) {
|
2009-06-18 09:23:53 +08:00
|
|
|
if (const RefVal* T = state->get<RefBindings>(Sym)) {
|
2009-05-04 12:57:00 +08:00
|
|
|
state = Update(state, Sym, *T, Summ.getReceiverEffect(), hasErr);
|
2009-04-30 02:50:19 +08:00
|
|
|
if (hasErr) {
|
|
|
|
ErrorExpr = Receiver;
|
|
|
|
ErrorSym = Sym;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-02-05 07:49:09 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Process any errors.
|
2009-04-30 02:50:19 +08:00
|
|
|
if (hasErr) {
|
|
|
|
ProcessNonLeakError(Dst, Builder, Ex, ErrorExpr, Pred, state,
|
|
|
|
hasErr, ErrorSym);
|
|
|
|
return;
|
2009-02-05 07:49:09 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Consult the summary for the return value.
|
2009-05-04 12:57:00 +08:00
|
|
|
RetEffect RE = Summ.getRetEffect();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-13 04:06:54 +08:00
|
|
|
if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) {
|
|
|
|
assert(Receiver);
|
2010-02-09 00:18:51 +08:00
|
|
|
SVal V = state->getSValAsScalarOrLoc(Receiver);
|
2009-05-13 04:06:54 +08:00
|
|
|
bool found = false;
|
|
|
|
if (SymbolRef Sym = V.getAsLocSymbol())
|
2009-06-18 09:23:53 +08:00
|
|
|
if (state->get<RefBindings>(Sym)) {
|
2009-05-13 04:06:54 +08:00
|
|
|
found = true;
|
|
|
|
RE = Summaries.getObjAllocRetEffect();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
RE = RetEffect::MakeNoRet();
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
switch (RE.getKind()) {
|
2008-05-01 07:47:44 +08:00
|
|
|
default:
|
2009-04-30 02:50:19 +08:00
|
|
|
assert (false && "Unhandled RetEffect."); break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
case RetEffect::NoRet: {
|
2009-04-30 02:50:19 +08:00
|
|
|
// Make up a symbol for the return value (not reference counted).
|
2009-06-26 08:05:51 +08:00
|
|
|
// FIXME: Most of this logic is not specific to the retain/release
|
|
|
|
// checker.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// FIXME: We eventually should handle structs and other compound types
|
|
|
|
// that are returned by value.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
QualType T = Ex->getType();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-12-19 11:17:55 +08:00
|
|
|
// For CallExpr, use the result type to know if it returns a reference.
|
|
|
|
if (const CallExpr *CE = dyn_cast<CallExpr>(Ex)) {
|
|
|
|
const Expr *Callee = CE->getCallee();
|
2010-02-09 00:18:51 +08:00
|
|
|
if (const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl())
|
2009-12-19 11:17:55 +08:00
|
|
|
T = FD->getResultType();
|
|
|
|
}
|
2009-12-23 06:13:46 +08:00
|
|
|
else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(Ex)) {
|
|
|
|
if (const ObjCMethodDecl *MD = ME->getMethodDecl())
|
|
|
|
T = MD->getResultType();
|
|
|
|
}
|
2009-12-19 11:17:55 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) {
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
|
|
|
ValueManager &ValMgr = Eng.getValueManager();
|
2009-09-28 04:45:21 +08:00
|
|
|
SVal X = ValMgr.getConjuredSymbolVal(NULL, Ex, T, Count);
|
2009-08-28 06:17:37 +08:00
|
|
|
state = state->BindExpr(Ex, X, false);
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-01-28 14:06:36 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RetEffect::Alias: {
|
|
|
|
unsigned idx = RE.getIndex();
|
|
|
|
assert (arg_end >= arg_beg);
|
|
|
|
assert (idx < (unsigned) (arg_end - arg_beg));
|
2010-02-09 00:18:51 +08:00
|
|
|
SVal V = state->getSValAsScalarOrLoc(*(arg_beg+idx));
|
2009-08-28 06:17:37 +08:00
|
|
|
state = state->BindExpr(Ex, V, false);
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-01-28 14:25:48 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RetEffect::ReceiverAlias: {
|
|
|
|
assert (Receiver);
|
2010-02-09 00:18:51 +08:00
|
|
|
SVal V = state->getSValAsScalarOrLoc(Receiver);
|
2009-08-28 06:17:37 +08:00
|
|
|
state = state->BindExpr(Ex, V, false);
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2008-04-18 12:55:01 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RetEffect::OwnedAllocatedSymbol:
|
|
|
|
case RetEffect::OwnedSymbol: {
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2009-09-09 23:08:12 +08:00
|
|
|
ValueManager &ValMgr = Eng.getValueManager();
|
2009-04-30 02:50:19 +08:00
|
|
|
SymbolRef Sym = ValMgr.getConjuredSymbol(Ex, Count);
|
2009-09-09 23:08:12 +08:00
|
|
|
QualType RetT = GetReturnType(Ex, ValMgr.getContext());
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(),
|
2009-04-30 02:50:19 +08:00
|
|
|
RetT));
|
2009-08-28 06:17:37 +08:00
|
|
|
state = state->BindExpr(Ex, ValMgr.makeLoc(Sym), false);
|
2009-03-04 06:06:47 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// FIXME: Add a flag to the checker where allocations are assumed to
|
|
|
|
// *not fail.
|
|
|
|
#if 0
|
|
|
|
if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) {
|
|
|
|
bool isFeasible;
|
|
|
|
state = state.Assume(loc::SymbolVal(Sym), true, isFeasible);
|
2009-09-09 23:08:12 +08:00
|
|
|
assert(isFeasible && "Cannot assume fresh symbol is non-null.");
|
2008-04-18 13:32:44 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
#endif
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-02-18 11:48:14 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RetEffect::GCNotOwnedSymbol:
|
|
|
|
case RetEffect::NotOwnedSymbol: {
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
|
|
|
ValueManager &ValMgr = Eng.getValueManager();
|
|
|
|
SymbolRef Sym = ValMgr.getConjuredSymbol(Ex, Count);
|
2009-09-09 23:08:12 +08:00
|
|
|
QualType RetT = GetReturnType(Ex, ValMgr.getContext());
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(),
|
2009-04-30 02:50:19 +08:00
|
|
|
RetT));
|
2009-08-28 06:17:37 +08:00
|
|
|
state = state->BindExpr(Ex, ValMgr.makeLoc(Sym), false);
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-02-18 11:48:14 +08:00
|
|
|
}
|
2009-02-19 05:57:45 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Generate a sink node if we are at the end of a path.
|
2009-08-06 20:48:26 +08:00
|
|
|
ExplodedNode *NewNode =
|
2009-05-04 12:57:00 +08:00
|
|
|
Summ.isEndPath() ? Builder.MakeSinkNode(Dst, Ex, Pred, state)
|
|
|
|
: Builder.MakeNode(Dst, Ex, Pred, state);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Annotate the edge with summary we used.
|
2009-05-04 12:57:00 +08:00
|
|
|
if (NewNode) SummaryLog[NewNode] = &Summ;
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-02-19 05:57:45 +08:00
|
|
|
|
2009-02-18 11:48:14 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
void CFRefCount::EvalCall(ExplodedNodeSet& Dst,
|
2009-04-30 02:50:19 +08:00
|
|
|
GRExprEngine& Eng,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2009-04-30 02:50:19 +08:00
|
|
|
CallExpr* CE, SVal L,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred) {
|
2009-11-25 09:35:18 +08:00
|
|
|
|
|
|
|
RetainSummary *Summ = 0;
|
|
|
|
|
|
|
|
// FIXME: Better support for blocks. For now we stop tracking anything
|
|
|
|
// that is passed to blocks.
|
|
|
|
// FIXME: Need to handle variables that are "captured" by the block.
|
2009-11-26 07:53:07 +08:00
|
|
|
if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
|
2009-11-25 09:35:18 +08:00
|
|
|
Summ = Summaries.getPersistentStopSummary();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const FunctionDecl* FD = L.getAsFunctionDecl();
|
|
|
|
Summ = !FD ? Summaries.getDefaultSummary() :
|
|
|
|
Summaries.getSummary(const_cast<FunctionDecl*>(FD));
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 12:57:00 +08:00
|
|
|
assert(Summ);
|
2009-12-03 16:25:47 +08:00
|
|
|
EvalSummary(Dst, Eng, Builder, CE, 0, *Summ, L.getAsRegion(),
|
2009-12-02 13:49:12 +08:00
|
|
|
CE->arg_begin(), CE->arg_end(), Pred, Builder.GetState(Pred));
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst,
|
2009-04-30 02:50:19 +08:00
|
|
|
GRExprEngine& Eng,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2009-04-30 02:50:19 +08:00
|
|
|
ObjCMessageExpr* ME,
|
2009-12-02 13:49:12 +08:00
|
|
|
ExplodedNode* Pred,
|
|
|
|
const GRState *state) {
|
2009-11-13 09:54:21 +08:00
|
|
|
RetainSummary *Summ =
|
|
|
|
ME->getReceiver()
|
2009-12-02 13:49:12 +08:00
|
|
|
? Summaries.getInstanceMethodSummary(ME, state,Pred->getLocationContext())
|
2009-11-13 09:54:21 +08:00
|
|
|
: Summaries.getClassMethodSummary(ME);
|
2009-04-30 02:50:19 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
assert(Summ && "RetainSummary is null");
|
2009-12-03 16:25:47 +08:00
|
|
|
EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), *Summ, NULL,
|
2009-12-02 13:49:12 +08:00
|
|
|
ME->arg_begin(), ME->arg_end(), Pred, state);
|
2008-04-18 11:39:05 +08:00
|
|
|
}
|
|
|
|
|
2008-10-04 13:50:14 +08:00
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class StopTrackingCallback : public SymbolVisitor {
|
2009-06-18 08:49:02 +08:00
|
|
|
const GRState *state;
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2009-06-18 08:49:02 +08:00
|
|
|
StopTrackingCallback(const GRState *st) : state(st) {}
|
|
|
|
const GRState *getState() const { return state; }
|
2009-03-26 11:35:11 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
bool VisitSymbol(SymbolRef sym) {
|
2009-06-18 08:49:02 +08:00
|
|
|
state = state->remove<RefBindings>(sym);
|
2009-04-30 02:50:19 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
void CFRefCount::EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val) {
|
|
|
|
// Are we storing to something that causes the value to "escape"?
|
2009-04-30 02:50:19 +08:00
|
|
|
bool escapes = false;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// A value escapes in three possible cases (this may change):
|
|
|
|
//
|
|
|
|
// (1) we are binding to something that is not a memory region.
|
|
|
|
// (2) we are binding to a memregion that does not have stack storage
|
|
|
|
// (3) we are binding to a memregion with stack storage that the store
|
2009-09-09 23:08:12 +08:00
|
|
|
// does not understand.
|
2009-06-18 08:49:02 +08:00
|
|
|
const GRState *state = B.getState();
|
2009-04-30 02:50:19 +08:00
|
|
|
|
|
|
|
if (!isa<loc::MemRegionVal>(location))
|
|
|
|
escapes = true;
|
|
|
|
else {
|
|
|
|
const MemRegion* R = cast<loc::MemRegionVal>(location).getRegion();
|
2009-06-24 02:05:21 +08:00
|
|
|
escapes = !R->hasStackStorage();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!escapes) {
|
|
|
|
// To test (3), generate a new state with the binding removed. If it is
|
|
|
|
// the same state, then it escapes (since the store cannot represent
|
|
|
|
// the binding).
|
2009-06-18 09:23:53 +08:00
|
|
|
escapes = (state == (state->bindLoc(cast<Loc>(location), UnknownVal())));
|
2008-10-04 13:50:14 +08:00
|
|
|
}
|
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
|
|
|
|
// If our store can represent the binding and we aren't storing to something
|
|
|
|
// that doesn't have local storage then just return and have the simulation
|
|
|
|
// state continue as is.
|
|
|
|
if (!escapes)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Otherwise, find all symbols referenced by 'val' that we are tracking
|
|
|
|
// and stop tracking them.
|
2009-06-18 08:49:02 +08:00
|
|
|
B.MakeNode(state->scanReachableSymbols<StopTrackingCallback>(val).getState());
|
2008-10-04 13:50:14 +08:00
|
|
|
}
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Return statements.
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
void CFRefCount::EvalReturn(ExplodedNodeSet& Dst,
|
2009-04-30 02:50:19 +08:00
|
|
|
GRExprEngine& Eng,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2009-04-30 02:50:19 +08:00
|
|
|
ReturnStmt* S,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
Expr* RetE = S->getRetValue();
|
|
|
|
if (!RetE)
|
|
|
|
return;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
const GRState *state = Builder.GetState(Pred);
|
2010-02-09 00:18:51 +08:00
|
|
|
SymbolRef Sym = state->getSValAsScalarOrLoc(RetE).getAsLocSymbol();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!Sym)
|
|
|
|
return;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Get the reference count binding (if any).
|
2009-06-18 09:23:53 +08:00
|
|
|
const RefVal* T = state->get<RefBindings>(Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!T)
|
|
|
|
return;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Change the reference count.
|
|
|
|
RefVal X = *T;
|
|
|
|
|
|
|
|
switch (X.getKind()) {
|
|
|
|
case RefVal::Owned: {
|
2009-04-30 02:50:19 +08:00
|
|
|
unsigned cnt = X.getCount();
|
|
|
|
assert (cnt > 0);
|
2009-05-10 13:11:21 +08:00
|
|
|
X.setCount(cnt - 1);
|
|
|
|
X = X ^ RefVal::ReturnedOwned;
|
2009-04-07 08:12:43 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RefVal::NotOwned: {
|
|
|
|
unsigned cnt = X.getCount();
|
2009-05-10 13:11:21 +08:00
|
|
|
if (cnt) {
|
|
|
|
X.setCount(cnt - 1);
|
|
|
|
X = X ^ RefVal::ReturnedOwned;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
X = X ^ RefVal::ReturnedNotOwned;
|
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-04-07 08:12:43 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
default:
|
2009-04-30 02:50:19 +08:00
|
|
|
return;
|
2009-04-07 08:12:43 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Update the binding.
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->set<RefBindings>(Sym, X);
|
2009-04-30 02:50:19 +08:00
|
|
|
Pred = Builder.MakeNode(Dst, S, Pred, state);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 13:51:50 +08:00
|
|
|
// Did we cache out?
|
|
|
|
if (!Pred)
|
|
|
|
return;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-10 13:11:21 +08:00
|
|
|
// Update the autorelease counts.
|
|
|
|
static unsigned autoreleasetag = 0;
|
|
|
|
GenericNodeBuilder Bd(Builder, S, &autoreleasetag);
|
|
|
|
bool stop = false;
|
|
|
|
llvm::tie(Pred, state) = HandleAutoreleaseCounts(state , Bd, Pred, Eng, Sym,
|
|
|
|
X, stop);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-10 13:11:21 +08:00
|
|
|
// Did we cache out?
|
|
|
|
if (!Pred || stop)
|
|
|
|
return;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-10 13:11:21 +08:00
|
|
|
// Get the updated binding.
|
2009-06-18 09:23:53 +08:00
|
|
|
T = state->get<RefBindings>(Sym);
|
2009-05-10 13:11:21 +08:00
|
|
|
assert(T);
|
|
|
|
X = *T;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Any leaks or other errors?
|
|
|
|
if (X.isReturnedOwned() && X.getCount() == 0) {
|
2009-09-10 13:44:00 +08:00
|
|
|
Decl const *CD = &Pred->getCodeDecl();
|
2009-09-09 23:08:12 +08:00
|
|
|
if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) {
|
2009-05-04 12:57:00 +08:00
|
|
|
const RetainSummary &Summ = *Summaries.getMethodSummary(MD);
|
2009-05-10 14:25:57 +08:00
|
|
|
RetEffect RE = Summ.getRetEffect();
|
|
|
|
bool hasError = false;
|
|
|
|
|
2009-05-16 09:38:01 +08:00
|
|
|
if (RE.getKind() != RetEffect::NoRet) {
|
|
|
|
if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) {
|
|
|
|
// Things are more complicated with garbage collection. If the
|
|
|
|
// returned object is suppose to be an Objective-C object, we have
|
|
|
|
// a leak (as the caller expects a GC'ed object) because no
|
|
|
|
// method should return ownership unless it returns a CF object.
|
|
|
|
hasError = true;
|
2009-10-15 07:58:34 +08:00
|
|
|
X = X ^ RefVal::ErrorGCLeakReturned;
|
2009-05-16 09:38:01 +08:00
|
|
|
}
|
|
|
|
else if (!RE.isOwned()) {
|
|
|
|
// Either we are using GC and the returned object is a CF type
|
|
|
|
// or we aren't using GC. In either case, we expect that the
|
2009-09-09 23:08:12 +08:00
|
|
|
// enclosing method is expected to return ownership.
|
2009-05-16 09:38:01 +08:00
|
|
|
hasError = true;
|
|
|
|
X = X ^ RefVal::ErrorLeakReturned;
|
|
|
|
}
|
2009-05-10 14:25:57 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
if (hasError) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// Generate an error node.
|
2009-05-10 14:25:57 +08:00
|
|
|
static int ReturnOwnLeakTag = 0;
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->set<RefBindings>(Sym, X);
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode *N =
|
2009-08-15 11:17:38 +08:00
|
|
|
Builder.generateNode(PostStmt(S, Pred->getLocationContext(),
|
|
|
|
&ReturnOwnLeakTag), state, Pred);
|
2009-05-10 14:25:57 +08:00
|
|
|
if (N) {
|
|
|
|
CFRefReport *report =
|
2009-04-30 13:51:50 +08:00
|
|
|
new CFRefLeakReport(*static_cast<CFRefBug*>(leakAtReturn), *this,
|
|
|
|
N, Sym, Eng);
|
|
|
|
BR->EmitReport(report);
|
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-05-10 14:25:57 +08:00
|
|
|
}
|
|
|
|
else if (X.isReturnedNotOwned()) {
|
2009-09-10 13:44:00 +08:00
|
|
|
Decl const *CD = &Pred->getCodeDecl();
|
2009-05-10 14:25:57 +08:00
|
|
|
if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) {
|
|
|
|
const RetainSummary &Summ = *Summaries.getMethodSummary(MD);
|
|
|
|
if (Summ.getRetEffect().isOwned()) {
|
|
|
|
// Trying to return a not owned object to a caller expecting an
|
|
|
|
// owned object.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-10 14:25:57 +08:00
|
|
|
static int ReturnNotOwnedForOwnedTag = 0;
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned);
|
2009-08-06 09:32:16 +08:00
|
|
|
if (ExplodedNode *N =
|
2009-08-15 11:17:38 +08:00
|
|
|
Builder.generateNode(PostStmt(S, Pred->getLocationContext(),
|
|
|
|
&ReturnNotOwnedForOwnedTag),
|
|
|
|
state, Pred)) {
|
2009-05-10 14:25:57 +08:00
|
|
|
CFRefReport *report =
|
|
|
|
new CFRefReport(*static_cast<CFRefBug*>(returnNotOwnedForOwned),
|
|
|
|
*this, N, Sym);
|
|
|
|
BR->EmitReport(report);
|
|
|
|
}
|
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-04-07 08:12:43 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-04-07 08:12:43 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Assumptions.
|
|
|
|
|
2009-06-19 06:57:13 +08:00
|
|
|
const GRState* CFRefCount::EvalAssume(const GRState *state,
|
|
|
|
SVal Cond, bool Assumption) {
|
2009-04-30 02:50:19 +08:00
|
|
|
|
|
|
|
// 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
|
2009-09-09 23:08:12 +08:00
|
|
|
// too bad since the number of symbols we will track in practice are
|
2009-04-30 02:50:19 +08:00
|
|
|
// probably small and EvalAssume is only called at branches and a few
|
|
|
|
// other places.
|
2009-06-18 09:23:53 +08:00
|
|
|
RefBindings B = state->get<RefBindings>();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (B.isEmpty())
|
2009-06-18 09:23:53 +08:00
|
|
|
return state;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
bool changed = false;
|
2009-06-18 09:23:53 +08:00
|
|
|
RefBindings::Factory& RefBFactory = state->get_context<RefBindings>();
|
2009-04-30 02:50:19 +08:00
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// Check if the symbol is null (or equal to any constant).
|
|
|
|
// If this is the case, stop tracking the symbol.
|
2009-06-19 06:57:13 +08:00
|
|
|
if (state->getSymVal(I.getKey())) {
|
2009-04-30 02:50:19 +08:00
|
|
|
changed = true;
|
|
|
|
B = RefBFactory.Remove(B, I.getKey());
|
|
|
|
}
|
2008-10-23 07:56:21 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (changed)
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->set<RefBindings>(B);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
return state;
|
2008-05-02 07:13:35 +08:00
|
|
|
}
|
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,
|
2009-04-30 02:50:19 +08:00
|
|
|
RefVal V, ArgEffect E,
|
|
|
|
RefVal::Kind& hasErr) {
|
2008-04-18 07:43:50 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// In GC mode [... release] and [... retain] do nothing.
|
|
|
|
switch (E) {
|
|
|
|
default: break;
|
|
|
|
case IncRefMsg: E = isGCEnabled() ? DoNothing : IncRef; break;
|
|
|
|
case DecRefMsg: E = isGCEnabled() ? DoNothing : DecRef; break;
|
|
|
|
case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break;
|
2009-09-09 23:08:12 +08:00
|
|
|
case NewAutoreleasePool: E = isGCEnabled() ? DoNothing :
|
2009-04-30 02:50:19 +08:00
|
|
|
NewAutoreleasePool; break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Handle all use-after-releases.
|
|
|
|
if (!isGCEnabled() && V.getKind() == RefVal::Released) {
|
|
|
|
V = V ^ RefVal::ErrorUseAfterRelease;
|
|
|
|
hasErr = V.getKind();
|
2009-06-18 09:23:53 +08:00
|
|
|
return state->set<RefBindings>(sym, V);
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
switch (E) {
|
|
|
|
default:
|
|
|
|
assert (false && "Unhandled CFRef transition.");
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case Dealloc:
|
|
|
|
// Any use of -dealloc in GC is *bad*.
|
|
|
|
if (isGCEnabled()) {
|
|
|
|
V = V ^ RefVal::ErrorDeallocGC;
|
|
|
|
hasErr = V.getKind();
|
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
assert(false && "Invalid case.");
|
|
|
|
case RefVal::Owned:
|
|
|
|
// The object immediately transitions to the released state.
|
|
|
|
V = V ^ RefVal::Released;
|
|
|
|
V.clearCounts();
|
2009-06-18 09:23:53 +08:00
|
|
|
return state->set<RefBindings>(sym, V);
|
2009-04-30 02:50:19 +08:00
|
|
|
case RefVal::NotOwned:
|
|
|
|
V = V ^ RefVal::ErrorDeallocNotOwned;
|
|
|
|
hasErr = V.getKind();
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-02-08 06:19:59 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case NewAutoreleasePool:
|
|
|
|
assert(!isGCEnabled());
|
2009-06-18 09:23:53 +08:00
|
|
|
return state->add<AutoreleaseStack>(sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case MayEscape:
|
|
|
|
if (V.getKind() == RefVal::Owned) {
|
|
|
|
V = V ^ RefVal::NotOwned;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall-through.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case DoNothingByRef:
|
|
|
|
case DoNothing:
|
|
|
|
return state;
|
|
|
|
|
|
|
|
case Autorelease:
|
|
|
|
if (isGCEnabled())
|
|
|
|
return state;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Update the autorelease counts.
|
|
|
|
state = SendAutorelease(state, ARCountFactory, sym);
|
2009-05-09 04:01:42 +08:00
|
|
|
V = V.autorelease();
|
2009-05-09 09:50:57 +08:00
|
|
|
break;
|
2009-05-09 08:10:05 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case StopTracking:
|
2009-06-18 09:23:53 +08:00
|
|
|
return state->remove<RefBindings>(sym);
|
2009-04-30 02:50:19 +08:00
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
case IncRef:
|
2009-04-30 02:50:19 +08:00
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
|
|
|
|
case RefVal::Owned:
|
|
|
|
case RefVal::NotOwned:
|
|
|
|
V = V + 1;
|
2009-09-09 23:08:12 +08:00
|
|
|
break;
|
2009-04-30 02:50:19 +08:00
|
|
|
case RefVal::Released:
|
|
|
|
// Non-GC cases are handled above.
|
|
|
|
assert(isGCEnabled());
|
|
|
|
V = (V ^ RefVal::Owned) + 1;
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case SelfOwn:
|
|
|
|
V = V ^ RefVal::NotOwned;
|
|
|
|
// Fall-through.
|
|
|
|
case DecRef:
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
// case 'RefVal::Released' handled above.
|
|
|
|
assert (false);
|
|
|
|
|
|
|
|
case RefVal::Owned:
|
|
|
|
assert(V.getCount() > 0);
|
|
|
|
if (V.getCount() == 1) V = V ^ RefVal::Released;
|
|
|
|
V = V - 1;
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RefVal::NotOwned:
|
|
|
|
if (V.getCount() > 0)
|
|
|
|
V = V - 1;
|
|
|
|
else {
|
|
|
|
V = V ^ RefVal::ErrorReleaseNotOwned;
|
|
|
|
hasErr = V.getKind();
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RefVal::Released:
|
|
|
|
// Non-GC cases are handled above.
|
|
|
|
assert(isGCEnabled());
|
|
|
|
V = V ^ RefVal::ErrorUseAfterRelease;
|
|
|
|
hasErr = V.getKind();
|
2009-09-09 23:08:12 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-06-18 09:23:53 +08:00
|
|
|
return state->set<RefBindings>(sym, V);
|
2008-05-17 02:33:44 +08:00
|
|
|
}
|
|
|
|
|
2009-02-05 14:50:21 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Handle dead symbols and end-of-path.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
std::pair<ExplodedNode*, const GRState *>
|
2009-06-18 09:23:53 +08:00
|
|
|
CFRefCount::HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred,
|
2009-05-09 08:10:05 +08:00
|
|
|
GRExprEngine &Eng,
|
|
|
|
SymbolRef Sym, RefVal V, bool &stop) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 08:10:05 +08:00
|
|
|
unsigned ACnt = V.getAutoreleaseCount();
|
|
|
|
stop = false;
|
|
|
|
|
|
|
|
// No autorelease counts? Nothing to be done.
|
|
|
|
if (!ACnt)
|
|
|
|
return std::make_pair(Pred, state);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
assert(!isGCEnabled() && "Autorelease counts in GC mode?");
|
2009-05-09 08:10:05 +08:00
|
|
|
unsigned Cnt = V.getCount();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-11 23:26:06 +08:00
|
|
|
// FIXME: Handle sending 'autorelease' to already released object.
|
|
|
|
|
|
|
|
if (V.getKind() == RefVal::ReturnedOwned)
|
|
|
|
++Cnt;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 08:10:05 +08:00
|
|
|
if (ACnt <= Cnt) {
|
2009-05-09 08:44:07 +08:00
|
|
|
if (ACnt == Cnt) {
|
|
|
|
V.clearCounts();
|
2009-05-11 23:26:06 +08:00
|
|
|
if (V.getKind() == RefVal::ReturnedOwned)
|
|
|
|
V = V ^ RefVal::ReturnedNotOwned;
|
|
|
|
else
|
|
|
|
V = V ^ RefVal::NotOwned;
|
2009-05-09 08:44:07 +08:00
|
|
|
}
|
2009-05-11 23:26:06 +08:00
|
|
|
else {
|
2009-05-09 08:44:07 +08:00
|
|
|
V.setCount(Cnt - ACnt);
|
|
|
|
V.setAutoreleaseCount(0);
|
|
|
|
}
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->set<RefBindings>(Sym, V);
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode *N = Bd.MakeNode(state, Pred);
|
2009-05-09 08:10:05 +08:00
|
|
|
stop = (N == 0);
|
|
|
|
return std::make_pair(N, state);
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-05-09 08:10:05 +08:00
|
|
|
|
|
|
|
// Woah! More autorelease counts then retain counts left.
|
|
|
|
// Emit hard error.
|
|
|
|
stop = true;
|
|
|
|
V = V ^ RefVal::ErrorOverAutorelease;
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->set<RefBindings>(Sym, V);
|
2009-05-09 08:10:05 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
if (ExplodedNode *N = Bd.MakeNode(state, Pred)) {
|
2009-05-09 08:44:07 +08:00
|
|
|
N->markAsSink();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-10 13:11:21 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-05-15 14:02:08 +08:00
|
|
|
os << "Object over-autoreleased: object was sent -autorelease";
|
2009-05-10 13:11:21 +08:00
|
|
|
if (V.getAutoreleaseCount() > 1)
|
|
|
|
os << V.getAutoreleaseCount() << " times";
|
|
|
|
os << " but the object has ";
|
|
|
|
if (V.getCount() == 0)
|
|
|
|
os << "zero (locally visible)";
|
|
|
|
else
|
|
|
|
os << "+" << V.getCount();
|
|
|
|
os << " retain counts";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 08:10:05 +08:00
|
|
|
CFRefReport *report =
|
|
|
|
new CFRefReport(*static_cast<CFRefBug*>(overAutorelease),
|
2009-11-30 02:27:55 +08:00
|
|
|
*this, N, Sym, os.str());
|
2009-05-09 08:10:05 +08:00
|
|
|
BR->EmitReport(report);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
return std::make_pair((ExplodedNode*)0, state);
|
2009-05-09 07:32:51 +08:00
|
|
|
}
|
2009-05-09 07:09:42 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
const GRState *
|
|
|
|
CFRefCount::HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V,
|
2009-05-09 07:09:42 +08:00
|
|
|
llvm::SmallVectorImpl<SymbolRef> &Leaked) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
bool hasLeak = V.isOwned() ||
|
2009-05-09 07:09:42 +08:00
|
|
|
((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 07:09:42 +08:00
|
|
|
if (!hasLeak)
|
2009-06-18 09:23:53 +08:00
|
|
|
return state->remove<RefBindings>(sid);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 07:09:42 +08:00
|
|
|
Leaked.push_back(sid);
|
2009-06-18 09:23:53 +08:00
|
|
|
return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak);
|
2009-05-09 07:09:42 +08:00
|
|
|
}
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode*
|
2009-06-18 09:23:53 +08:00
|
|
|
CFRefCount::ProcessLeaks(const GRState * state,
|
2009-05-09 07:09:42 +08:00
|
|
|
llvm::SmallVectorImpl<SymbolRef> &Leaked,
|
|
|
|
GenericNodeBuilder &Builder,
|
|
|
|
GRExprEngine& Eng,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode *Pred) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-05 14:50:21 +08:00
|
|
|
if (Leaked.empty())
|
2009-05-09 07:09:42 +08:00
|
|
|
return Pred;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 07:32:51 +08:00
|
|
|
// Generate an intermediate node representing the leak point.
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode *N = Builder.MakeNode(state, Pred);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 07:09:42 +08:00
|
|
|
if (N) {
|
|
|
|
for (llvm::SmallVectorImpl<SymbolRef>::iterator
|
|
|
|
I = Leaked.begin(), E = Leaked.end(); I != E; ++I) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
CFRefBug *BT = static_cast<CFRefBug*>(Pred ? leakWithinFunction
|
2009-05-09 07:09:42 +08:00
|
|
|
: leakAtReturn);
|
|
|
|
assert(BT && "BugType not initialized.");
|
|
|
|
CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, *I, Eng);
|
|
|
|
BR->EmitReport(report);
|
|
|
|
}
|
2009-02-05 14:50:21 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 07:09:42 +08:00
|
|
|
return N;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFRefCount::EvalEndPath(GRExprEngine& Eng,
|
2009-08-06 20:48:26 +08:00
|
|
|
GREndPathNodeBuilder& Builder) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
const GRState *state = Builder.getState();
|
2009-05-09 07:32:51 +08:00
|
|
|
GenericNodeBuilder Bd(Builder);
|
2009-09-09 23:08:12 +08:00
|
|
|
RefBindings B = state->get<RefBindings>();
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode *Pred = 0;
|
2009-05-09 07:32:51 +08:00
|
|
|
|
|
|
|
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
|
2009-05-09 08:10:05 +08:00
|
|
|
bool stop = false;
|
|
|
|
llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng,
|
|
|
|
(*I).first,
|
2009-09-09 23:08:12 +08:00
|
|
|
(*I).second, stop);
|
2009-05-09 08:10:05 +08:00
|
|
|
|
|
|
|
if (stop)
|
|
|
|
return;
|
2009-05-09 07:32:51 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
B = state->get<RefBindings>();
|
|
|
|
llvm::SmallVector<SymbolRef, 10> Leaked;
|
|
|
|
|
2009-05-09 07:09:42 +08:00
|
|
|
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I)
|
|
|
|
state = HandleSymbolDeath(state, (*I).first, (*I).second, Leaked);
|
|
|
|
|
2009-05-09 07:32:51 +08:00
|
|
|
ProcessLeaks(state, Leaked, Bd, Eng, Pred);
|
2009-02-05 14:50:21 +08:00
|
|
|
}
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
void CFRefCount::EvalDeadSymbols(ExplodedNodeSet& Dst,
|
2009-02-05 14:50:21 +08:00
|
|
|
GRExprEngine& Eng,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred,
|
2009-02-05 14:50:21 +08:00
|
|
|
Stmt* S,
|
2009-06-18 09:23:53 +08:00
|
|
|
const GRState* state,
|
2009-02-05 14:50:21 +08:00
|
|
|
SymbolReaper& SymReaper) {
|
2009-05-09 07:09:42 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
RefBindings B = state->get<RefBindings>();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 07:32:51 +08:00
|
|
|
// Update counts from autorelease pools
|
|
|
|
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
|
|
|
|
E = SymReaper.dead_end(); I != E; ++I) {
|
|
|
|
SymbolRef Sym = *I;
|
|
|
|
if (const RefVal* T = B.lookup(Sym)){
|
|
|
|
// Use the symbol as the tag.
|
|
|
|
// FIXME: This might not be as unique as we would like.
|
|
|
|
GenericNodeBuilder Bd(Builder, S, Sym);
|
2009-05-09 08:10:05 +08:00
|
|
|
bool stop = false;
|
|
|
|
llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng,
|
|
|
|
Sym, *T, stop);
|
|
|
|
if (stop)
|
|
|
|
return;
|
2009-05-09 07:32:51 +08:00
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
B = state->get<RefBindings>();
|
2009-05-09 07:09:42 +08:00
|
|
|
llvm::SmallVector<SymbolRef, 10> Leaked;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-05 14:50:21 +08:00
|
|
|
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
|
2009-09-09 23:08:12 +08:00
|
|
|
E = SymReaper.dead_end(); I != E; ++I) {
|
2009-05-09 07:09:42 +08:00
|
|
|
if (const RefVal* T = B.lookup(*I))
|
|
|
|
state = HandleSymbolDeath(state, *I, *T, Leaked);
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
|
|
|
|
2009-05-09 07:09:42 +08:00
|
|
|
static unsigned LeakPPTag = 0;
|
2009-05-09 07:32:51 +08:00
|
|
|
{
|
|
|
|
GenericNodeBuilder Bd(Builder, S, &LeakPPTag);
|
|
|
|
Pred = ProcessLeaks(state, Leaked, Bd, Eng, Pred);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 07:09:42 +08:00
|
|
|
// Did we cache out?
|
|
|
|
if (!Pred)
|
|
|
|
return;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-20 07:47:02 +08:00
|
|
|
// Now generate a new node that nukes the old bindings.
|
2009-06-18 09:23:53 +08:00
|
|
|
RefBindings::Factory& F = state->get_context<RefBindings>();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-20 07:47:02 +08:00
|
|
|
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
|
2009-05-09 07:09:42 +08:00
|
|
|
E = SymReaper.dead_end(); I!=E; ++I) B = F.Remove(B, *I);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->set<RefBindings>(B);
|
2009-02-20 07:47:02 +08:00
|
|
|
Builder.MakeNode(Dst, S, Pred, state);
|
2009-02-05 14:50:21 +08:00
|
|
|
}
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst,
|
2009-08-06 20:48:26 +08:00
|
|
|
GRStmtNodeBuilder& Builder,
|
2009-08-06 09:32:16 +08:00
|
|
|
Expr* NodeExpr, Expr* ErrorExpr,
|
|
|
|
ExplodedNode* Pred,
|
2009-02-05 14:50:21 +08:00
|
|
|
const GRState* St,
|
|
|
|
RefVal::Kind hasErr, SymbolRef Sym) {
|
|
|
|
Builder.BuildSinks = true;
|
2009-08-06 20:48:26 +08:00
|
|
|
ExplodedNode *N = Builder.MakeNode(Dst, NodeExpr, Pred, St);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 09:50:57 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-05 14:50:21 +08:00
|
|
|
CFRefBug *BT = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-18 03:42:23 +08:00
|
|
|
switch (hasErr) {
|
|
|
|
default:
|
|
|
|
assert(false && "Unhandled error.");
|
|
|
|
return;
|
|
|
|
case RefVal::ErrorUseAfterRelease:
|
|
|
|
BT = static_cast<CFRefBug*>(useAfterRelease);
|
2009-09-09 23:08:12 +08:00
|
|
|
break;
|
2009-03-18 03:42:23 +08:00
|
|
|
case RefVal::ErrorReleaseNotOwned:
|
|
|
|
BT = static_cast<CFRefBug*>(releaseNotOwned);
|
|
|
|
break;
|
|
|
|
case RefVal::ErrorDeallocGC:
|
|
|
|
BT = static_cast<CFRefBug*>(deallocGC);
|
|
|
|
break;
|
|
|
|
case RefVal::ErrorDeallocNotOwned:
|
|
|
|
BT = static_cast<CFRefBug*>(deallocNotOwned);
|
|
|
|
break;
|
2009-02-05 14:50:21 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-18 11:48:14 +08:00
|
|
|
CFRefReport *report = new CFRefReport(*BT, *this, N, Sym);
|
2009-02-05 14:50:21 +08:00
|
|
|
report->addRange(ErrorExpr->getSourceRange());
|
|
|
|
BR->EmitReport(report);
|
|
|
|
}
|
|
|
|
|
2009-11-26 06:17:44 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Pieces of the retain/release checker implemented using a CheckerVisitor.
|
|
|
|
// More pieces of the retain/release checker will be migrated to this interface
|
|
|
|
// (ideally, all of it some day).
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class RetainReleaseChecker
|
2009-11-26 06:17:44 +08:00
|
|
|
: public CheckerVisitor<RetainReleaseChecker> {
|
|
|
|
CFRefCount *TF;
|
|
|
|
public:
|
|
|
|
RetainReleaseChecker(CFRefCount *tf) : TF(tf) {}
|
2009-11-26 10:38:19 +08:00
|
|
|
static void* getTag() { static int x = 0; return &x; }
|
|
|
|
|
|
|
|
void PostVisitBlockExpr(CheckerContext &C, const BlockExpr *BE);
|
2009-11-26 06:17:44 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2009-11-26 10:38:19 +08:00
|
|
|
|
|
|
|
void RetainReleaseChecker::PostVisitBlockExpr(CheckerContext &C,
|
|
|
|
const BlockExpr *BE) {
|
|
|
|
|
|
|
|
// Scan the BlockDecRefExprs for any object the retain/release checker
|
|
|
|
// may be tracking.
|
|
|
|
if (!BE->hasBlockDeclRefExprs())
|
|
|
|
return;
|
|
|
|
|
|
|
|
const GRState *state = C.getState();
|
|
|
|
const BlockDataRegion *R =
|
2010-02-09 00:18:51 +08:00
|
|
|
cast<BlockDataRegion>(state->getSVal(BE).getAsRegion());
|
2009-11-26 10:38:19 +08:00
|
|
|
|
|
|
|
BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
|
|
|
|
E = R->referenced_vars_end();
|
|
|
|
|
|
|
|
if (I == E)
|
|
|
|
return;
|
|
|
|
|
2009-12-08 06:05:27 +08:00
|
|
|
// FIXME: For now we invalidate the tracking of all symbols passed to blocks
|
|
|
|
// via captured variables, even though captured variables result in a copy
|
|
|
|
// and in implicit increment/decrement of a retain count.
|
|
|
|
llvm::SmallVector<const MemRegion*, 10> Regions;
|
|
|
|
const LocationContext *LC = C.getPredecessor()->getLocationContext();
|
|
|
|
MemRegionManager &MemMgr = C.getValueManager().getRegionManager();
|
|
|
|
|
|
|
|
for ( ; I != E; ++I) {
|
|
|
|
const VarRegion *VR = *I;
|
|
|
|
if (VR->getSuperRegion() == R) {
|
|
|
|
VR = MemMgr.getVarRegion(VR->getDecl(), LC);
|
|
|
|
}
|
|
|
|
Regions.push_back(VR);
|
|
|
|
}
|
|
|
|
|
|
|
|
state =
|
|
|
|
state->scanReachableSymbols<StopTrackingCallback>(Regions.data(),
|
|
|
|
Regions.data() + Regions.size()).getState();
|
2009-11-26 10:38:19 +08:00
|
|
|
C.addTransition(state);
|
|
|
|
}
|
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-04-11 06:16:52 +08:00
|
|
|
// Transfer function creation for external clients.
|
2008-03-11 14:39:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
void CFRefCount::RegisterChecks(GRExprEngine& Eng) {
|
|
|
|
BugReporter &BR = Eng.getBugReporter();
|
|
|
|
|
|
|
|
useAfterRelease = new UseAfterRelease(this);
|
|
|
|
BR.Register(useAfterRelease);
|
|
|
|
|
|
|
|
releaseNotOwned = new BadRelease(this);
|
|
|
|
BR.Register(releaseNotOwned);
|
|
|
|
|
|
|
|
deallocGC = new DeallocGC(this);
|
|
|
|
BR.Register(deallocGC);
|
|
|
|
|
|
|
|
deallocNotOwned = new DeallocNotOwned(this);
|
|
|
|
BR.Register(deallocNotOwned);
|
|
|
|
|
|
|
|
overAutorelease = new OverAutorelease(this);
|
|
|
|
BR.Register(overAutorelease);
|
|
|
|
|
|
|
|
returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this);
|
|
|
|
BR.Register(returnNotOwnedForOwned);
|
|
|
|
|
|
|
|
// First register "return" leaks.
|
|
|
|
const char* name = 0;
|
|
|
|
|
|
|
|
if (isGCEnabled())
|
|
|
|
name = "Leak of returned object when using garbage collection";
|
|
|
|
else if (getLangOptions().getGCMode() == LangOptions::HybridGC)
|
|
|
|
name = "Leak of returned object when not using garbage collection (GC) in "
|
|
|
|
"dual GC/non-GC code";
|
|
|
|
else {
|
|
|
|
assert(getLangOptions().getGCMode() == LangOptions::NonGC);
|
|
|
|
name = "Leak of returned object";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Leaks should not be reported if they are post-dominated by a sink.
|
|
|
|
leakAtReturn = new LeakAtReturn(this, name);
|
|
|
|
leakAtReturn->setSuppressOnSink(true);
|
|
|
|
BR.Register(leakAtReturn);
|
|
|
|
|
|
|
|
// Second, register leaks within a function/method.
|
|
|
|
if (isGCEnabled())
|
|
|
|
name = "Leak of object when using garbage collection";
|
|
|
|
else if (getLangOptions().getGCMode() == LangOptions::HybridGC)
|
|
|
|
name = "Leak of object when not using garbage collection (GC) in "
|
|
|
|
"dual GC/non-GC code";
|
|
|
|
else {
|
|
|
|
assert(getLangOptions().getGCMode() == LangOptions::NonGC);
|
|
|
|
name = "Leak";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Leaks should not be reported if they are post-dominated by sinks.
|
|
|
|
leakWithinFunction = new LeakWithinFunction(this, name);
|
|
|
|
leakWithinFunction->setSuppressOnSink(true);
|
|
|
|
BR.Register(leakWithinFunction);
|
|
|
|
|
|
|
|
// Save the reference to the BugReporter.
|
|
|
|
this->BR = &BR;
|
2009-11-26 06:17:44 +08:00
|
|
|
|
|
|
|
// Register the RetainReleaseChecker with the GRExprEngine object.
|
|
|
|
// Functionality in CFRefCount will be migrated to RetainReleaseChecker
|
|
|
|
// over time.
|
|
|
|
Eng.registerCheck(new RetainReleaseChecker(this));
|
2009-11-26 06:08:49 +08:00
|
|
|
}
|
|
|
|
|
2008-05-01 07:47:44 +08:00
|
|
|
GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
|
|
|
|
const LangOptions& lopts) {
|
2008-07-23 00:21:24 +08:00
|
|
|
return new CFRefCount(Ctx, GCEnabled, lopts);
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|