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).
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2011-03-01 09:16:21 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
2011-03-01 09:16:03 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
2010-02-18 08:05:58 +08:00
|
|
|
#include "clang/AST/DeclObjC.h"
|
2011-03-31 01:41:19 +08:00
|
|
|
#include "clang/AST/DeclCXX.h"
|
2010-02-18 08:05:58 +08:00
|
|
|
#include "clang/AST/StmtVisitor.h"
|
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"
|
2011-02-10 09:03:03 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
|
2010-12-24 03:38:26 +08:00
|
|
|
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
|
2010-12-17 13:21:58 +08:00
|
|
|
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
|
2011-02-10 09:03:03 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
|
2008-03-11 14:39:11 +08:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/FoldingSet.h"
|
2008-10-21 23:53:15 +08:00
|
|
|
#include "llvm/ADT/ImmutableList.h"
|
2010-02-18 08:05:58 +08:00
|
|
|
#include "llvm/ADT/ImmutableMap.h"
|
2008-05-17 02:33:44 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2010-02-18 08:05:58 +08:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2008-08-13 04:41:56 +08:00
|
|
|
#include <stdarg.h>
|
2008-03-06 08:08:09 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
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
|
|
|
|
2010-05-22 05:57:00 +08:00
|
|
|
namespace {
|
|
|
|
class InstanceReceiver {
|
2011-01-25 08:03:53 +08:00
|
|
|
ObjCMessage Msg;
|
2010-05-22 05:57:00 +08:00
|
|
|
const LocationContext *LC;
|
|
|
|
public:
|
2011-01-25 08:03:53 +08:00
|
|
|
InstanceReceiver() : LC(0) { }
|
|
|
|
InstanceReceiver(const ObjCMessage &msg,
|
|
|
|
const LocationContext *lc = 0) : Msg(msg), LC(lc) {}
|
2010-05-22 05:57:00 +08:00
|
|
|
|
|
|
|
bool isValid() const {
|
2011-01-25 08:03:53 +08:00
|
|
|
return Msg.isValid() && Msg.isInstanceMessage();
|
2010-05-22 05:57:00 +08:00
|
|
|
}
|
|
|
|
operator bool() const {
|
|
|
|
return isValid();
|
|
|
|
}
|
|
|
|
|
|
|
|
SVal getSValAsScalarOrLoc(const GRState *state) {
|
|
|
|
assert(isValid());
|
|
|
|
// We have an expression for the receiver? Fetch the value
|
|
|
|
// of that expression.
|
2011-01-25 08:03:53 +08:00
|
|
|
if (const Expr *Ex = Msg.getInstanceReceiver())
|
2010-05-22 05:57:00 +08:00
|
|
|
return state->getSValAsScalarOrLoc(Ex);
|
|
|
|
|
|
|
|
// Otherwise we are sending a message to super. In this case the
|
|
|
|
// object reference is the same as 'self'.
|
|
|
|
if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl())
|
|
|
|
return state->getSVal(state->getRegion(SelfDecl, LC));
|
|
|
|
|
|
|
|
return UnknownVal();
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceRange getSourceRange() const {
|
|
|
|
assert(isValid());
|
2011-01-25 08:03:53 +08:00
|
|
|
if (const Expr *Ex = Msg.getInstanceReceiver())
|
2010-05-22 05:57:00 +08:00
|
|
|
return Ex->getSourceRange();
|
|
|
|
|
|
|
|
// Otherwise we are sending a message to super.
|
2011-01-25 08:03:53 +08:00
|
|
|
SourceLocation L = Msg.getSuperLoc();
|
2010-05-22 05:57:00 +08:00
|
|
|
assert(L.isValid());
|
|
|
|
return SourceRange(L, L);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2009-04-30 07:03:22 +08:00
|
|
|
static const ObjCMethodDecl*
|
2009-09-09 23:08:12 +08:00
|
|
|
ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) {
|
2010-07-20 10:53:15 +08:00
|
|
|
const ObjCInterfaceDecl *ID = 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 {
|
2011-01-11 18:41:37 +08:00
|
|
|
class GenericNodeBuilderRefCount {
|
2010-12-23 02:53:44 +08:00
|
|
|
StmtNodeBuilder *SNB;
|
2010-07-20 14:22:24 +08:00
|
|
|
const Stmt *S;
|
2009-05-09 07:09:42 +08:00
|
|
|
const void *tag;
|
2011-01-11 10:34:45 +08:00
|
|
|
EndOfFunctionNodeBuilder *ENB;
|
2009-05-09 07:09:42 +08:00
|
|
|
public:
|
2011-01-11 18:41:37 +08:00
|
|
|
GenericNodeBuilderRefCount(StmtNodeBuilder &snb, const 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
|
|
|
|
2011-01-11 18:41:37 +08:00
|
|
|
GenericNodeBuilderRefCount(EndOfFunctionNodeBuilder &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
|
|
|
}
|
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
|
|
|
|
};
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
private:
|
|
|
|
Kind kind;
|
|
|
|
RetEffect::ObjKind okind;
|
|
|
|
unsigned Cnt;
|
|
|
|
unsigned ACnt;
|
|
|
|
QualType T;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t)
|
|
|
|
: kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
public:
|
|
|
|
Kind getKind() const { return kind; }
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RetEffect::ObjKind getObjKind() const { return okind; }
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
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; }
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
QualType getType() const { return T; }
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
bool isOwned() const {
|
|
|
|
return getKind() == Owned;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
bool isNotOwned() const {
|
|
|
|
return getKind() == NotOwned;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
bool isReturnedOwned() const {
|
|
|
|
return getKind() == ReturnedOwned;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
bool isReturnedNotOwned() const {
|
|
|
|
return getKind() == ReturnedNotOwned;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
static RefVal makeOwned(RetEffect::ObjKind o, QualType t,
|
|
|
|
unsigned Count = 1) {
|
|
|
|
return RefVal(Owned, o, Count, 0, t);
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t,
|
|
|
|
unsigned Count = 0) {
|
|
|
|
return RefVal(NotOwned, o, Count, 0, t);
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
// Comparison, profiling, and pretty-printing.
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
bool operator==(const RefVal& X) const {
|
|
|
|
return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RefVal operator-(size_t i) const {
|
|
|
|
return RefVal(getKind(), getObjKind(), getCount() - i,
|
|
|
|
getAutoreleaseCount(), getType());
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RefVal operator+(size_t i) const {
|
|
|
|
return RefVal(getKind(), getObjKind(), getCount() + i,
|
|
|
|
getAutoreleaseCount(), getType());
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RefVal operator^(Kind k) const {
|
|
|
|
return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(),
|
|
|
|
getType());
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RefVal autorelease() const {
|
|
|
|
return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1,
|
|
|
|
getType());
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
|
|
|
ID.AddInteger((unsigned) kind);
|
|
|
|
ID.AddInteger(Cnt);
|
|
|
|
ID.AddInteger(ACnt);
|
|
|
|
ID.Add(T);
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
void print(llvm::raw_ostream& Out) const;
|
|
|
|
};
|
|
|
|
|
|
|
|
void RefVal::print(llvm::raw_ostream& Out) const {
|
|
|
|
if (!T.isNull())
|
|
|
|
Out << "Tracked Type:" << T.getAsString() << '\n';
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
switch (getKind()) {
|
|
|
|
default: assert(false);
|
|
|
|
case Owned: {
|
|
|
|
Out << "Owned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case NotOwned: {
|
|
|
|
Out << "NotOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ReturnedOwned: {
|
|
|
|
Out << "ReturnedOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ReturnedNotOwned: {
|
|
|
|
Out << "ReturnedNotOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case Released:
|
|
|
|
Out << "Released";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorDeallocGC:
|
|
|
|
Out << "-dealloc (GC)";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorDeallocNotOwned:
|
|
|
|
Out << "-dealloc (not-owned)";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorLeak:
|
|
|
|
Out << "Leaked";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorLeakReturned:
|
|
|
|
Out << "Leaked (Bad naming)";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorGCLeakReturned:
|
|
|
|
Out << "Leaked (GC-ed at return)";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorUseAfterRelease:
|
|
|
|
Out << "Use-After-Release [ERROR]";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorReleaseNotOwned:
|
|
|
|
Out << "Release of Not-Owned [ERROR]";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case RefVal::ErrorOverAutorelease:
|
|
|
|
Out << "Over autoreleased";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case RefVal::ErrorReturnedNotOwned:
|
|
|
|
Out << "Non-owned object returned instead of owned";
|
|
|
|
break;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
if (ACnt) {
|
|
|
|
Out << " [ARC +" << ACnt << ']';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} //end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// RefBindings - State used to track object reference counts.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings;
|
|
|
|
|
|
|
|
namespace clang {
|
2010-12-23 15:20:52 +08:00
|
|
|
namespace ento {
|
2009-11-13 09:54:21 +08:00
|
|
|
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
|
|
|
};
|
|
|
|
}
|
2010-12-23 02:53:20 +08:00
|
|
|
}
|
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
|
|
|
}
|
2011-01-28 02:43:03 +08:00
|
|
|
|
|
|
|
void addArg(ArgEffects::Factory &af, unsigned idx, ArgEffect e) {
|
|
|
|
Args = af.add(Args, idx, e);
|
|
|
|
}
|
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
|
|
|
|
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
|
|
|
|
2011-01-27 14:54:14 +08:00
|
|
|
|
|
|
|
/// Sets the effect on the receiver of the message.
|
|
|
|
void setReceiverEffect(ArgEffect e) { Receiver = e; }
|
|
|
|
|
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; }
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
2010-07-20 10:56:49 +08:00
|
|
|
typedef llvm::DenseMap<const FunctionDecl*, RetainSummary*>
|
2008-05-06 06:11:16 +08:00
|
|
|
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
|
|
|
|
2010-07-20 10:56:49 +08:00
|
|
|
RetainSummary* getCFSummaryCreateRule(const FunctionDecl* FD);
|
|
|
|
RetainSummary* getCFSummaryGetRule(const FunctionDecl* FD);
|
|
|
|
RetainSummary* getCFCreateGetRuleSummary(const 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:
|
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, ...) {
|
2010-11-24 08:54:37 +08:00
|
|
|
RetainSummary* Summ = getPersistentSummary(AF.getEmptyMap(),
|
2009-05-03 13:20:50 +08:00
|
|
|
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")),
|
2010-11-24 08:54:37 +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()),
|
2010-11-24 08:54:37 +08:00
|
|
|
DefaultSummary(AF.getEmptyMap() /* per-argument effects (none) */,
|
2009-05-04 12:57:00 +08:00
|
|
|
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
|
|
|
|
2010-07-20 10:56:49 +08:00
|
|
|
RetainSummary* getSummary(const FunctionDecl* FD);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-01-25 08:03:53 +08:00
|
|
|
RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
|
2009-11-13 09:54:21 +08:00
|
|
|
const GRState *state,
|
|
|
|
const LocationContext *LC);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2011-01-25 08:03:53 +08:00
|
|
|
RetainSummary* getInstanceMethodSummary(const ObjCMessage &msg,
|
2009-04-30 07:03:22 +08:00
|
|
|
const ObjCInterfaceDecl* ID) {
|
2011-01-25 08:03:53 +08:00
|
|
|
return getInstanceMethodSummary(msg.getSelector(), 0,
|
|
|
|
ID, msg.getMethodDecl(), msg.getType(Ctx));
|
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
|
|
|
|
2011-01-25 08:03:53 +08:00
|
|
|
RetainSummary *getClassMethodSummary(const ObjCMessage &msg) {
|
|
|
|
const ObjCInterfaceDecl *Class = 0;
|
|
|
|
if (!msg.isInstanceMessage())
|
|
|
|
Class = msg.getReceiverInterface();
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
|
2011-01-25 08:03:53 +08:00
|
|
|
return getClassMethodSummary(msg.getSelector(),
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
Class? Class->getIdentifier() : 0,
|
|
|
|
Class,
|
2011-01-25 08:03:53 +08:00
|
|
|
msg.getMethodDecl(), msg.getType(Ctx));
|
2009-04-29 08:42:39 +08:00
|
|
|
}
|
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;
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.getEmptyMap();
|
2009-05-03 13:20:50 +08:00
|
|
|
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-07-20 10:56:49 +08:00
|
|
|
static bool isRetain(const FunctionDecl* FD, StringRef FName) {
|
2010-02-09 02:38:55 +08:00
|
|
|
return FName.endswith("Retain");
|
2009-01-13 05:45:02 +08:00
|
|
|
}
|
|
|
|
|
2010-07-20 10:56:49 +08:00
|
|
|
static bool isRelease(const FunctionDecl* FD, StringRef FName) {
|
2010-02-09 02:38:55 +08:00
|
|
|
return FName.endswith("Release");
|
2009-01-13 05:45:02 +08:00
|
|
|
}
|
|
|
|
|
2010-07-20 10:56:49 +08:00
|
|
|
RetainSummary* RetainSummaryManager::getSummary(const 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.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 1, DecRef);
|
2010-02-09 02:38:55 +08:00
|
|
|
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.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 2, DecRef);
|
2010-02-09 02:38:55 +08:00
|
|
|
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.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 7, StopTracking);
|
2010-02-09 02:38:55 +08:00
|
|
|
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.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 8, StopTracking);
|
2010-02-09 02:38:55 +08:00
|
|
|
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.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 12, StopTracking);
|
2010-02-09 02:38:55 +08:00
|
|
|
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*
|
2010-07-20 10:56:49 +08:00
|
|
|
RetainSummaryManager::getCFCreateGetRuleSummary(const 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: {
|
2010-11-24 08:54:37 +08:00
|
|
|
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: {
|
2010-11-24 08:54:37 +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: {
|
2010-11-24 08:54:37 +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
|
|
|
}
|
|
|
|
|
2010-07-20 10:56:49 +08:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getCFSummaryCreateRule(const 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) {
|
2010-11-24 08:54:37 +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
|
|
|
}
|
|
|
|
|
2010-07-20 10:56:49 +08:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getCFSummaryGetRule(const 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;
|
|
|
|
|
2011-01-28 02:43:03 +08:00
|
|
|
// Effects on the parameters.
|
|
|
|
unsigned parm_idx = 0;
|
|
|
|
for (FunctionDecl::param_const_iterator pi = FD->param_begin(),
|
2011-04-06 17:02:12 +08:00
|
|
|
pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) {
|
2011-01-28 02:43:03 +08:00
|
|
|
const ParmVarDecl *pd = *pi;
|
|
|
|
if (pd->getAttr<NSConsumedAttr>()) {
|
|
|
|
if (!GCEnabled)
|
|
|
|
Summ.addArg(AF, parm_idx, DecRef);
|
|
|
|
}
|
|
|
|
else if(pd->getAttr<CFConsumedAttr>()) {
|
|
|
|
Summ.addArg(AF, parm_idx, DecRef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2010-02-18 08:06:12 +08:00
|
|
|
else if (FD->getAttr<NSReturnsNotRetainedAttr>()) {
|
|
|
|
Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC));
|
|
|
|
}
|
|
|
|
else if (FD->getAttr<CFReturnsNotRetainedAttr>()) {
|
|
|
|
Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
|
|
|
|
}
|
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
|
|
|
|
2011-01-27 14:54:14 +08:00
|
|
|
// Effects on the receiver.
|
|
|
|
if (MD->getAttr<NSConsumesSelfAttr>()) {
|
2011-01-28 02:43:03 +08:00
|
|
|
if (!GCEnabled)
|
|
|
|
Summ.setReceiverEffect(DecRefMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Effects on the parameters.
|
|
|
|
unsigned parm_idx = 0;
|
|
|
|
for (ObjCMethodDecl::param_iterator pi=MD->param_begin(), pe=MD->param_end();
|
|
|
|
pi != pe; ++pi, ++parm_idx) {
|
|
|
|
const ParmVarDecl *pd = *pi;
|
|
|
|
if (pd->getAttr<NSConsumedAttr>()) {
|
|
|
|
if (!GCEnabled)
|
|
|
|
Summ.addArg(AF, parm_idx, DecRef);
|
|
|
|
}
|
|
|
|
else if(pd->getAttr<CFConsumedAttr>()) {
|
|
|
|
Summ.addArg(AF, parm_idx, DecRef);
|
|
|
|
}
|
2011-01-27 14:54:14 +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
|
|
|
}
|
2010-02-18 08:06:12 +08:00
|
|
|
if (MD->getAttr<NSReturnsNotRetainedAttr>()) {
|
|
|
|
Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC));
|
|
|
|
return;
|
|
|
|
}
|
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
|
|
|
|
2010-02-18 08:06:12 +08:00
|
|
|
if (isTrackedLoc) {
|
|
|
|
if (MD->getAttr<CFReturnsRetainedAttr>())
|
|
|
|
Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
|
|
|
|
else if (MD->getAttr<CFReturnsNotRetainedAttr>())
|
|
|
|
Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
|
|
|
|
}
|
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)
|
2010-11-24 08:54:37 +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)) {
|
2010-12-02 06:16:56 +08:00
|
|
|
// EXPERIMENTAL: assume the Cocoa conventions for all objects returned
|
2009-05-03 14:08:32 +08:00
|
|
|
// 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*
|
2011-01-25 08:03:53 +08:00
|
|
|
RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg,
|
2009-11-13 09:54:21 +08:00
|
|
|
const GRState *state,
|
|
|
|
const LocationContext *LC) {
|
|
|
|
|
|
|
|
// We need the type-information of the tracked receiver object
|
|
|
|
// Retrieve it from the state.
|
2011-01-25 08:03:53 +08:00
|
|
|
const Expr *Receiver = msg.getInstanceReceiver();
|
2009-11-13 09:54:21 +08:00
|
|
|
const ObjCInterfaceDecl* ID = 0;
|
|
|
|
|
|
|
|
// FIXME: Is this really working as expected? There are cases where
|
|
|
|
// we just use the 'ID' from the message expression.
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
SVal receiverV;
|
|
|
|
|
2010-05-22 05:56:53 +08:00
|
|
|
if (Receiver) {
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
receiverV = state->getSValAsScalarOrLoc(Receiver);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +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))
|
2010-07-02 04:16:50 +08:00
|
|
|
if (const ObjCObjectPointerType* PT =
|
2009-11-13 09:54:21 +08:00
|
|
|
T->getType()->getAs<ObjCObjectPointerType>())
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
ID = PT->getInterfaceDecl();
|
2010-07-02 04:16:50 +08:00
|
|
|
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// FIXME: Hack for 'super'.
|
2011-01-25 08:03:53 +08:00
|
|
|
ID = msg.getReceiverInterface();
|
2009-11-13 09:54:21 +08:00
|
|
|
}
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
// FIXME: The receiver could be a reference to a class, meaning that
|
|
|
|
// we should use the class method.
|
2011-01-25 08:03:53 +08:00
|
|
|
RetainSummary *Summ = getInstanceMethodSummary(msg, ID);
|
2009-11-13 09:54:21 +08:00
|
|
|
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
|
|
|
|
|
|
|
// 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.
|
2010-11-24 08:54:37 +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-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);
|
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
|
|
|
|
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 {
|
2010-12-23 15:20:52 +08:00
|
|
|
namespace ento {
|
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
|
|
|
};
|
2010-12-23 02:53:20 +08:00
|
|
|
} // end GR namespace
|
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);
|
2010-11-24 08:54:37 +08:00
|
|
|
newCnts = F.add(*cnts, sym, cnt ? *cnt + 1 : 1);
|
2009-03-21 01:34:15 +08:00
|
|
|
}
|
|
|
|
else
|
2010-11-24 08:54:37 +08:00
|
|
|
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
|
|
|
|
2010-12-23 02:53:44 +08:00
|
|
|
class CFRefCount : public TransferFuncs {
|
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,
|
2010-12-23 02:53:44 +08:00
|
|
|
StmtNodeBuilder& Builder,
|
2010-07-20 14:22:24 +08:00
|
|
|
const Expr* NodeExpr, SourceRange ErrorRange,
|
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,
|
2011-01-11 18:41:37 +08:00
|
|
|
GenericNodeBuilderRefCount &Builder,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine &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
|
|
|
|
2010-12-23 02:53:44 +08:00
|
|
|
void RegisterChecks(ExprEngine &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
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
void evalSummary(ExplodedNodeSet& Dst,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine& Eng,
|
|
|
|
StmtNodeBuilder& Builder,
|
2010-07-20 14:22:24 +08:00
|
|
|
const Expr* Ex,
|
2011-01-25 08:03:53 +08:00
|
|
|
const CallOrObjCMessage &callOrMsg,
|
2010-05-22 05:57:00 +08:00
|
|
|
InstanceReceiver Receiver,
|
2009-05-04 12:57:00 +08:00
|
|
|
const RetainSummary& Summ,
|
2009-12-03 16:25:47 +08:00
|
|
|
const MemRegion *Callee,
|
2009-12-02 13:49:12 +08:00
|
|
|
ExplodedNode* Pred, const GRState *state);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
virtual void evalCall(ExplodedNodeSet& Dst,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine& Eng,
|
|
|
|
StmtNodeBuilder& Builder,
|
2010-07-20 14:22:24 +08:00
|
|
|
const CallExpr* CE, SVal L,
|
2009-09-09 23:08:12 +08:00
|
|
|
ExplodedNode* Pred);
|
|
|
|
|
|
|
|
|
2011-01-25 08:03:53 +08:00
|
|
|
virtual void evalObjCMessage(ExplodedNodeSet& Dst,
|
|
|
|
ExprEngine& Engine,
|
|
|
|
StmtNodeBuilder& Builder,
|
|
|
|
ObjCMessage msg,
|
|
|
|
ExplodedNode* Pred,
|
|
|
|
const GRState *state);
|
2009-09-09 23:08:12 +08:00
|
|
|
// Stores.
|
2010-12-23 02:53:44 +08:00
|
|
|
virtual void evalBind(StmtNodeBuilderRef& B, SVal location, SVal val);
|
2009-02-14 09:43:44 +08:00
|
|
|
|
2008-04-12 06:25:11 +08:00
|
|
|
// End-of-path.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-12-23 02:53:44 +08:00
|
|
|
virtual void evalEndPath(ExprEngine& Engine,
|
2011-01-11 10:34:45 +08:00
|
|
|
EndOfFunctionNodeBuilder& Builder);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
virtual void evalDeadSymbols(ExplodedNodeSet& Dst,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine& Engine,
|
|
|
|
StmtNodeBuilder& Builder,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred,
|
2010-07-02 04:09:55 +08:00
|
|
|
const GRState* state,
|
2009-01-22 06:26:05 +08:00
|
|
|
SymbolReaper& SymReaper);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
std::pair<ExplodedNode*, const GRState *>
|
2011-01-11 18:41:37 +08:00
|
|
|
HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilderRefCount Bd,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExplodedNode* Pred, ExprEngine &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
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
virtual void evalReturn(ExplodedNodeSet& Dst,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine& Engine,
|
|
|
|
StmtNodeBuilder& Builder,
|
2010-07-20 14:22:24 +08:00
|
|
|
const ReturnStmt* S,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred);
|
2008-04-19 03:23:43 +08:00
|
|
|
|
|
|
|
// Assumptions.
|
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
virtual const GRState *evalAssume(const GRState* state, SVal condition,
|
2009-06-19 06:57:13 +08:00
|
|
|
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; }
|
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
|
|
|
|
2010-12-04 09:12:15 +08:00
|
|
|
CFRefBug& getBugType() const {
|
2009-04-30 02:50:19 +08:00
|
|
|
return (CFRefBug&) RangedBugReport::getBugType();
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-12-04 09:12:15 +08:00
|
|
|
virtual std::pair<ranges_iterator, ranges_iterator> getRanges() const {
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!getBugType().isLeak())
|
2010-12-04 09:12:15 +08:00
|
|
|
return RangedBugReport::getRanges();
|
2009-04-30 02:50:19 +08:00
|
|
|
else
|
2010-12-04 09:12:15 +08:00
|
|
|
return std::make_pair(ranges_iterator(), ranges_iterator());
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
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,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine& 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())
|
2010-04-17 17:33:03 +08:00
|
|
|
os << "Call to function '" << FD << '\'';
|
2009-04-30 02:50:19 +08:00
|
|
|
else
|
2009-09-09 23:08:12 +08:00
|
|
|
os << "function call";
|
|
|
|
}
|
2011-01-25 08:04:03 +08:00
|
|
|
else if (isa<ObjCMessageExpr>(S)) {
|
2009-04-30 02:50:19 +08:00
|
|
|
os << "Method";
|
2011-01-25 08:04:03 +08:00
|
|
|
} else {
|
|
|
|
os << "Property";
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
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)) {
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
if (const Expr *receiver = ME->getInstanceReceiver())
|
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
|
2010-10-16 06:50:23 +08:00
|
|
|
os << " is not referenced later in this execution path and has a retain "
|
|
|
|
"count of +" << 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,
|
2010-12-23 02:53:44 +08:00
|
|
|
SymbolRef sym, ExprEngine& 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.
|
2010-09-16 11:50:38 +08:00
|
|
|
GetAllocationSite(Eng.getStateManager(), getErrorNode(), 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).
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
const ObjCInterfaceDecl *D = ME->getReceiverInterface();
|
2010-05-15 19:32:37 +08:00
|
|
|
return !D ? RetTy :
|
|
|
|
Ctx.getObjCObjectPointerType(Ctx.getObjCInterfaceType(D));
|
2009-07-11 07:34:53 +08:00
|
|
|
}
|
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
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
void CFRefCount::evalSummary(ExplodedNodeSet& Dst,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine& Eng,
|
|
|
|
StmtNodeBuilder& Builder,
|
2010-07-20 14:22:24 +08:00
|
|
|
const Expr* Ex,
|
2011-01-25 08:03:53 +08:00
|
|
|
const CallOrObjCMessage &callOrMsg,
|
2010-05-22 05:57:00 +08:00
|
|
|
InstanceReceiver Receiver,
|
2009-05-04 12:57:00 +08:00
|
|
|
const RetainSummary& Summ,
|
2009-12-03 16:25:47 +08:00
|
|
|
const MemRegion *Callee,
|
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;
|
2010-05-22 05:56:53 +08:00
|
|
|
SourceRange ErrorRange;
|
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;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2010-08-03 05:59:12 +08:00
|
|
|
// HACK: Symbols that have ref-count state that are referenced directly
|
|
|
|
// (not as structure or array elements, or via bindings) by an argument
|
|
|
|
// should not have their ref-count state stripped after we have
|
|
|
|
// done an invalidation pass.
|
|
|
|
llvm::DenseSet<SymbolRef> WhitelistedSymbols;
|
|
|
|
|
2011-02-12 09:01:31 +08:00
|
|
|
// Invalidate all instance variables of the receiver of a message.
|
|
|
|
// FIXME: We should be able to do better with inter-procedural analysis.
|
|
|
|
if (Receiver) {
|
|
|
|
SVal V = Receiver.getSValAsScalarOrLoc(state);
|
|
|
|
if (SymbolRef Sym = V.getAsLocSymbol()) {
|
|
|
|
if (state->get<RefBindings>(Sym))
|
|
|
|
WhitelistedSymbols.insert(Sym);
|
|
|
|
}
|
|
|
|
if (const MemRegion *region = V.getAsRegion())
|
|
|
|
RegionsToInvalidate.push_back(region);
|
|
|
|
}
|
|
|
|
|
2011-01-25 08:03:53 +08:00
|
|
|
for (unsigned idx = 0, e = callOrMsg.getNumArgs(); idx != e; ++idx) {
|
|
|
|
SVal V = callOrMsg.getArgSValAsScalarOrLoc(idx);
|
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)) {
|
2010-08-03 05:59:12 +08:00
|
|
|
WhitelistedSymbols.insert(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) {
|
2011-01-25 08:03:53 +08:00
|
|
|
ErrorRange = callOrMsg.getArgSourceRange(idx);
|
2009-04-30 02:50:19 +08:00
|
|
|
ErrorSym = Sym;
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
|
|
|
|
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.
|
2010-06-16 08:17:44 +08:00
|
|
|
if (ER->getElementType()->isIntegralOrEnumerationType()) {
|
2009-07-06 14:01:24 +08:00
|
|
|
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
|
|
|
}
|
2010-07-02 04:16:50 +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
|
|
|
}
|
2010-07-02 04:16:50 +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);
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-12-03 11:27:11 +08:00
|
|
|
// Invalidate regions we designed for invalidation use the batch invalidation
|
|
|
|
// API.
|
2010-07-02 04:16:50 +08:00
|
|
|
|
|
|
|
// 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::InvalidatedSymbols IS;
|
|
|
|
|
|
|
|
// NOTE: Even if RegionsToInvalidate is empty, we must still invalidate
|
|
|
|
// global variables.
|
2011-02-12 03:48:15 +08:00
|
|
|
state = state->invalidateRegions(RegionsToInvalidate.data(),
|
2010-08-04 04:44:35 +08:00
|
|
|
RegionsToInvalidate.data() +
|
|
|
|
RegionsToInvalidate.size(),
|
|
|
|
Ex, Count, &IS,
|
|
|
|
/* invalidateGlobals = */ true);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
|
|
|
for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(),
|
|
|
|
E = IS.end(); I!=E; ++I) {
|
2010-08-03 05:59:12 +08:00
|
|
|
SymbolRef sym = *I;
|
|
|
|
if (WhitelistedSymbols.count(sym))
|
|
|
|
continue;
|
|
|
|
// Remove any existing reference-count binding.
|
2010-07-02 04:16:50 +08:00
|
|
|
state = state->remove<RefBindings>(*I);
|
2009-12-03 11:27:11 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Evaluate the effect on the message receiver.
|
2010-05-22 05:56:53 +08:00
|
|
|
if (!ErrorRange.isValid() && Receiver) {
|
2010-05-22 05:57:00 +08:00
|
|
|
SymbolRef Sym = Receiver.getSValAsScalarOrLoc(state).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) {
|
2010-05-22 05:57:00 +08:00
|
|
|
ErrorRange = Receiver.getSourceRange();
|
2009-04-30 02:50:19 +08:00
|
|
|
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) {
|
2010-05-22 05:56:53 +08:00
|
|
|
ProcessNonLeakError(Dst, Builder, Ex, ErrorRange, Pred, state,
|
2009-04-30 02:50:19 +08:00
|
|
|
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) {
|
|
|
|
bool found = false;
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
if (Receiver) {
|
2010-05-22 05:57:00 +08:00
|
|
|
SVal V = Receiver.getSValAsScalarOrLoc(state);
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
if (SymbolRef Sym = V.getAsLocSymbol())
|
|
|
|
if (state->get<RefBindings>(Sym)) {
|
|
|
|
found = true;
|
|
|
|
RE = Summaries.getObjAllocRetEffect();
|
|
|
|
}
|
|
|
|
} // FIXME: Otherwise, this is a send-to-super instance message.
|
2009-05-13 04:06:54 +08:00
|
|
|
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
|
|
|
|
2011-03-31 01:41:19 +08:00
|
|
|
// Use the result type from callOrMsg as it automatically adjusts
|
|
|
|
// for methods/functions that return references.
|
|
|
|
QualType resultTy = callOrMsg.getResultType(Eng.getContext());
|
|
|
|
if (Loc::isLocType(resultTy) ||
|
|
|
|
(resultTy->isIntegerType() && resultTy->isScalarType())) {
|
2009-04-30 02:50:19 +08:00
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2010-12-02 15:49:45 +08:00
|
|
|
SValBuilder &svalBuilder = Eng.getSValBuilder();
|
2011-03-31 01:41:19 +08:00
|
|
|
SVal X = svalBuilder.getConjuredSymbolVal(NULL, Ex, resultTy, 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();
|
2011-01-25 08:03:53 +08:00
|
|
|
assert (idx < callOrMsg.getNumArgs());
|
|
|
|
SVal V = callOrMsg.getArgSValAsScalarOrLoc(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: {
|
2010-05-22 05:56:53 +08:00
|
|
|
assert(Receiver);
|
2010-05-22 05:57:00 +08:00
|
|
|
SVal V = Receiver.getSValAsScalarOrLoc(state);
|
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();
|
2010-12-02 15:49:45 +08:00
|
|
|
SValBuilder &svalBuilder = Eng.getSValBuilder();
|
|
|
|
SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count);
|
2011-03-31 01:41:19 +08:00
|
|
|
|
|
|
|
// Use the result type from callOrMsg as it automatically adjusts
|
|
|
|
// for methods/functions that return references.
|
|
|
|
QualType resultTy = callOrMsg.getResultType(Eng.getContext());
|
2009-06-18 09:23:53 +08:00
|
|
|
state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(),
|
2011-03-31 01:41:19 +08:00
|
|
|
resultTy));
|
2010-12-02 15:49:45 +08:00
|
|
|
state = state->BindExpr(Ex, svalBuilder.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;
|
2010-12-02 06:16:56 +08:00
|
|
|
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();
|
2010-12-02 15:49:45 +08:00
|
|
|
SValBuilder &svalBuilder = Eng.getSValBuilder();
|
|
|
|
SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count);
|
|
|
|
QualType RetT = GetReturnType(Ex, svalBuilder.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));
|
2010-12-02 15:49:45 +08:00
|
|
|
state = state->BindExpr(Ex, svalBuilder.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
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
void CFRefCount::evalCall(ExplodedNodeSet& Dst,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine& Eng,
|
|
|
|
StmtNodeBuilder& Builder,
|
2010-07-20 14:22:24 +08:00
|
|
|
const 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;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-25 09:35:18 +08:00
|
|
|
// 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();
|
|
|
|
}
|
2011-03-31 01:41:19 +08:00
|
|
|
else if (const FunctionDecl* FD = L.getAsFunctionDecl()) {
|
|
|
|
Summ = Summaries.getSummary(FD);
|
2009-11-25 09:35:18 +08:00
|
|
|
}
|
2011-03-31 01:41:19 +08:00
|
|
|
else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
|
|
|
|
if (const CXXMethodDecl *MD = me->getMethodDecl())
|
|
|
|
Summ = Summaries.getSummary(MD);
|
|
|
|
else
|
|
|
|
Summ = Summaries.getDefaultSummary();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Summ = Summaries.getDefaultSummary();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 12:57:00 +08:00
|
|
|
assert(Summ);
|
2011-01-25 08:03:53 +08:00
|
|
|
evalSummary(Dst, Eng, Builder, CE,
|
|
|
|
CallOrObjCMessage(CE, Builder.GetState(Pred)),
|
|
|
|
InstanceReceiver(), *Summ,L.getAsRegion(),
|
|
|
|
Pred, Builder.GetState(Pred));
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
|
|
|
|
2011-01-25 08:03:53 +08:00
|
|
|
void CFRefCount::evalObjCMessage(ExplodedNodeSet& Dst,
|
|
|
|
ExprEngine& Eng,
|
|
|
|
StmtNodeBuilder& Builder,
|
|
|
|
ObjCMessage msg,
|
|
|
|
ExplodedNode* Pred,
|
|
|
|
const GRState *state) {
|
2009-11-13 09:54:21 +08:00
|
|
|
RetainSummary *Summ =
|
2011-01-25 08:03:53 +08:00
|
|
|
msg.isInstanceMessage()
|
|
|
|
? Summaries.getInstanceMethodSummary(msg, state,Pred->getLocationContext())
|
|
|
|
: Summaries.getClassMethodSummary(msg);
|
2009-04-30 02:50:19 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
assert(Summ && "RetainSummary is null");
|
2011-01-25 08:03:53 +08:00
|
|
|
evalSummary(Dst, Eng, Builder, msg.getOriginExpr(),
|
|
|
|
CallOrObjCMessage(msg, Builder.GetState(Pred)),
|
|
|
|
InstanceReceiver(msg, Pred->getLocationContext()), *Summ, NULL,
|
|
|
|
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
|
|
|
|
2010-12-23 02:53:44 +08:00
|
|
|
void CFRefCount::evalBind(StmtNodeBuilderRef& B, SVal location, SVal val) {
|
2009-09-09 23:08:12 +08:00
|
|
|
// 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.
|
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
void CFRefCount::evalReturn(ExplodedNodeSet& Dst,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine& Eng,
|
|
|
|
StmtNodeBuilder& Builder,
|
2010-07-20 14:22:24 +08:00
|
|
|
const ReturnStmt* S,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-07-20 14:22:24 +08:00
|
|
|
const Expr* RetE = S->getRetValue();
|
2009-04-30 02:50:19 +08:00
|
|
|
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;
|
2011-01-11 18:41:37 +08:00
|
|
|
GenericNodeBuilderRefCount Bd(Builder, S, &autoreleasetag);
|
2009-05-10 13:11:21 +08:00
|
|
|
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.
|
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
const GRState* CFRefCount::evalAssume(const GRState *state,
|
2009-06-19 06:57:13 +08:00
|
|
|
SVal Cond, bool Assumption) {
|
2009-04-30 02:50:19 +08:00
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
// FIXME: We may add to the interface of evalAssume the list of symbols
|
2009-04-30 02:50:19 +08:00
|
|
|
// 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
|
2010-12-02 05:57:22 +08:00
|
|
|
// probably small and evalAssume is only called at branches and a few
|
2009-04-30 02:50:19 +08:00
|
|
|
// 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;
|
2010-11-24 08:54:37 +08:00
|
|
|
B = RefBFactory.remove(B, I.getKey());
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
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 *>
|
2011-01-11 18:41:37 +08:00
|
|
|
CFRefCount::HandleAutoreleaseCounts(const GRState * state,
|
|
|
|
GenericNodeBuilderRefCount Bd,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine &Eng,
|
2009-05-09 08:10:05 +08:00
|
|
|
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,
|
2011-01-11 18:41:37 +08:00
|
|
|
GenericNodeBuilderRefCount &Builder,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine& 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;
|
|
|
|
}
|
|
|
|
|
2010-12-23 02:53:44 +08:00
|
|
|
void CFRefCount::evalEndPath(ExprEngine& Eng,
|
2011-01-11 10:34:45 +08:00
|
|
|
EndOfFunctionNodeBuilder& Builder) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
const GRState *state = Builder.getState();
|
2011-01-11 18:41:37 +08:00
|
|
|
GenericNodeBuilderRefCount 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
|
|
|
}
|
|
|
|
|
2010-12-02 05:57:22 +08:00
|
|
|
void CFRefCount::evalDeadSymbols(ExplodedNodeSet& Dst,
|
2010-12-23 02:53:44 +08:00
|
|
|
ExprEngine& Eng,
|
|
|
|
StmtNodeBuilder& Builder,
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode* Pred,
|
2009-06-18 09:23:53 +08:00
|
|
|
const GRState* state,
|
2009-02-05 14:50:21 +08:00
|
|
|
SymbolReaper& SymReaper) {
|
2010-07-20 14:22:24 +08:00
|
|
|
const Stmt *S = Builder.getStmt();
|
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.
|
2011-01-11 18:41:37 +08:00
|
|
|
GenericNodeBuilderRefCount 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
|
|
|
{
|
2011-01-11 18:41:37 +08:00
|
|
|
GenericNodeBuilderRefCount Bd(Builder, S, &LeakPPTag);
|
2009-05-09 07:32:51 +08:00
|
|
|
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(),
|
2010-11-24 08:54:37 +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,
|
2010-12-23 02:53:44 +08:00
|
|
|
StmtNodeBuilder& Builder,
|
2010-07-20 14:22:24 +08:00
|
|
|
const Expr* NodeExpr,
|
|
|
|
SourceRange ErrorRange,
|
2009-08-06 09:32:16 +08:00
|
|
|
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);
|
2010-05-22 05:56:53 +08:00
|
|
|
report->addRange(ErrorRange);
|
2009-02-05 14:50:21 +08:00
|
|
|
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
|
2011-03-01 09:16:21 +08:00
|
|
|
: public Checker< check::PostStmt<BlockExpr> > {
|
2009-11-26 06:17:44 +08:00
|
|
|
public:
|
2011-03-01 09:16:03 +08:00
|
|
|
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
|
2009-11-26 06:17:44 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2009-11-26 10:38:19 +08:00
|
|
|
|
2011-03-01 09:16:03 +08:00
|
|
|
void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE,
|
|
|
|
CheckerContext &C) const {
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 10:38:19 +08:00
|
|
|
// Scan the BlockDecRefExprs for any object the retain/release checker
|
2010-07-02 04:16:50 +08:00
|
|
|
// may be tracking.
|
2011-02-02 21:00:07 +08:00
|
|
|
if (!BE->getBlockDecl()->hasCaptures())
|
2009-11-26 10:38:19 +08:00
|
|
|
return;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 10:38:19 +08:00
|
|
|
const GRState *state = C.getState();
|
|
|
|
const BlockDataRegion *R =
|
2010-02-09 00:18:51 +08:00
|
|
|
cast<BlockDataRegion>(state->getSVal(BE).getAsRegion());
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 10:38:19 +08:00
|
|
|
BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
|
|
|
|
E = R->referenced_vars_end();
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 10:38:19 +08:00
|
|
|
if (I == E)
|
|
|
|
return;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
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();
|
2010-12-02 15:49:45 +08:00
|
|
|
MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-12-08 06:05:27 +08:00
|
|
|
for ( ; I != E; ++I) {
|
|
|
|
const VarRegion *VR = *I;
|
|
|
|
if (VR->getSuperRegion() == R) {
|
|
|
|
VR = MemMgr.getVarRegion(VR->getDecl(), LC);
|
|
|
|
}
|
|
|
|
Regions.push_back(VR);
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-12-08 06:05:27 +08:00
|
|
|
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
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2010-12-23 02:53:44 +08:00
|
|
|
void CFRefCount::RegisterChecks(ExprEngine& Eng) {
|
2009-11-26 06:08:49 +08:00
|
|
|
BugReporter &BR = Eng.getBugReporter();
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
useAfterRelease = new UseAfterRelease(this);
|
|
|
|
BR.Register(useAfterRelease);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
releaseNotOwned = new BadRelease(this);
|
|
|
|
BR.Register(releaseNotOwned);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
deallocGC = new DeallocGC(this);
|
|
|
|
BR.Register(deallocGC);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
deallocNotOwned = new DeallocNotOwned(this);
|
|
|
|
BR.Register(deallocNotOwned);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
overAutorelease = new OverAutorelease(this);
|
|
|
|
BR.Register(overAutorelease);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this);
|
|
|
|
BR.Register(returnNotOwnedForOwned);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
// First register "return" leaks.
|
|
|
|
const char* name = 0;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
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";
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
// Leaks should not be reported if they are post-dominated by a sink.
|
|
|
|
leakAtReturn = new LeakAtReturn(this, name);
|
|
|
|
leakAtReturn->setSuppressOnSink(true);
|
|
|
|
BR.Register(leakAtReturn);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
// 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";
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
// Leaks should not be reported if they are post-dominated by sinks.
|
|
|
|
leakWithinFunction = new LeakWithinFunction(this, name);
|
|
|
|
leakWithinFunction->setSuppressOnSink(true);
|
|
|
|
BR.Register(leakWithinFunction);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 06:08:49 +08:00
|
|
|
// Save the reference to the BugReporter.
|
|
|
|
this->BR = &BR;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2010-12-23 02:53:44 +08:00
|
|
|
// Register the RetainReleaseChecker with the ExprEngine object.
|
2009-11-26 06:17:44 +08:00
|
|
|
// Functionality in CFRefCount will be migrated to RetainReleaseChecker
|
|
|
|
// over time.
|
2011-03-01 09:16:03 +08:00
|
|
|
// FIXME: HACK! Remove TransferFuncs and turn all of CFRefCount into fully
|
|
|
|
// using the checker mechanism.
|
|
|
|
Eng.getCheckerManager().registerChecker<RetainReleaseChecker>();
|
2009-11-26 06:08:49 +08:00
|
|
|
}
|
|
|
|
|
2010-12-23 15:20:52 +08:00
|
|
|
TransferFuncs* ento::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
|
2008-05-01 07:47:44 +08:00
|
|
|
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
|
|
|
}
|