2008-03-16 02:59:48 +03:00
|
|
|
// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--//
|
2008-03-06 03:08:09 +03: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 13:40:09 +03:00
|
|
|
// This file defines the methods for CFRefCount, which implements
|
2008-03-06 03:08:09 +03:00
|
|
|
// a reference count checker for Core Foundation (Mac OS X).
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
#include "GRSimpleVals.h"
|
2008-05-01 03:47:44 +04:00
|
|
|
#include "clang/Basic/LangOptions.h"
|
2008-05-02 03:13:35 +04:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
2008-03-06 03:08:09 +03:00
|
|
|
#include "clang/Analysis/PathSensitive/ValueState.h"
|
2008-03-31 22:26:32 +04:00
|
|
|
#include "clang/Analysis/PathDiagnostic.h"
|
2008-03-06 03:08:09 +03:00
|
|
|
#include "clang/Analysis/LocalCheckers.h"
|
2008-04-09 05:10:13 +04:00
|
|
|
#include "clang/Analysis/PathDiagnostic.h"
|
|
|
|
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
2008-03-11 09:39:11 +03:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/FoldingSet.h"
|
|
|
|
#include "llvm/ADT/ImmutableMap.h"
|
2008-04-09 05:10:13 +04:00
|
|
|
#include "llvm/Support/Compiler.h"
|
2008-03-11 22:44:10 +03:00
|
|
|
#include <ostream>
|
2008-04-18 08:55:01 +04:00
|
|
|
#include <sstream>
|
2008-03-06 03:08:09 +03:00
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Utility functions.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-05-01 22:31:44 +04:00
|
|
|
static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) {
|
2008-04-17 22:12:53 +04:00
|
|
|
IdentifierInfo* II = &Ctx.Idents.get(name);
|
|
|
|
return Ctx.Selectors.getSelector(0, &II);
|
|
|
|
}
|
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
static inline Selector GetUnarySelector(const char* name, ASTContext& Ctx) {
|
|
|
|
IdentifierInfo* II = &Ctx.Idents.get(name);
|
|
|
|
return Ctx.Selectors.getSelector(1, &II);
|
|
|
|
}
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Symbolic Evaluation of Reference Counting Logic
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
namespace {
|
2008-05-06 06:41:27 +04:00
|
|
|
enum ArgEffect { IncRef, DecRef, DoNothing, StopTracking };
|
2008-04-24 21:22:33 +04:00
|
|
|
typedef std::vector<std::pair<unsigned,ArgEffect> > ArgEffects;
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
namespace llvm {
|
|
|
|
template <> struct FoldingSetTrait<ArgEffects> {
|
2008-04-11 02:58:08 +04:00
|
|
|
static void Profile(const ArgEffects& X, FoldingSetNodeID& ID) {
|
2008-04-24 21:22:33 +04:00
|
|
|
for (ArgEffects::const_iterator I = X.begin(), E = X.end(); I!= E; ++I) {
|
|
|
|
ID.AddInteger(I->first);
|
|
|
|
ID.AddInteger((unsigned) I->second);
|
|
|
|
}
|
2008-04-11 02:58:08 +04:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
};
|
|
|
|
} // end llvm namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class RetEffect {
|
|
|
|
public:
|
2008-04-11 03:44:06 +04:00
|
|
|
enum Kind { NoRet = 0x0, Alias = 0x1, OwnedSymbol = 0x2,
|
2008-05-06 02:11:16 +04:00
|
|
|
NotOwnedSymbol = 0x3, ReceiverAlias=0x4 };
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
private:
|
|
|
|
unsigned Data;
|
2008-05-06 02:11:16 +04:00
|
|
|
RetEffect(Kind k, unsigned D) { Data = (D << 3) | (unsigned) k; }
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
Kind getKind() const { return (Kind) (Data & 0x7); }
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
unsigned getValue() const {
|
|
|
|
assert(getKind() == Alias);
|
2008-05-06 02:11:16 +04:00
|
|
|
return Data >> 3;
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
2008-04-12 02:25:11 +04:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
static RetEffect MakeAlias(unsigned Idx) { return RetEffect(Alias, Idx); }
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
static RetEffect MakeReceiverAlias() { return RetEffect(ReceiverAlias, 0); }
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
static RetEffect MakeOwned() { return RetEffect(OwnedSymbol, 0); }
|
|
|
|
|
|
|
|
static RetEffect MakeNotOwned() { return RetEffect(NotOwnedSymbol, 0); }
|
|
|
|
|
2008-04-11 03:44:06 +04:00
|
|
|
static RetEffect MakeNoRet() { return RetEffect(NoRet, 0); }
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
operator Kind() const { return getKind(); }
|
|
|
|
|
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger(Data); }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
class RetainSummary : public llvm::FoldingSetNode {
|
2008-05-06 19:44:25 +04: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'.
|
2008-03-11 09:39:11 +03:00
|
|
|
ArgEffects* Args;
|
2008-05-06 19:44:25 +04:00
|
|
|
|
|
|
|
/// DefaultArgEffect - The default ArgEffect to apply to arguments that
|
|
|
|
/// do not have an entry in Args.
|
|
|
|
ArgEffect DefaultArgEffect;
|
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
ArgEffect Receiver;
|
2008-03-11 09:39:11 +03:00
|
|
|
RetEffect Ret;
|
|
|
|
public:
|
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
RetainSummary(ArgEffects* A, RetEffect R, ArgEffect defaultEff,
|
|
|
|
ArgEffect ReceiverEff)
|
|
|
|
: Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R) {}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
ArgEffect getArg(unsigned idx) const {
|
2008-05-06 19:44:25 +04:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
if (!Args)
|
2008-05-06 19:44:25 +04:00
|
|
|
return DefaultArgEffect;
|
2008-04-24 21:22:33 +04:00
|
|
|
|
|
|
|
// If Args is present, it is likely to contain only 1 element.
|
|
|
|
// Just do a linear search. Do it from the back because functions with
|
|
|
|
// large numbers of arguments will be tail heavy with respect to which
|
|
|
|
// argument they actually modify with respect to the reference count.
|
|
|
|
|
|
|
|
for (ArgEffects::reverse_iterator I=Args->rbegin(), E=Args->rend();
|
|
|
|
I!=E; ++I) {
|
|
|
|
|
|
|
|
if (idx > I->first)
|
2008-05-06 19:44:25 +04:00
|
|
|
return DefaultArgEffect;
|
2008-04-24 21:22:33 +04:00
|
|
|
|
|
|
|
if (idx == I->first)
|
|
|
|
return I->second;
|
|
|
|
}
|
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
return DefaultArgEffect;
|
2008-03-11 20:48:22 +03:00
|
|
|
}
|
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
RetEffect getRetEffect() const {
|
2008-03-12 04:21:45 +03:00
|
|
|
return Ret;
|
|
|
|
}
|
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
ArgEffect getReceiverEffect() const {
|
|
|
|
return Receiver;
|
|
|
|
}
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
typedef ArgEffects::const_iterator arg_iterator;
|
|
|
|
|
|
|
|
arg_iterator begin_args() const { return Args->begin(); }
|
|
|
|
arg_iterator end_args() const { return Args->end(); }
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects* A,
|
2008-05-06 19:44:25 +04:00
|
|
|
RetEffect RetEff, ArgEffect DefaultEff,
|
|
|
|
ArgEffect ReceiverEff) {
|
2008-03-11 09:39:11 +03:00
|
|
|
ID.AddPointer(A);
|
2008-05-06 06:26:56 +04:00
|
|
|
ID.Add(RetEff);
|
2008-05-06 19:44:25 +04:00
|
|
|
ID.AddInteger((unsigned) DefaultEff);
|
2008-05-06 06:26:56 +04:00
|
|
|
ID.AddInteger((unsigned) ReceiverEff);
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
2008-05-06 19:44:25 +04:00
|
|
|
Profile(ID, Args, Ret, DefaultArgEffect, Receiver);
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
class RetainSummaryManager {
|
|
|
|
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Typedefs.
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
|
|
|
|
typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<ArgEffects> >
|
|
|
|
ArgEffectsSetTy;
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
typedef llvm::FoldingSet<RetainSummary>
|
|
|
|
SummarySetTy;
|
|
|
|
|
|
|
|
typedef llvm::DenseMap<FunctionDecl*, RetainSummary*>
|
|
|
|
FuncSummariesTy;
|
|
|
|
|
|
|
|
typedef llvm::DenseMap<Selector, RetainSummary*>
|
2008-05-06 04:30:21 +04:00
|
|
|
ObjCMethSummariesTy;
|
2008-05-06 02:11:16 +04:00
|
|
|
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Data.
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
|
|
|
|
// Ctx - The ASTContext object for the analyzed ASTs.
|
2008-04-29 09:33:51 +04:00
|
|
|
ASTContext& Ctx;
|
2008-05-06 02:11:16 +04:00
|
|
|
|
|
|
|
// GCEnabled - Records whether or not the analyzed code runs in GC mode.
|
2008-04-29 09:33:51 +04:00
|
|
|
const bool GCEnabled;
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
// SummarySet - A FoldingSet of uniqued summaries.
|
2008-04-11 02:58:08 +04:00
|
|
|
SummarySetTy SummarySet;
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
// FuncSummaries - A map from FunctionDecls to summaries.
|
|
|
|
FuncSummariesTy FuncSummaries;
|
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
// ObjCInstMethSummaries - A map from selectors (for instance methods)
|
2008-05-06 02:11:16 +04:00
|
|
|
// to summaries.
|
2008-05-06 04:30:21 +04:00
|
|
|
ObjCMethSummariesTy ObjCInstMethSummaries;
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
// ObjCMethSummaries - A map from selectors to summaries.
|
|
|
|
ObjCMethSummariesTy ObjCMethSummaries;
|
2008-05-06 02:11:16 +04:00
|
|
|
|
|
|
|
// ArgEffectsSet - A FoldingSet of uniqued ArgEffects.
|
|
|
|
ArgEffectsSetTy ArgEffectsSet;
|
|
|
|
|
|
|
|
// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects,
|
|
|
|
// and all other data used by the checker.
|
|
|
|
llvm::BumpPtrAllocator BPAlloc;
|
|
|
|
|
|
|
|
// ScratchArgs - A holding buffer for construct ArgEffects.
|
|
|
|
ArgEffects ScratchArgs;
|
|
|
|
|
2008-05-06 22:11:36 +04:00
|
|
|
RetainSummary* StopSummary;
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Methods.
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
|
|
|
|
// getArgEffects - Returns a persistent ArgEffects object based on the
|
|
|
|
// data in ScratchArgs.
|
2008-03-12 04:21:45 +03:00
|
|
|
ArgEffects* getArgEffects();
|
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable };
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* getUnarySummary(FunctionDecl* FD, UnaryFuncKind func);
|
2008-04-29 09:33:51 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* getNSSummary(FunctionDecl* FD, const char* FName);
|
|
|
|
RetainSummary* getCFSummary(FunctionDecl* FD, const char* FName);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD);
|
|
|
|
RetainSummary* getCFSummaryGetRule(FunctionDecl* FD);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
RetainSummary* getPersistentSummary(ArgEffects* AE, RetEffect RetEff,
|
2008-05-06 19:44:25 +04:00
|
|
|
ArgEffect ReceiverEff = DoNothing,
|
|
|
|
ArgEffect DefaultEff = DoNothing);
|
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
RetainSummary* getPersistentSummary(RetEffect RE,
|
2008-05-06 19:44:25 +04:00
|
|
|
ArgEffect ReceiverEff = DoNothing,
|
|
|
|
ArgEffect DefaultEff = DoNothing) {
|
|
|
|
return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff);
|
2008-05-06 04:30:21 +04:00
|
|
|
}
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-05-06 22:11:36 +04:00
|
|
|
|
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
RetainSummary* getPersistentStopSummary() {
|
2008-05-06 22:11:36 +04:00
|
|
|
if (StopSummary)
|
|
|
|
return StopSummary;
|
|
|
|
|
|
|
|
StopSummary = getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
StopTracking, StopTracking);
|
|
|
|
|
|
|
|
return StopSummary;
|
2008-05-06 19:44:25 +04:00
|
|
|
}
|
2008-05-06 08:20:12 +04:00
|
|
|
|
2008-05-06 03:55:01 +04:00
|
|
|
RetainSummary* getInitMethodSummary(Selector S);
|
|
|
|
|
2008-05-06 04:38:54 +04:00
|
|
|
void InitializeInstMethSummaries();
|
|
|
|
void InitializeMethSummaries();
|
2008-05-06 04:30:21 +04:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
public:
|
2008-05-06 02:11:16 +04:00
|
|
|
|
|
|
|
RetainSummaryManager(ASTContext& ctx, bool gcenabled)
|
2008-05-06 22:11:36 +04:00
|
|
|
: Ctx(ctx), GCEnabled(gcenabled), StopSummary(0) {
|
2008-05-06 04:30:21 +04:00
|
|
|
|
2008-05-06 04:38:54 +04:00
|
|
|
InitializeInstMethSummaries();
|
|
|
|
InitializeMethSummaries();
|
2008-05-06 04:30:21 +04:00
|
|
|
}
|
2008-04-29 09:33:51 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
~RetainSummaryManager();
|
|
|
|
|
|
|
|
RetainSummary* getSummary(FunctionDecl* FD, ASTContext& Ctx);
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
RetainSummary* getMethodSummary(ObjCMessageExpr* ME);
|
2008-05-06 08:20:12 +04:00
|
|
|
RetainSummary* getInstanceMethodSummary(IdentifierInfo* ClsName, Selector S);
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
bool isGCEnabled() const { return GCEnabled; }
|
2008-03-11 09:39:11 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Implementation of checker data structures.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummaryManager::~RetainSummaryManager() {
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
// FIXME: The ArgEffects could eventually be allocated from BPAlloc,
|
|
|
|
// mitigating the need to do explicit cleanup of the
|
|
|
|
// Argument-Effect summaries.
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-05-06 03:55:01 +04:00
|
|
|
for (ArgEffectsSetTy::iterator I = ArgEffectsSet.begin(),
|
|
|
|
E = ArgEffectsSet.end(); I!=E; ++I)
|
2008-03-11 09:39:11 +03:00
|
|
|
I->getValue().~ArgEffects();
|
2008-03-06 03:08:09 +03:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
ArgEffects* RetainSummaryManager::getArgEffects() {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
if (ScratchArgs.empty())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Compute a profile for a non-empty ScratchArgs.
|
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
llvm::FoldingSetNodeID profile;
|
2008-04-24 21:22:33 +04:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
profile.Add(ScratchArgs);
|
|
|
|
void* InsertPos;
|
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
// Look up the uniqued copy, or create a new one.
|
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
llvm::FoldingSetNodeWrapper<ArgEffects>* E =
|
2008-05-06 02:11:16 +04:00
|
|
|
ArgEffectsSet.FindNodeOrInsertPos(profile, InsertPos);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
if (E) {
|
2008-03-12 04:21:45 +03:00
|
|
|
ScratchArgs.clear();
|
|
|
|
return &E->getValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
E = (llvm::FoldingSetNodeWrapper<ArgEffects>*)
|
|
|
|
BPAlloc.Allocate<llvm::FoldingSetNodeWrapper<ArgEffects> >();
|
|
|
|
|
|
|
|
new (E) llvm::FoldingSetNodeWrapper<ArgEffects>(ScratchArgs);
|
2008-05-06 02:11:16 +04:00
|
|
|
ArgEffectsSet.InsertNode(E, InsertPos);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
ScratchArgs.clear();
|
|
|
|
return &E->getValue();
|
|
|
|
}
|
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getPersistentSummary(ArgEffects* AE, RetEffect RetEff,
|
2008-05-06 19:44:25 +04:00
|
|
|
ArgEffect ReceiverEff,
|
|
|
|
ArgEffect DefaultEff) {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
// Generate a profile for the summary.
|
2008-03-12 04:21:45 +03:00
|
|
|
llvm::FoldingSetNodeID profile;
|
2008-05-06 19:44:25 +04:00
|
|
|
RetainSummary::Profile(profile, AE, RetEff, DefaultEff, ReceiverEff);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
// Look up the uniqued summary, or create one if it doesn't exist.
|
|
|
|
void* InsertPos;
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* Summ = SummarySet.FindNodeOrInsertPos(profile, InsertPos);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
if (Summ)
|
|
|
|
return Summ;
|
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
// Create the summary and return it.
|
2008-05-06 02:11:16 +04:00
|
|
|
Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
|
2008-05-06 19:44:25 +04:00
|
|
|
new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff);
|
2008-03-12 04:21:45 +03:00
|
|
|
SummarySet.InsertNode(Summ, InsertPos);
|
|
|
|
|
|
|
|
return Summ;
|
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Summary creation for functions (largely uses of Core Foundation).
|
|
|
|
//===----------------------------------------------------------------------===//
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD,
|
2008-05-06 03:55:01 +04:00
|
|
|
ASTContext& Ctx) {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
SourceLocation Loc = FD->getLocation();
|
|
|
|
|
|
|
|
if (!Loc.isFileID())
|
|
|
|
return NULL;
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
// Look up a summary in our cache of FunctionDecls -> Summaries.
|
2008-05-06 02:11:16 +04:00
|
|
|
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
|
2008-04-24 21:22:33 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
if (I != FuncSummaries.end())
|
2008-04-24 21:22:33 +04:00
|
|
|
return I->second;
|
|
|
|
|
|
|
|
// No summary. Generate one.
|
2008-03-12 04:21:45 +03:00
|
|
|
const char* FName = FD->getIdentifier()->getName();
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary *S = 0;
|
2008-05-05 20:51:50 +04:00
|
|
|
|
|
|
|
if (FName[0] == 'C' && FName[1] == 'F')
|
|
|
|
S = getCFSummary(FD, FName);
|
|
|
|
else if (FName[0] == 'N' && FName[1] == 'S')
|
|
|
|
S = getNSSummary(FD, FName);
|
2008-04-24 21:22:33 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
FuncSummaries[FD] = S;
|
2008-05-05 20:51:50 +04:00
|
|
|
return S;
|
2008-03-06 03:08:09 +03:00
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* RetainSummaryManager::getNSSummary(FunctionDecl* FD,
|
2008-05-06 03:55:01 +04:00
|
|
|
const char* FName) {
|
2008-05-05 20:51:50 +04:00
|
|
|
FName += 2;
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
if (strcmp(FName, "MakeCollectable") == 0)
|
|
|
|
return getUnarySummary(FD, cfmakecollectable);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* RetainSummaryManager::getCFSummary(FunctionDecl* FD,
|
2008-05-06 03:55:01 +04:00
|
|
|
const char* FName) {
|
2008-05-05 20:51:50 +04:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
FName += 2;
|
|
|
|
|
|
|
|
if (strcmp(FName, "Retain") == 0)
|
2008-05-05 20:51:50 +04:00
|
|
|
return getUnarySummary(FD, cfretain);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
if (strcmp(FName, "Release") == 0)
|
2008-05-05 20:51:50 +04:00
|
|
|
return getUnarySummary(FD, cfrelease);
|
2008-05-02 01:31:50 +04:00
|
|
|
|
2008-04-29 09:33:51 +04:00
|
|
|
if (strcmp(FName, "MakeCollectable") == 0)
|
2008-05-05 20:51:50 +04:00
|
|
|
return getUnarySummary(FD, cfmakecollectable);
|
|
|
|
|
|
|
|
if (strstr(FName, "Create") || strstr(FName, "Copy"))
|
|
|
|
return getCFSummaryCreateRule(FD);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
if (strstr(FName, "Get"))
|
2008-05-05 20:51:50 +04:00
|
|
|
return getCFSummaryGetRule(FD);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
return 0;
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getUnarySummary(FunctionDecl* FD, UnaryFuncKind func) {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
FunctionTypeProto* FT =
|
|
|
|
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
if (FT) {
|
|
|
|
|
|
|
|
if (FT->getNumArgs() != 1)
|
|
|
|
return 0;
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
TypedefType* ArgT = dyn_cast<TypedefType>(FT->getArgType(0).getTypePtr());
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
if (!ArgT)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!ArgT->isPointerType())
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
assert (ScratchArgs.empty());
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-04-29 09:33:51 +04:00
|
|
|
switch (func) {
|
|
|
|
case cfretain: {
|
|
|
|
ScratchArgs.push_back(std::make_pair(0, IncRef));
|
2008-05-06 04:30:21 +04:00
|
|
|
return getPersistentSummary(RetEffect::MakeAlias(0));
|
2008-04-29 09:33:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
case cfrelease: {
|
|
|
|
ScratchArgs.push_back(std::make_pair(0, DecRef));
|
2008-05-06 04:30:21 +04:00
|
|
|
return getPersistentSummary(RetEffect::MakeNoRet());
|
2008-04-29 09:33:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
case cfmakecollectable: {
|
|
|
|
if (GCEnabled)
|
|
|
|
ScratchArgs.push_back(std::make_pair(0, DecRef));
|
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
return getPersistentSummary(RetEffect::MakeAlias(0));
|
2008-04-29 09:33:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2008-05-05 20:51:50 +04:00
|
|
|
assert (false && "Not a supported unary function.");
|
2008-04-11 03:44:06 +04:00
|
|
|
}
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool isCFRefType(QualType T) {
|
|
|
|
|
|
|
|
if (!T->isPointerType())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Check the typedef for the name "CF" and the substring "Ref".
|
|
|
|
|
|
|
|
TypedefType* TD = dyn_cast<TypedefType>(T.getTypePtr());
|
|
|
|
|
|
|
|
if (!TD)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const char* TDName = TD->getDecl()->getIdentifier()->getName();
|
|
|
|
assert (TDName);
|
|
|
|
|
|
|
|
if (TDName[0] != 'C' || TDName[1] != 'F')
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (strstr(TDName, "Ref") == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
static bool isNSType(QualType T) {
|
|
|
|
|
|
|
|
if (!T->isPointerType())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Check the typedef for the name "CF" and the substring "Ref".
|
|
|
|
|
|
|
|
TypedefType* TD = dyn_cast<TypedefType>(T.getTypePtr());
|
|
|
|
|
|
|
|
if (!TD)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const char* TDName = TD->getDecl()->getIdentifier()->getName();
|
|
|
|
assert (TDName);
|
|
|
|
|
|
|
|
if (TDName[0] != 'N' || TDName[1] != 'S')
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
FunctionTypeProto* FT =
|
|
|
|
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
|
|
|
|
|
|
|
|
if (FT && !isCFRefType(FT->getResultType()))
|
|
|
|
return 0;
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
// FIXME: Add special-cases for functions that retain/release. For now
|
|
|
|
// just handle the default case.
|
2008-04-24 21:22:33 +04:00
|
|
|
|
|
|
|
assert (ScratchArgs.empty());
|
2008-05-06 04:30:21 +04:00
|
|
|
return getPersistentSummary(RetEffect::MakeOwned());
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
FunctionTypeProto* FT =
|
|
|
|
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
|
2008-04-12 00:11:19 +04:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
if (FT) {
|
|
|
|
QualType RetTy = FT->getResultType();
|
2008-04-12 00:11:19 +04:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
// FIXME: For now we assume that all pointer types returned are referenced
|
|
|
|
// counted. Since this is the "Get" rule, we assume non-ownership, which
|
|
|
|
// works fine for things that are not reference counted. We do this because
|
|
|
|
// some generic data structures return "void*". We need something better
|
|
|
|
// in the future.
|
|
|
|
|
|
|
|
if (!isCFRefType(RetTy) && !RetTy->isPointerType())
|
|
|
|
return 0;
|
|
|
|
}
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
// FIXME: Add special-cases for functions that retain/release. For now
|
|
|
|
// just handle the default case.
|
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
assert (ScratchArgs.empty());
|
2008-05-06 04:30:21 +04:00
|
|
|
return getPersistentSummary(RetEffect::MakeNotOwned());
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Summary creation for Selectors.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getInitMethodSummary(Selector S) {
|
2008-05-06 03:55:01 +04:00
|
|
|
assert(ScratchArgs.empty());
|
|
|
|
|
|
|
|
RetainSummary* Summ =
|
2008-05-06 04:30:21 +04:00
|
|
|
getPersistentSummary(RetEffect::MakeReceiverAlias());
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
ObjCMethSummaries[S] = Summ;
|
2008-05-06 03:55:01 +04:00
|
|
|
return Summ;
|
|
|
|
}
|
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getMethodSummary(ObjCMessageExpr* ME) {
|
|
|
|
|
|
|
|
Selector S = ME->getSelector();
|
2008-05-06 03:55:01 +04:00
|
|
|
|
|
|
|
// Look up a summary in our cache of Selectors -> Summaries.
|
2008-05-06 04:30:21 +04:00
|
|
|
ObjCMethSummariesTy::iterator I = ObjCMethSummaries.find(S);
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
if (I != ObjCMethSummaries.end())
|
2008-05-06 03:55:01 +04:00
|
|
|
return I->second;
|
2008-05-06 22:11:36 +04:00
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
// Only generate real summaries for methods involving
|
|
|
|
// NSxxxx objects.
|
2008-05-06 22:11:36 +04:00
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
if (!isNSType(ME->getReceiver()->getType())) {
|
|
|
|
RetainSummary* Summ = getPersistentStopSummary();
|
|
|
|
ObjCMethSummaries[S] = Summ;
|
|
|
|
return Summ;
|
|
|
|
}
|
2008-05-06 22:11:36 +04:00
|
|
|
|
2008-05-06 03:55:01 +04:00
|
|
|
// "initXXX": pass-through for receiver.
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-05-06 03:55:01 +04:00
|
|
|
const char* s = S.getIdentifierInfoForSlot(0)->getName();
|
2008-05-06 10:09:09 +04:00
|
|
|
|
2008-05-06 10:17:42 +04:00
|
|
|
if (strncmp(s, "init", 4) == 0)
|
2008-05-06 22:11:36 +04:00
|
|
|
return getInitMethodSummary(S);
|
2008-05-06 19:44:25 +04:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
// Generate a summary. For all "setYYY:" and "addXXX:" slots => StopTracking.
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
assert (ScratchArgs.empty());
|
|
|
|
|
|
|
|
if (S.isUnarySelector()) {
|
|
|
|
RetainSummary* Summ = getPersistentSummary(RetEffect::MakeNoRet());
|
|
|
|
return Summ;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i = 0, e = ME->getNumArgs(); i!=e; ++i) {
|
|
|
|
IdentifierInfo *II = S.getIdentifierInfoForSlot(i);
|
|
|
|
const char* s = II->getName();
|
|
|
|
|
|
|
|
if (strncmp(s, "set", 3) == 0 || strncmp(s, "add", 3) == 0)
|
|
|
|
ScratchArgs.push_back(std::make_pair(i, StopTracking));
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-05-06 03:55:01 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-05-06 04:38:54 +04:00
|
|
|
void RetainSummaryManager::InitializeInstMethSummaries() {
|
2008-05-06 04:30:21 +04:00
|
|
|
|
|
|
|
assert (ScratchArgs.empty());
|
|
|
|
|
|
|
|
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet() : RetEffect::MakeOwned();
|
|
|
|
RetainSummary* Summ = getPersistentSummary(E);
|
|
|
|
|
|
|
|
// Create the "alloc" selector.
|
|
|
|
ObjCInstMethSummaries[ GetNullarySelector("alloc", Ctx) ] = Summ;
|
|
|
|
|
|
|
|
// Create the "new" selector.
|
|
|
|
ObjCInstMethSummaries[ GetNullarySelector("new", Ctx) ] = Summ;
|
|
|
|
|
|
|
|
// Create the "allocWithZone:" selector.
|
|
|
|
ObjCInstMethSummaries[ GetUnarySelector("allocWithZone", Ctx) ] = Summ;
|
|
|
|
|
|
|
|
// Create the "copyWithZone:" selector.
|
|
|
|
ObjCInstMethSummaries[ GetUnarySelector("copyWithZone", Ctx) ] = Summ;
|
|
|
|
|
|
|
|
// Create the "mutableCopyWithZone:" selector.
|
|
|
|
ObjCInstMethSummaries[ GetUnarySelector("mutableCopyWithZone", Ctx) ] = Summ;
|
2008-05-06 22:11:36 +04:00
|
|
|
|
|
|
|
// Special cases: create the NSProcessInfo::processInfo selector.
|
|
|
|
ObjCInstMethSummaries[ GetNullarySelector("processInfo", Ctx) ] =
|
|
|
|
getPersistentSummary(RetEffect::MakeNoRet());
|
2008-05-06 04:30:21 +04:00
|
|
|
}
|
|
|
|
|
2008-05-06 04:38:54 +04:00
|
|
|
void RetainSummaryManager::InitializeMethSummaries() {
|
|
|
|
|
|
|
|
assert (ScratchArgs.empty());
|
|
|
|
|
|
|
|
// Create the "init" selector.
|
|
|
|
RetainSummary* Summ = getPersistentSummary(RetEffect::MakeReceiverAlias());
|
|
|
|
ObjCMethSummaries[ GetNullarySelector("init", Ctx) ] = Summ;
|
|
|
|
|
|
|
|
// Create the "copy" selector.
|
|
|
|
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet() : RetEffect::MakeOwned();
|
|
|
|
Summ = getPersistentSummary(E);
|
|
|
|
ObjCMethSummaries[ GetNullarySelector("copy", Ctx) ] = Summ;
|
|
|
|
|
|
|
|
// Create the "mutableCopy" selector.
|
|
|
|
ObjCMethSummaries[ GetNullarySelector("mutableCopy", Ctx) ] = Summ;
|
2008-05-06 06:26:56 +04:00
|
|
|
|
|
|
|
// Create the "retain" selector.
|
|
|
|
E = RetEffect::MakeReceiverAlias();
|
|
|
|
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : IncRef);
|
|
|
|
ObjCMethSummaries[ GetNullarySelector("retain", Ctx) ] = Summ;
|
|
|
|
|
|
|
|
// Create the "release" selector.
|
|
|
|
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef);
|
|
|
|
ObjCMethSummaries[ GetNullarySelector("release", Ctx) ] = Summ;
|
|
|
|
|
|
|
|
// Create the "autorelease" selector.
|
2008-05-06 06:41:27 +04:00
|
|
|
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : StopTracking);
|
2008-05-06 06:26:56 +04:00
|
|
|
ObjCMethSummaries[ GetNullarySelector("autorelease", Ctx) ] = Summ;
|
2008-05-06 04:38:54 +04:00
|
|
|
}
|
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
|
2008-05-06 08:20:12 +04:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getInstanceMethodSummary(IdentifierInfo* ClsName,
|
|
|
|
Selector S) {
|
2008-05-06 03:55:01 +04:00
|
|
|
|
|
|
|
// Look up a summary in our cache of Selectors -> Summaries.
|
2008-05-06 04:30:21 +04:00
|
|
|
ObjCMethSummariesTy::iterator I = ObjCInstMethSummaries.find(S);
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
if (I != ObjCInstMethSummaries.end())
|
2008-05-06 03:55:01 +04:00
|
|
|
return I->second;
|
2008-05-06 08:20:12 +04:00
|
|
|
|
|
|
|
// Don't track anything if using GC.
|
|
|
|
if (isGCEnabled())
|
|
|
|
return 0;
|
|
|
|
|
2008-05-06 22:11:36 +04:00
|
|
|
// Only generate real summaries for NSXXX classes.
|
|
|
|
|
|
|
|
const char* cls = ClsName->getName();
|
|
|
|
|
|
|
|
if (cls[0] != 'N' || cls[1] != 'S')
|
|
|
|
return getPersistentStopSummary();
|
|
|
|
|
2008-05-06 08:20:12 +04:00
|
|
|
// Heuristic: XXXXwithYYYY, where XXX is the class name with the "NS"
|
|
|
|
// stripped off is usually an allocation.
|
|
|
|
|
2008-05-06 22:11:36 +04:00
|
|
|
const char* s = S.getIdentifierInfoForSlot(0)->getName();
|
2008-05-06 08:20:12 +04:00
|
|
|
|
2008-05-06 22:11:36 +04:00
|
|
|
cls += 2;
|
2008-05-06 08:20:12 +04:00
|
|
|
|
|
|
|
if (cls[0] == '\0' || s[0] == '\0' || tolower(cls[0]) != s[0])
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
++cls;
|
|
|
|
++s;
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-05-06 08:20:12 +04:00
|
|
|
// Now look at the rest of the characters.
|
|
|
|
unsigned len = strlen(cls);
|
|
|
|
|
|
|
|
// Prefix matches?
|
2008-05-06 10:17:42 +04:00
|
|
|
if (strncmp(cls, s, len) != 0)
|
2008-05-06 08:20:12 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
s += len;
|
|
|
|
|
|
|
|
// If 's' is the same as clsName, or 's' has "With" after the class name,
|
|
|
|
// treat it as an allocator.
|
|
|
|
do {
|
|
|
|
|
|
|
|
if (s[0] == '\0')
|
|
|
|
break;
|
|
|
|
|
2008-05-06 10:17:42 +04:00
|
|
|
if (strncmp(s, "With", 4) == 0)
|
2008-05-06 08:20:12 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
} while(0);
|
|
|
|
|
|
|
|
// Generate the summary.
|
|
|
|
|
|
|
|
assert (ScratchArgs.empty());
|
|
|
|
RetainSummary* Summ = getPersistentSummary(RetEffect::MakeOwned());
|
|
|
|
ObjCInstMethSummaries[S] = Summ;
|
|
|
|
return Summ;
|
2008-05-06 03:55:01 +04:00
|
|
|
}
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-04-17 00:40:59 +04:00
|
|
|
// Reference-counting logic (typestate + counts).
|
2008-03-11 09:39:11 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
class VISIBILITY_HIDDEN RefVal {
|
2008-04-17 22:12:53 +04:00
|
|
|
public:
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
enum Kind {
|
|
|
|
Owned = 0, // Owning reference.
|
|
|
|
NotOwned, // Reference is not owned by still valid (not freed).
|
|
|
|
Released, // Object has been released.
|
|
|
|
ReturnedOwned, // Returned object passes ownership to caller.
|
|
|
|
ReturnedNotOwned, // Return object does not pass ownership to caller.
|
|
|
|
ErrorUseAfterRelease, // Object used after released.
|
|
|
|
ErrorReleaseNotOwned, // Release of an object that was not owned.
|
|
|
|
ErrorLeak // A memory leak due to excessive reference counts.
|
|
|
|
};
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
private:
|
|
|
|
|
|
|
|
Kind kind;
|
|
|
|
unsigned Cnt;
|
|
|
|
|
|
|
|
RefVal(Kind k, unsigned cnt) : kind(k), Cnt(cnt) {}
|
|
|
|
|
|
|
|
RefVal(Kind k) : kind(k), Cnt(0) {}
|
2008-03-11 20:48:22 +03:00
|
|
|
|
|
|
|
public:
|
2008-04-17 02:32:20 +04:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
Kind getKind() const { return kind; }
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
unsigned getCount() const { return Cnt; }
|
|
|
|
|
|
|
|
// Useful predicates.
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-03-11 21:14:09 +03:00
|
|
|
static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
static bool isLeak(Kind k) { return k == ErrorLeak; }
|
|
|
|
|
2008-04-12 02:25:11 +04:00
|
|
|
bool isOwned() const {
|
|
|
|
return getKind() == Owned;
|
|
|
|
}
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
bool isNotOwned() const {
|
|
|
|
return getKind() == NotOwned;
|
|
|
|
}
|
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
bool isReturnedOwned() const {
|
|
|
|
return getKind() == ReturnedOwned;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isReturnedNotOwned() const {
|
|
|
|
return getKind() == ReturnedNotOwned;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isNonLeakError() const {
|
|
|
|
Kind k = getKind();
|
|
|
|
return isError(k) && !isLeak(k);
|
|
|
|
}
|
|
|
|
|
|
|
|
// State creation: normal state.
|
|
|
|
|
2008-04-11 03:09:18 +04:00
|
|
|
static RefVal makeOwned(unsigned Count = 0) {
|
|
|
|
return RefVal(Owned, Count);
|
|
|
|
}
|
|
|
|
|
|
|
|
static RefVal makeNotOwned(unsigned Count = 0) {
|
|
|
|
return RefVal(NotOwned, Count);
|
|
|
|
}
|
2008-04-17 22:12:53 +04:00
|
|
|
|
|
|
|
static RefVal makeReturnedOwned(unsigned Count) {
|
|
|
|
return RefVal(ReturnedOwned, Count);
|
|
|
|
}
|
|
|
|
|
|
|
|
static RefVal makeReturnedNotOwned() {
|
|
|
|
return RefVal(ReturnedNotOwned);
|
|
|
|
}
|
|
|
|
|
|
|
|
// State creation: errors.
|
2008-04-11 03:09:18 +04:00
|
|
|
|
2008-05-05 21:53:17 +04:00
|
|
|
static RefVal makeLeak(unsigned Count) { return RefVal(ErrorLeak, Count); }
|
2008-03-11 20:48:22 +03:00
|
|
|
static RefVal makeReleased() { return RefVal(Released); }
|
|
|
|
static RefVal makeUseAfterRelease() { return RefVal(ErrorUseAfterRelease); }
|
|
|
|
static RefVal makeReleaseNotOwned() { return RefVal(ErrorReleaseNotOwned); }
|
2008-04-17 22:12:53 +04:00
|
|
|
|
|
|
|
// Comparison, profiling, and pretty-printing.
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
bool operator==(const RefVal& X) const {
|
|
|
|
return kind == X.kind && Cnt == X.Cnt;
|
|
|
|
}
|
2008-03-11 22:44:10 +03:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
|
|
|
ID.AddInteger((unsigned) kind);
|
|
|
|
ID.AddInteger(Cnt);
|
|
|
|
}
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
void print(std::ostream& Out) const;
|
2008-03-11 20:48:22 +03:00
|
|
|
};
|
2008-03-11 22:44:10 +03:00
|
|
|
|
|
|
|
void RefVal::print(std::ostream& Out) const {
|
|
|
|
switch (getKind()) {
|
|
|
|
default: assert(false);
|
2008-04-11 03:09:18 +04:00
|
|
|
case Owned: {
|
|
|
|
Out << "Owned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
2008-03-11 22:44:10 +03:00
|
|
|
break;
|
2008-04-11 03:09:18 +04:00
|
|
|
}
|
2008-03-11 22:44:10 +03:00
|
|
|
|
2008-04-11 03:09:18 +04:00
|
|
|
case NotOwned: {
|
2008-04-17 22:12:53 +04:00
|
|
|
Out << "NotOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ReturnedOwned: {
|
|
|
|
Out << "ReturnedOwned";
|
2008-04-11 03:09:18 +04:00
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
2008-03-11 22:44:10 +03:00
|
|
|
break;
|
2008-04-11 03:09:18 +04:00
|
|
|
}
|
2008-03-11 22:44:10 +03:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
case ReturnedNotOwned: {
|
|
|
|
Out << "ReturnedNotOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
case Released:
|
|
|
|
Out << "Released";
|
|
|
|
break;
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
case ErrorLeak:
|
|
|
|
Out << "Leaked";
|
|
|
|
break;
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
case ErrorUseAfterRelease:
|
|
|
|
Out << "Use-After-Release [ERROR]";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ErrorReleaseNotOwned:
|
|
|
|
Out << "Release of Not-Owned [ERROR]";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-05-05 21:53:17 +04:00
|
|
|
static inline unsigned GetCount(RefVal V) {
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
return V.getCount();
|
|
|
|
|
|
|
|
case RefVal::Owned:
|
|
|
|
return V.getCount()+1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-17 00:40:59 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Transfer functions.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
|
2008-04-18 07:39:05 +04:00
|
|
|
public:
|
2008-03-11 22:44:10 +03:00
|
|
|
// Type definitions.
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
typedef llvm::ImmutableMap<SymbolID, RefVal> RefBindings;
|
2008-03-11 09:39:11 +03:00
|
|
|
typedef RefBindings::Factory RefBFactoryTy;
|
2008-03-11 21:14:09 +03:00
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
typedef llvm::DenseMap<GRExprEngine::NodeTy*,std::pair<Expr*, SymbolID> >
|
|
|
|
ReleasesNotOwnedTy;
|
2008-03-11 21:14:09 +03:00
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
typedef ReleasesNotOwnedTy UseAfterReleasesTy;
|
|
|
|
|
|
|
|
typedef llvm::DenseMap<GRExprEngine::NodeTy*, std::vector<SymbolID>*>
|
|
|
|
LeaksTy;
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
class BindingsPrinter : public ValueState::CheckerStatePrinter {
|
|
|
|
public:
|
|
|
|
virtual void PrintCheckerState(std::ostream& Out, void* State,
|
|
|
|
const char* nl, const char* sep);
|
|
|
|
};
|
2008-04-18 07:39:05 +04:00
|
|
|
|
|
|
|
private:
|
2008-03-11 22:44:10 +03:00
|
|
|
// Instance variables.
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummaryManager Summaries;
|
|
|
|
const bool EmitStandardWarnings;
|
|
|
|
const LangOptions& LOpts;
|
|
|
|
RefBFactoryTy RefBFactory;
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-03-11 21:14:09 +03:00
|
|
|
UseAfterReleasesTy UseAfterReleases;
|
|
|
|
ReleasesNotOwnedTy ReleasesNotOwned;
|
2008-04-17 02:32:20 +04:00
|
|
|
LeaksTy Leaks;
|
2008-03-11 21:14:09 +03:00
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
BindingsPrinter Printer;
|
|
|
|
|
2008-04-16 08:28:53 +04:00
|
|
|
Selector RetainSelector;
|
|
|
|
Selector ReleaseSelector;
|
2008-05-01 06:18:37 +04:00
|
|
|
Selector AutoreleaseSelector;
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
public:
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
static RefBindings GetRefBindings(ValueState& StImpl) {
|
|
|
|
return RefBindings((RefBindings::TreeTy*) StImpl.CheckerState);
|
|
|
|
}
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
private:
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
static void SetRefBindings(ValueState& StImpl, RefBindings B) {
|
|
|
|
StImpl.CheckerState = B.getRoot();
|
|
|
|
}
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
RefBindings Remove(RefBindings B, SymbolID sym) {
|
|
|
|
return RefBFactory.Remove(B, sym);
|
|
|
|
}
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
RefBindings Update(RefBindings B, SymbolID sym, RefVal V, ArgEffect E,
|
2008-04-16 08:28:53 +04:00
|
|
|
RefVal::Kind& hasErr);
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
void ProcessNonLeakError(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
Expr* NodeExpr, Expr* ErrorExpr,
|
|
|
|
ExplodedNode<ValueState>* Pred,
|
|
|
|
ValueState* St,
|
2008-04-18 07:39:05 +04:00
|
|
|
RefVal::Kind hasErr, SymbolID Sym);
|
2008-04-17 02:32:20 +04:00
|
|
|
|
|
|
|
ValueState* HandleSymbolDeath(ValueStateManager& VMgr, ValueState* St,
|
|
|
|
SymbolID sid, RefVal V, bool& hasLeak);
|
|
|
|
|
|
|
|
ValueState* NukeBinding(ValueStateManager& VMgr, ValueState* St,
|
|
|
|
SymbolID sid);
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
public:
|
2008-04-17 00:40:59 +04:00
|
|
|
|
2008-05-02 22:01:49 +04:00
|
|
|
CFRefCount(ASTContext& Ctx, bool gcenabled, bool StandardWarnings,
|
|
|
|
const LangOptions& lopts)
|
2008-04-29 09:33:51 +04:00
|
|
|
: Summaries(Ctx, gcenabled),
|
2008-05-02 22:01:49 +04:00
|
|
|
EmitStandardWarnings(StandardWarnings),
|
2008-05-01 03:47:44 +04:00
|
|
|
LOpts(lopts),
|
2008-05-01 22:31:44 +04:00
|
|
|
RetainSelector(GetNullarySelector("retain", Ctx)),
|
|
|
|
ReleaseSelector(GetNullarySelector("release", Ctx)),
|
|
|
|
AutoreleaseSelector(GetNullarySelector("autorelease", Ctx)) {}
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
virtual ~CFRefCount() {
|
|
|
|
for (LeaksTy::iterator I = Leaks.begin(), E = Leaks.end(); I!=E; ++I)
|
|
|
|
delete I->second;
|
|
|
|
}
|
2008-04-10 03:49:11 +04:00
|
|
|
|
|
|
|
virtual void RegisterChecks(GRExprEngine& Eng);
|
2008-03-11 22:44:10 +03:00
|
|
|
|
|
|
|
virtual ValueState::CheckerStatePrinter* getCheckerStatePrinter() {
|
|
|
|
return &Printer;
|
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
bool isGCEnabled() const { return Summaries.isGCEnabled(); }
|
2008-05-01 03:47:44 +04:00
|
|
|
const LangOptions& getLangOptions() const { return LOpts; }
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
// Calls.
|
2008-05-06 02:11:16 +04:00
|
|
|
|
|
|
|
void EvalSummary(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Eng,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
Expr* Ex,
|
|
|
|
Expr* Receiver,
|
|
|
|
RetainSummary* Summ,
|
|
|
|
Expr** arg_beg, Expr** arg_end,
|
|
|
|
ExplodedNode<ValueState>* Pred);
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
virtual void EvalCall(ExplodedNodeSet<ValueState>& Dst,
|
2008-03-13 00:06:49 +03:00
|
|
|
GRExprEngine& Eng,
|
2008-03-11 09:39:11 +03:00
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
2008-04-24 00:12:28 +04:00
|
|
|
CallExpr* CE, RVal L,
|
2008-03-11 09:39:11 +03:00
|
|
|
ExplodedNode<ValueState>* Pred);
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-04-16 03:44:31 +04:00
|
|
|
virtual void EvalObjCMessageExpr(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Engine,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
ObjCMessageExpr* ME,
|
|
|
|
ExplodedNode<ValueState>* Pred);
|
|
|
|
|
|
|
|
bool EvalObjCMessageExprAux(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Engine,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
ObjCMessageExpr* ME,
|
|
|
|
ExplodedNode<ValueState>* Pred);
|
|
|
|
|
2008-04-17 00:40:59 +04:00
|
|
|
// Stores.
|
|
|
|
|
|
|
|
virtual void EvalStore(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Engine,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
Expr* E, ExplodedNode<ValueState>* Pred,
|
|
|
|
ValueState* St, RVal TargetLV, RVal Val);
|
2008-04-12 02:25:11 +04:00
|
|
|
// End-of-path.
|
|
|
|
|
|
|
|
virtual void EvalEndPath(GRExprEngine& Engine,
|
|
|
|
GREndPathNodeBuilder<ValueState>& Builder);
|
|
|
|
|
2008-04-25 03:57:27 +04:00
|
|
|
virtual void EvalDeadSymbols(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Engine,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
2008-04-25 05:25:15 +04:00
|
|
|
ExplodedNode<ValueState>* Pred,
|
|
|
|
Stmt* S,
|
2008-04-25 03:57:27 +04:00
|
|
|
ValueState* St,
|
|
|
|
const ValueStateManager::DeadSymbolsTy& Dead);
|
2008-04-17 22:12:53 +04:00
|
|
|
// Return statements.
|
|
|
|
|
|
|
|
virtual void EvalReturn(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Engine,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
ReturnStmt* S,
|
|
|
|
ExplodedNode<ValueState>* Pred);
|
2008-04-18 23:23:43 +04:00
|
|
|
|
|
|
|
// Assumptions.
|
|
|
|
|
|
|
|
virtual ValueState* EvalAssume(GRExprEngine& Engine, ValueState* St,
|
|
|
|
RVal Cond, bool Assumption, bool& isFeasible);
|
|
|
|
|
2008-04-09 05:10:13 +04:00
|
|
|
// Error iterators.
|
|
|
|
|
|
|
|
typedef UseAfterReleasesTy::iterator use_after_iterator;
|
|
|
|
typedef ReleasesNotOwnedTy::iterator bad_release_iterator;
|
2008-04-18 03:43:50 +04:00
|
|
|
typedef LeaksTy::iterator leaks_iterator;
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
use_after_iterator use_after_begin() { return UseAfterReleases.begin(); }
|
|
|
|
use_after_iterator use_after_end() { return UseAfterReleases.end(); }
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
bad_release_iterator bad_release_begin() { return ReleasesNotOwned.begin(); }
|
|
|
|
bad_release_iterator bad_release_end() { return ReleasesNotOwned.end(); }
|
2008-04-18 03:43:50 +04:00
|
|
|
|
|
|
|
leaks_iterator leaks_begin() { return Leaks.begin(); }
|
|
|
|
leaks_iterator leaks_end() { return Leaks.end(); }
|
2008-03-11 09:39:11 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
void CFRefCount::BindingsPrinter::PrintCheckerState(std::ostream& Out,
|
|
|
|
void* State, const char* nl,
|
|
|
|
const char* sep) {
|
|
|
|
RefBindings B((RefBindings::TreeTy*) State);
|
|
|
|
|
|
|
|
if (State)
|
|
|
|
Out << sep << nl;
|
|
|
|
|
|
|
|
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
|
|
|
|
Out << (*I).first << " : ";
|
|
|
|
(*I).second.print(Out);
|
|
|
|
Out << nl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
static inline ArgEffect GetArgE(RetainSummary* Summ, unsigned idx) {
|
2008-04-12 00:23:24 +04:00
|
|
|
return Summ ? Summ->getArg(idx) : DoNothing;
|
|
|
|
}
|
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
static inline RetEffect GetRetEffect(RetainSummary* Summ) {
|
|
|
|
return Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet();
|
2008-04-12 00:23:24 +04:00
|
|
|
}
|
|
|
|
|
2008-05-06 06:41:27 +04:00
|
|
|
static inline ArgEffect GetReceiverE(RetainSummary* Summ) {
|
|
|
|
return Summ ? Summ->getReceiverEffect() : DoNothing;
|
|
|
|
}
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
void CFRefCount::ProcessNonLeakError(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
Expr* NodeExpr, Expr* ErrorExpr,
|
|
|
|
ExplodedNode<ValueState>* Pred,
|
|
|
|
ValueState* St,
|
2008-04-18 07:39:05 +04:00
|
|
|
RefVal::Kind hasErr, SymbolID Sym) {
|
2008-04-16 08:28:53 +04:00
|
|
|
Builder.BuildSinks = true;
|
|
|
|
GRExprEngine::NodeTy* N = Builder.MakeNode(Dst, NodeExpr, Pred, St);
|
|
|
|
|
|
|
|
if (!N) return;
|
|
|
|
|
|
|
|
switch (hasErr) {
|
|
|
|
default: assert(false);
|
|
|
|
case RefVal::ErrorUseAfterRelease:
|
2008-04-18 07:39:05 +04:00
|
|
|
UseAfterReleases[N] = std::make_pair(ErrorExpr, Sym);
|
2008-04-16 08:28:53 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::ErrorReleaseNotOwned:
|
2008-04-18 07:39:05 +04:00
|
|
|
ReleasesNotOwned[N] = std::make_pair(ErrorExpr, Sym);
|
2008-04-16 08:28:53 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
void CFRefCount::EvalSummary(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Eng,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
Expr* Ex,
|
|
|
|
Expr* Receiver,
|
|
|
|
RetainSummary* Summ,
|
|
|
|
Expr** arg_beg, Expr** arg_end,
|
|
|
|
ExplodedNode<ValueState>* Pred) {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
// Get the state.
|
2008-05-06 02:11:16 +04:00
|
|
|
ValueStateManager& StateMgr = Eng.getStateManager();
|
2008-03-11 09:39:11 +03:00
|
|
|
ValueState* St = Builder.GetState(Pred);
|
|
|
|
|
2008-05-06 06:41:27 +04:00
|
|
|
|
|
|
|
// Evaluate the effect of the arguments.
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
ValueState StVals = *St;
|
2008-04-16 08:28:53 +04:00
|
|
|
RefVal::Kind hasErr = (RefVal::Kind) 0;
|
2008-03-12 04:21:45 +03:00
|
|
|
unsigned idx = 0;
|
2008-04-11 22:40:51 +04:00
|
|
|
Expr* ErrorExpr = NULL;
|
2008-04-18 07:39:05 +04:00
|
|
|
SymbolID ErrorSym = 0;
|
2008-04-11 22:40:51 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
for (Expr **I = arg_beg, **E = arg_end; I != E; ++I, ++idx) {
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
RVal V = StateMgr.GetRVal(St, *I);
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
if (isa<lval::SymbolVal>(V)) {
|
|
|
|
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
|
2008-04-12 00:23:24 +04:00
|
|
|
RefBindings B = GetRefBindings(StVals);
|
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
if (RefBindings::TreeTy* T = B.SlimFind(Sym)) {
|
2008-04-16 08:28:53 +04:00
|
|
|
B = Update(B, Sym, T->getValue().second, GetArgE(Summ, idx), hasErr);
|
2008-03-12 04:21:45 +03:00
|
|
|
SetRefBindings(StVals, B);
|
2008-04-11 22:40:51 +04:00
|
|
|
|
2008-04-16 08:28:53 +04:00
|
|
|
if (hasErr) {
|
2008-04-11 22:40:51 +04:00
|
|
|
ErrorExpr = *I;
|
2008-04-18 07:39:05 +04:00
|
|
|
ErrorSym = T->getValue().first;
|
2008-04-11 22:40:51 +04:00
|
|
|
break;
|
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
2008-04-12 00:51:02 +04:00
|
|
|
}
|
2008-05-06 02:11:16 +04:00
|
|
|
else if (isa<LVal>(V)) {
|
|
|
|
// Nuke all arguments passed by reference.
|
2008-04-12 00:23:24 +04:00
|
|
|
StateMgr.Unbind(StVals, cast<LVal>(V));
|
2008-04-12 00:51:02 +04:00
|
|
|
}
|
2008-04-23 01:39:21 +04:00
|
|
|
else if (isa<nonlval::LValAsInteger>(V))
|
|
|
|
StateMgr.Unbind(StVals, cast<nonlval::LValAsInteger>(V).getLVal());
|
2008-05-06 02:11:16 +04:00
|
|
|
}
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2008-05-06 06:41:27 +04:00
|
|
|
// Evaluate the effect on the message receiver.
|
|
|
|
|
|
|
|
if (!ErrorExpr && Receiver) {
|
|
|
|
RVal V = StateMgr.GetRVal(St, Receiver);
|
|
|
|
|
|
|
|
if (isa<lval::SymbolVal>(V)) {
|
|
|
|
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
|
|
|
|
RefBindings B = GetRefBindings(StVals);
|
|
|
|
|
|
|
|
if (RefBindings::TreeTy* T = B.SlimFind(Sym)) {
|
|
|
|
B = Update(B, Sym, T->getValue().second, GetReceiverE(Summ), hasErr);
|
|
|
|
SetRefBindings(StVals, B);
|
|
|
|
|
|
|
|
if (hasErr) {
|
|
|
|
ErrorExpr = Receiver;
|
|
|
|
ErrorSym = T->getValue().first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the persistent state.
|
|
|
|
|
2008-04-16 08:28:53 +04:00
|
|
|
St = StateMgr.getPersistentState(StVals);
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-05-06 06:41:27 +04:00
|
|
|
// Process any errors.
|
|
|
|
|
2008-04-16 08:28:53 +04:00
|
|
|
if (hasErr) {
|
2008-05-06 02:11:16 +04:00
|
|
|
ProcessNonLeakError(Dst, Builder, Ex, ErrorExpr, Pred, St,
|
2008-04-18 07:39:05 +04:00
|
|
|
hasErr, ErrorSym);
|
2008-03-12 04:21:45 +03:00
|
|
|
return;
|
2008-03-11 20:48:22 +03:00
|
|
|
}
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
// Finally, consult the summary for the return value.
|
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
RetEffect RE = GetRetEffect(Summ);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
switch (RE.getKind()) {
|
|
|
|
default:
|
|
|
|
assert (false && "Unhandled RetEffect."); break;
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-04-11 03:44:06 +04:00
|
|
|
case RetEffect::NoRet:
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-04-12 00:23:24 +04:00
|
|
|
// Make up a symbol for the return value (not reference counted).
|
2008-04-12 00:51:02 +04:00
|
|
|
// FIXME: This is basically copy-and-paste from GRSimpleVals. We
|
|
|
|
// should compose behavior, not copy it.
|
2008-04-12 00:23:24 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
if (Ex->getType() != Eng.getContext().VoidTy) {
|
2008-04-12 00:23:24 +04:00
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2008-05-06 02:11:16 +04:00
|
|
|
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
|
2008-04-12 00:23:24 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RVal X = Ex->getType()->isPointerType()
|
|
|
|
? cast<RVal>(lval::SymbolVal(Sym))
|
|
|
|
: cast<RVal>(nonlval::SymbolVal(Sym));
|
2008-04-12 00:23:24 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
St = StateMgr.SetRVal(St, Ex, X, Eng.getCFG().isBlkExpr(Ex), false);
|
2008-04-12 00:23:24 +04:00
|
|
|
}
|
|
|
|
|
2008-04-11 03:44:06 +04:00
|
|
|
break;
|
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
case RetEffect::Alias: {
|
|
|
|
unsigned idx = RE.getValue();
|
2008-05-06 02:11:16 +04:00
|
|
|
assert ((arg_end - arg_beg) >= 0);
|
|
|
|
assert (idx < (unsigned) (arg_end - arg_beg));
|
|
|
|
RVal V = StateMgr.GetRVal(St, arg_beg[idx]);
|
|
|
|
St = StateMgr.SetRVal(St, Ex, V, Eng.getCFG().isBlkExpr(Ex), false);
|
2008-03-12 04:21:45 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-05-06 06:41:27 +04:00
|
|
|
case RetEffect::ReceiverAlias: {
|
|
|
|
assert (Receiver);
|
|
|
|
RVal V = StateMgr.GetRVal(St, Receiver);
|
|
|
|
St = StateMgr.SetRVal(St, Ex, V, Eng.getCFG().isBlkExpr(Ex), false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
case RetEffect::OwnedSymbol: {
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2008-05-06 02:11:16 +04:00
|
|
|
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
|
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
ValueState StImpl = *St;
|
|
|
|
RefBindings B = GetRefBindings(StImpl);
|
2008-04-11 03:09:18 +04:00
|
|
|
SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeOwned()));
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
|
2008-05-06 02:11:16 +04:00
|
|
|
Ex, lval::SymbolVal(Sym),
|
|
|
|
Eng.getCFG().isBlkExpr(Ex), false);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RetEffect::NotOwnedSymbol: {
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2008-05-06 02:11:16 +04:00
|
|
|
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
ValueState StImpl = *St;
|
|
|
|
RefBindings B = GetRefBindings(StImpl);
|
|
|
|
SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeNotOwned()));
|
|
|
|
|
|
|
|
St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
|
2008-05-06 02:11:16 +04:00
|
|
|
Ex, lval::SymbolVal(Sym),
|
|
|
|
Eng.getCFG().isBlkExpr(Ex), false);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-05-06 02:11:16 +04:00
|
|
|
|
|
|
|
Builder.MakeNode(Dst, Ex, Pred, St);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Eng,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
CallExpr* CE, RVal L,
|
|
|
|
ExplodedNode<ValueState>* Pred) {
|
|
|
|
|
|
|
|
|
|
|
|
RetainSummary* Summ = NULL;
|
|
|
|
|
|
|
|
// Get the summary.
|
|
|
|
|
|
|
|
if (isa<lval::FuncVal>(L)) {
|
|
|
|
lval::FuncVal FV = cast<lval::FuncVal>(L);
|
|
|
|
FunctionDecl* FD = FV.getDecl();
|
|
|
|
Summ = Summaries.getSummary(FD, Eng.getContext());
|
|
|
|
}
|
|
|
|
|
|
|
|
EvalSummary(Dst, Eng, Builder, CE, 0, Summ,
|
|
|
|
CE->arg_begin(), CE->arg_end(), Pred);
|
2008-03-06 03:08:09 +03:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-04-16 03:44:31 +04:00
|
|
|
|
|
|
|
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Eng,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
ObjCMessageExpr* ME,
|
|
|
|
ExplodedNode<ValueState>* Pred) {
|
|
|
|
|
2008-05-06 08:20:12 +04:00
|
|
|
RetainSummary* Summ;
|
2008-05-02 01:31:50 +04:00
|
|
|
|
2008-05-06 08:20:12 +04:00
|
|
|
if (ME->getReceiver())
|
2008-05-06 19:44:25 +04:00
|
|
|
Summ = Summaries.getMethodSummary(ME);
|
2008-04-16 08:28:53 +04:00
|
|
|
else
|
2008-05-06 08:20:12 +04:00
|
|
|
Summ = Summaries.getInstanceMethodSummary(ME->getClassName(),
|
|
|
|
ME->getSelector());
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2008-05-06 08:20:12 +04:00
|
|
|
EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), Summ,
|
|
|
|
ME->arg_begin(), ME->arg_end(), Pred);
|
2008-04-16 03:44:31 +04:00
|
|
|
}
|
2008-05-06 08:20:12 +04:00
|
|
|
|
2008-04-17 00:40:59 +04:00
|
|
|
// Stores.
|
|
|
|
|
|
|
|
void CFRefCount::EvalStore(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Eng,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
Expr* E, ExplodedNode<ValueState>* Pred,
|
|
|
|
ValueState* St, RVal TargetLV, RVal Val) {
|
|
|
|
|
|
|
|
// Check if we have a binding for "Val" and if we are storing it to something
|
|
|
|
// we don't understand or otherwise the value "escapes" the function.
|
|
|
|
|
|
|
|
if (!isa<lval::SymbolVal>(Val))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Are we storing to something that causes the value to "escape"?
|
|
|
|
|
|
|
|
bool escapes = false;
|
|
|
|
|
|
|
|
if (!isa<lval::DeclVal>(TargetLV))
|
|
|
|
escapes = true;
|
|
|
|
else
|
|
|
|
escapes = cast<lval::DeclVal>(TargetLV).getDecl()->hasGlobalStorage();
|
|
|
|
|
|
|
|
if (!escapes)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SymbolID Sym = cast<lval::SymbolVal>(Val).getSymbol();
|
|
|
|
RefBindings B = GetRefBindings(*St);
|
|
|
|
RefBindings::TreeTy* T = B.SlimFind(Sym);
|
|
|
|
|
|
|
|
if (!T)
|
|
|
|
return;
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
// Nuke the binding.
|
|
|
|
St = NukeBinding(Eng.getStateManager(), St, Sym);
|
2008-04-17 00:40:59 +04:00
|
|
|
|
|
|
|
// Hand of the remaining logic to the parent implementation.
|
|
|
|
GRSimpleVals::EvalStore(Dst, Eng, Builder, E, Pred, St, TargetLV, Val);
|
|
|
|
}
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
|
|
|
|
ValueState* CFRefCount::NukeBinding(ValueStateManager& VMgr, ValueState* St,
|
|
|
|
SymbolID sid) {
|
|
|
|
ValueState StImpl = *St;
|
|
|
|
RefBindings B = GetRefBindings(StImpl);
|
|
|
|
StImpl.CheckerState = RefBFactory.Remove(B, sid).getRoot();
|
|
|
|
return VMgr.getPersistentState(StImpl);
|
|
|
|
}
|
|
|
|
|
2008-04-12 02:25:11 +04:00
|
|
|
// End-of-path.
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
ValueState* CFRefCount::HandleSymbolDeath(ValueStateManager& VMgr,
|
|
|
|
ValueState* St, SymbolID sid,
|
|
|
|
RefVal V, bool& hasLeak) {
|
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
hasLeak = V.isOwned() ||
|
|
|
|
((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0);
|
2008-04-17 02:32:20 +04:00
|
|
|
|
|
|
|
if (!hasLeak)
|
|
|
|
return NukeBinding(VMgr, St, sid);
|
|
|
|
|
|
|
|
RefBindings B = GetRefBindings(*St);
|
|
|
|
ValueState StImpl = *St;
|
2008-05-05 21:53:17 +04:00
|
|
|
|
|
|
|
StImpl.CheckerState =
|
|
|
|
RefBFactory.Add(B, sid, RefVal::makeLeak(GetCount(V))).getRoot();
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
return VMgr.getPersistentState(StImpl);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFRefCount::EvalEndPath(GRExprEngine& Eng,
|
2008-04-12 02:25:11 +04:00
|
|
|
GREndPathNodeBuilder<ValueState>& Builder) {
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
ValueState* St = Builder.getState();
|
|
|
|
RefBindings B = GetRefBindings(*St);
|
2008-04-12 02:25:11 +04:00
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
llvm::SmallVector<SymbolID, 10> Leaked;
|
2008-04-12 02:25:11 +04:00
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
|
|
|
|
bool hasLeak = false;
|
2008-04-12 02:25:11 +04:00
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
St = HandleSymbolDeath(Eng.getStateManager(), St,
|
|
|
|
(*I).first, (*I).second, hasLeak);
|
|
|
|
|
|
|
|
if (hasLeak) Leaked.push_back((*I).first);
|
|
|
|
}
|
2008-04-25 03:57:27 +04:00
|
|
|
|
|
|
|
if (Leaked.empty())
|
|
|
|
return;
|
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
ExplodedNode<ValueState>* N = Builder.MakeNode(St);
|
2008-04-18 20:30:14 +04:00
|
|
|
|
2008-04-25 03:57:27 +04:00
|
|
|
if (!N)
|
2008-04-18 20:30:14 +04:00
|
|
|
return;
|
2008-04-18 23:23:43 +04:00
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
std::vector<SymbolID>*& LeaksAtNode = Leaks[N];
|
|
|
|
assert (!LeaksAtNode);
|
|
|
|
LeaksAtNode = new std::vector<SymbolID>();
|
2008-04-17 02:32:20 +04:00
|
|
|
|
|
|
|
for (llvm::SmallVector<SymbolID, 10>::iterator I=Leaked.begin(),
|
|
|
|
E = Leaked.end(); I != E; ++I)
|
2008-04-18 07:39:05 +04:00
|
|
|
(*LeaksAtNode).push_back(*I);
|
2008-04-12 02:25:11 +04:00
|
|
|
}
|
|
|
|
|
2008-04-25 03:57:27 +04:00
|
|
|
// Dead symbols.
|
|
|
|
|
|
|
|
void CFRefCount::EvalDeadSymbols(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Eng,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
2008-04-25 05:25:15 +04:00
|
|
|
ExplodedNode<ValueState>* Pred,
|
|
|
|
Stmt* S,
|
2008-04-25 03:57:27 +04:00
|
|
|
ValueState* St,
|
|
|
|
const ValueStateManager::DeadSymbolsTy& Dead) {
|
2008-04-25 05:25:15 +04:00
|
|
|
|
2008-04-25 03:57:27 +04:00
|
|
|
// FIXME: a lot of copy-and-paste from EvalEndPath. Refactor.
|
|
|
|
|
|
|
|
RefBindings B = GetRefBindings(*St);
|
|
|
|
llvm::SmallVector<SymbolID, 10> Leaked;
|
|
|
|
|
|
|
|
for (ValueStateManager::DeadSymbolsTy::const_iterator
|
|
|
|
I=Dead.begin(), E=Dead.end(); I!=E; ++I) {
|
|
|
|
|
|
|
|
RefBindings::TreeTy* T = B.SlimFind(*I);
|
|
|
|
|
|
|
|
if (!T)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bool hasLeak = false;
|
|
|
|
|
|
|
|
St = HandleSymbolDeath(Eng.getStateManager(), St,
|
|
|
|
*I, T->getValue().second, hasLeak);
|
|
|
|
|
|
|
|
if (hasLeak) Leaked.push_back(*I);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Leaked.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ExplodedNode<ValueState>* N = Builder.MakeNode(Dst, S, Pred, St);
|
|
|
|
|
|
|
|
if (!N)
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::vector<SymbolID>*& LeaksAtNode = Leaks[N];
|
|
|
|
assert (!LeaksAtNode);
|
|
|
|
LeaksAtNode = new std::vector<SymbolID>();
|
|
|
|
|
|
|
|
for (llvm::SmallVector<SymbolID, 10>::iterator I=Leaked.begin(),
|
|
|
|
E = Leaked.end(); I != E; ++I)
|
|
|
|
(*LeaksAtNode).push_back(*I);
|
|
|
|
}
|
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
// Return statements.
|
|
|
|
|
|
|
|
void CFRefCount::EvalReturn(ExplodedNodeSet<ValueState>& Dst,
|
|
|
|
GRExprEngine& Eng,
|
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
ReturnStmt* S,
|
|
|
|
ExplodedNode<ValueState>* Pred) {
|
|
|
|
|
|
|
|
Expr* RetE = S->getRetValue();
|
|
|
|
if (!RetE) return;
|
|
|
|
|
|
|
|
ValueStateManager& StateMgr = Eng.getStateManager();
|
|
|
|
ValueState* St = Builder.GetState(Pred);
|
|
|
|
RVal V = StateMgr.GetRVal(St, RetE);
|
|
|
|
|
|
|
|
if (!isa<lval::SymbolVal>(V))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Get the reference count binding (if any).
|
|
|
|
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
|
|
|
|
RefBindings B = GetRefBindings(*St);
|
|
|
|
RefBindings::TreeTy* T = B.SlimFind(Sym);
|
|
|
|
|
|
|
|
if (!T)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Change the reference count.
|
|
|
|
|
|
|
|
RefVal X = T->getValue().second;
|
|
|
|
|
|
|
|
switch (X.getKind()) {
|
|
|
|
|
|
|
|
case RefVal::Owned: {
|
|
|
|
unsigned cnt = X.getCount();
|
|
|
|
X = RefVal::makeReturnedOwned(cnt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RefVal::NotOwned: {
|
|
|
|
unsigned cnt = X.getCount();
|
|
|
|
X = cnt ? RefVal::makeReturnedOwned(cnt - 1)
|
|
|
|
: RefVal::makeReturnedNotOwned();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the binding.
|
|
|
|
|
|
|
|
ValueState StImpl = *St;
|
|
|
|
StImpl.CheckerState = RefBFactory.Add(B, Sym, X).getRoot();
|
|
|
|
Builder.MakeNode(Dst, S, Pred, StateMgr.getPersistentState(StImpl));
|
|
|
|
}
|
|
|
|
|
2008-04-18 23:23:43 +04:00
|
|
|
// Assumptions.
|
|
|
|
|
|
|
|
ValueState* CFRefCount::EvalAssume(GRExprEngine& Eng, ValueState* St,
|
|
|
|
RVal Cond, bool Assumption,
|
|
|
|
bool& isFeasible) {
|
|
|
|
|
|
|
|
// FIXME: We may add to the interface of EvalAssume the list of symbols
|
|
|
|
// whose assumptions have changed. For now we just iterate through the
|
|
|
|
// bindings and check if any of the tracked symbols are NULL. This isn't
|
|
|
|
// too bad since the number of symbols we will track in practice are
|
|
|
|
// probably small and EvalAssume is only called at branches and a few
|
|
|
|
// other places.
|
|
|
|
|
|
|
|
RefBindings B = GetRefBindings(*St);
|
|
|
|
|
|
|
|
if (B.isEmpty())
|
|
|
|
return St;
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
|
|
|
|
|
|
|
|
// Check if the symbol is null (or equal to any constant).
|
|
|
|
// If this is the case, stop tracking the symbol.
|
|
|
|
|
|
|
|
if (St->getSymVal(I.getKey())) {
|
|
|
|
changed = true;
|
|
|
|
B = RefBFactory.Remove(B, I.getKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!changed)
|
|
|
|
return St;
|
|
|
|
|
|
|
|
ValueState StImpl = *St;
|
|
|
|
StImpl.CheckerState = B.getRoot();
|
|
|
|
return Eng.getStateManager().getPersistentState(StImpl);
|
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
|
2008-03-11 20:48:22 +03:00
|
|
|
RefVal V, ArgEffect E,
|
2008-04-16 08:28:53 +04:00
|
|
|
RefVal::Kind& hasErr) {
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
// FIXME: This dispatch can potentially be sped up by unifiying it into
|
|
|
|
// a single switch statement. Opt for simplicity for now.
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
switch (E) {
|
|
|
|
default:
|
|
|
|
assert (false && "Unhandled CFRef transition.");
|
|
|
|
|
|
|
|
case DoNothing:
|
2008-05-06 02:11:16 +04:00
|
|
|
if (!isGCEnabled() && V.getKind() == RefVal::Released) {
|
2008-03-12 04:21:45 +03:00
|
|
|
V = RefVal::makeUseAfterRelease();
|
2008-04-16 08:28:53 +04:00
|
|
|
hasErr = V.getKind();
|
2008-03-12 04:21:45 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
return B;
|
|
|
|
|
2008-05-06 06:41:27 +04:00
|
|
|
case StopTracking:
|
|
|
|
return RefBFactory.Remove(B, sym);
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
case IncRef:
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
|
|
|
|
case RefVal::Owned:
|
2008-04-11 03:44:06 +04:00
|
|
|
V = RefVal::makeOwned(V.getCount()+1);
|
|
|
|
break;
|
2008-04-11 03:09:18 +04:00
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
case RefVal::NotOwned:
|
2008-04-11 03:09:18 +04:00
|
|
|
V = RefVal::makeNotOwned(V.getCount()+1);
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::Released:
|
2008-05-06 02:11:16 +04:00
|
|
|
if (isGCEnabled())
|
2008-04-29 09:44:10 +04:00
|
|
|
V = RefVal::makeOwned();
|
|
|
|
else {
|
|
|
|
V = RefVal::makeUseAfterRelease();
|
|
|
|
hasErr = V.getKind();
|
|
|
|
}
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-04-11 03:44:06 +04:00
|
|
|
break;
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
case DecRef:
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
assert (false);
|
|
|
|
|
|
|
|
case RefVal::Owned: {
|
2008-04-17 22:12:53 +04:00
|
|
|
unsigned Count = V.getCount();
|
|
|
|
V = Count > 0 ? RefVal::makeOwned(Count - 1) : RefVal::makeReleased();
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-04-11 03:09:18 +04:00
|
|
|
case RefVal::NotOwned: {
|
2008-04-17 22:12:53 +04:00
|
|
|
unsigned Count = V.getCount();
|
2008-04-11 03:09:18 +04:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
if (Count > 0)
|
|
|
|
V = RefVal::makeNotOwned(Count - 1);
|
2008-04-11 03:09:18 +04:00
|
|
|
else {
|
|
|
|
V = RefVal::makeReleaseNotOwned();
|
2008-04-16 08:28:53 +04:00
|
|
|
hasErr = V.getKind();
|
2008-04-11 03:09:18 +04:00
|
|
|
}
|
2008-03-11 20:48:22 +03:00
|
|
|
|
|
|
|
break;
|
2008-04-11 03:09:18 +04:00
|
|
|
}
|
2008-03-11 20:48:22 +03:00
|
|
|
|
|
|
|
case RefVal::Released:
|
|
|
|
V = RefVal::makeUseAfterRelease();
|
2008-04-16 08:28:53 +04:00
|
|
|
hasErr = V.getKind();
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
|
|
|
}
|
2008-04-11 03:44:06 +04:00
|
|
|
|
|
|
|
break;
|
2008-03-11 20:48:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return RefBFactory.Add(B, sym, V);
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
|
|
|
|
2008-04-09 05:10:13 +04:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2008-04-10 03:49:11 +04:00
|
|
|
// Error reporting.
|
2008-04-09 05:10:13 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
//===-------------===//
|
|
|
|
// Bug Descriptions. //
|
|
|
|
//===-------------===//
|
|
|
|
|
2008-04-19 00:54:29 +04:00
|
|
|
class VISIBILITY_HIDDEN CFRefBug : public BugTypeCacheLocation {
|
2008-04-18 07:39:05 +04:00
|
|
|
protected:
|
|
|
|
CFRefCount& TF;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CFRefBug(CFRefCount& tf) : TF(tf) {}
|
2008-05-01 03:47:44 +04:00
|
|
|
|
2008-05-02 02:50:36 +04:00
|
|
|
CFRefCount& getTF() { return TF; }
|
2008-05-06 03:16:31 +04:00
|
|
|
const CFRefCount& getTF() const { return TF; }
|
|
|
|
|
2008-05-02 03:13:35 +04:00
|
|
|
virtual bool isLeak() const { return false; }
|
2008-04-18 07:39:05 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
class VISIBILITY_HIDDEN UseAfterRelease : public CFRefBug {
|
|
|
|
public:
|
|
|
|
UseAfterRelease(CFRefCount& tf) : CFRefBug(tf) {}
|
|
|
|
|
|
|
|
virtual const char* getName() const {
|
2008-05-06 03:16:31 +04:00
|
|
|
return "Use-After-Release";
|
2008-04-18 07:39:05 +04:00
|
|
|
}
|
|
|
|
virtual const char* getDescription() const {
|
2008-04-18 08:55:01 +04:00
|
|
|
return "Reference-counted object is used"
|
|
|
|
" after it is released.";
|
2008-04-18 07:39:05 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void EmitWarnings(BugReporter& BR);
|
|
|
|
};
|
|
|
|
|
|
|
|
class VISIBILITY_HIDDEN BadRelease : public CFRefBug {
|
|
|
|
public:
|
|
|
|
BadRelease(CFRefCount& tf) : CFRefBug(tf) {}
|
|
|
|
|
|
|
|
virtual const char* getName() const {
|
2008-05-06 03:16:31 +04:00
|
|
|
return "Bad Release";
|
2008-04-18 07:39:05 +04:00
|
|
|
}
|
|
|
|
virtual const char* getDescription() const {
|
|
|
|
return "Incorrect decrement of the reference count of a "
|
2008-04-18 08:55:01 +04:00
|
|
|
"CoreFoundation object: "
|
2008-04-18 07:39:05 +04:00
|
|
|
"The object is not owned at this point by the caller.";
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void EmitWarnings(BugReporter& BR);
|
|
|
|
};
|
|
|
|
|
|
|
|
class VISIBILITY_HIDDEN Leak : public CFRefBug {
|
|
|
|
public:
|
|
|
|
Leak(CFRefCount& tf) : CFRefBug(tf) {}
|
|
|
|
|
|
|
|
virtual const char* getName() const {
|
2008-05-06 22:11:36 +04:00
|
|
|
|
|
|
|
if (getTF().isGCEnabled())
|
|
|
|
return "Memory Leak (GC)";
|
|
|
|
|
|
|
|
if (getTF().getLangOptions().getGCMode() == LangOptions::HybridGC)
|
|
|
|
return "Memory Leak (Hybrid MM, non-GC)";
|
|
|
|
|
|
|
|
assert (getTF().getLangOptions().getGCMode() == LangOptions::NonGC);
|
|
|
|
return "Memory Leak";
|
2008-04-18 07:39:05 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual const char* getDescription() const {
|
2008-04-18 08:55:01 +04:00
|
|
|
return "Object leaked.";
|
2008-04-18 07:39:05 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void EmitWarnings(BugReporter& BR);
|
2008-05-02 02:50:36 +04:00
|
|
|
virtual void GetErrorNodes(std::vector<ExplodedNode<ValueState>*>& Nodes);
|
2008-05-02 03:13:35 +04:00
|
|
|
virtual bool isLeak() const { return true; }
|
2008-04-18 07:39:05 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
//===---------===//
|
|
|
|
// Bug Reports. //
|
|
|
|
//===---------===//
|
|
|
|
|
|
|
|
class VISIBILITY_HIDDEN CFRefReport : public RangedBugReport {
|
|
|
|
SymbolID Sym;
|
|
|
|
public:
|
2008-05-01 03:47:44 +04:00
|
|
|
CFRefReport(CFRefBug& D, ExplodedNode<ValueState> *n, SymbolID sym)
|
2008-04-18 07:39:05 +04:00
|
|
|
: RangedBugReport(D, n), Sym(sym) {}
|
|
|
|
|
|
|
|
virtual ~CFRefReport() {}
|
|
|
|
|
2008-05-02 02:50:36 +04:00
|
|
|
CFRefBug& getBugType() {
|
|
|
|
return (CFRefBug&) RangedBugReport::getBugType();
|
|
|
|
}
|
|
|
|
const CFRefBug& getBugType() const {
|
|
|
|
return (const CFRefBug&) RangedBugReport::getBugType();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void getRanges(BugReporter& BR, const SourceRange*& beg,
|
|
|
|
const SourceRange*& end) {
|
|
|
|
|
2008-05-03 00:53:50 +04:00
|
|
|
if (!getBugType().isLeak())
|
2008-05-02 02:50:36 +04:00
|
|
|
RangedBugReport::getRanges(BR, beg, end);
|
|
|
|
else {
|
|
|
|
beg = 0;
|
|
|
|
end = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-02 03:13:35 +04:00
|
|
|
virtual PathDiagnosticPiece* getEndPath(BugReporter& BR,
|
|
|
|
ExplodedNode<ValueState>* N);
|
|
|
|
|
2008-05-01 03:47:44 +04:00
|
|
|
virtual std::pair<const char**,const char**> getExtraDescriptiveText();
|
2008-04-18 07:39:05 +04:00
|
|
|
|
|
|
|
virtual PathDiagnosticPiece* VisitNode(ExplodedNode<ValueState>* N,
|
|
|
|
ExplodedNode<ValueState>* PrevN,
|
|
|
|
ExplodedGraph<ValueState>& G,
|
|
|
|
BugReporter& BR);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
void CFRefCount::RegisterChecks(GRExprEngine& Eng) {
|
2008-05-02 22:01:49 +04:00
|
|
|
if (EmitStandardWarnings) GRSimpleVals::RegisterChecks(Eng);
|
2008-04-18 07:39:05 +04:00
|
|
|
Eng.Register(new UseAfterRelease(*this));
|
|
|
|
Eng.Register(new BadRelease(*this));
|
|
|
|
Eng.Register(new Leak(*this));
|
|
|
|
}
|
|
|
|
|
2008-05-01 03:47:44 +04:00
|
|
|
|
|
|
|
static const char* Msgs[] = {
|
|
|
|
"Code is compiled in garbage collection only mode" // GC only
|
|
|
|
" (the bug occurs with garbage collection enabled).",
|
|
|
|
|
|
|
|
"Code is compiled without garbage collection.", // No GC.
|
|
|
|
|
|
|
|
"Code is compiled for use with and without garbage collection (GC)."
|
|
|
|
" The bug occurs with GC enabled.", // Hybrid, with GC.
|
|
|
|
|
|
|
|
"Code is compiled for use with and without garbage collection (GC)."
|
|
|
|
" The bug occurs in non-GC mode." // Hyrbird, without GC/
|
|
|
|
};
|
|
|
|
|
|
|
|
std::pair<const char**,const char**> CFRefReport::getExtraDescriptiveText() {
|
|
|
|
CFRefCount& TF = static_cast<CFRefBug&>(getBugType()).getTF();
|
|
|
|
|
|
|
|
switch (TF.getLangOptions().getGCMode()) {
|
|
|
|
default:
|
|
|
|
assert(false);
|
2008-05-01 08:02:04 +04:00
|
|
|
|
|
|
|
case LangOptions::GCOnly:
|
|
|
|
assert (TF.isGCEnabled());
|
|
|
|
return std::make_pair(&Msgs[0], &Msgs[0]+1);
|
2008-05-01 03:47:44 +04:00
|
|
|
|
|
|
|
case LangOptions::NonGC:
|
|
|
|
assert (!TF.isGCEnabled());
|
|
|
|
return std::make_pair(&Msgs[1], &Msgs[1]+1);
|
|
|
|
|
|
|
|
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-18 07:39:05 +04:00
|
|
|
PathDiagnosticPiece* CFRefReport::VisitNode(ExplodedNode<ValueState>* N,
|
|
|
|
ExplodedNode<ValueState>* PrevN,
|
|
|
|
ExplodedGraph<ValueState>& G,
|
|
|
|
BugReporter& BR) {
|
|
|
|
|
|
|
|
// Check if the type state has changed.
|
|
|
|
|
|
|
|
ValueState* PrevSt = PrevN->getState();
|
|
|
|
ValueState* CurrSt = N->getState();
|
|
|
|
|
|
|
|
CFRefCount::RefBindings PrevB = CFRefCount::GetRefBindings(*PrevSt);
|
|
|
|
CFRefCount::RefBindings CurrB = CFRefCount::GetRefBindings(*CurrSt);
|
|
|
|
|
2008-04-18 08:55:01 +04:00
|
|
|
CFRefCount::RefBindings::TreeTy* PrevT = PrevB.SlimFind(Sym);
|
|
|
|
CFRefCount::RefBindings::TreeTy* CurrT = CurrB.SlimFind(Sym);
|
2008-04-18 07:39:05 +04:00
|
|
|
|
2008-04-18 08:55:01 +04:00
|
|
|
if (!CurrT)
|
|
|
|
return NULL;
|
2008-04-18 07:39:05 +04:00
|
|
|
|
2008-04-18 08:55:01 +04:00
|
|
|
const char* Msg = NULL;
|
|
|
|
RefVal CurrV = CurrB.SlimFind(Sym)->getValue().second;
|
2008-05-05 21:53:17 +04:00
|
|
|
|
2008-04-18 08:55:01 +04:00
|
|
|
if (!PrevT) {
|
|
|
|
|
2008-05-05 21:53:17 +04:00
|
|
|
Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
|
|
|
|
|
|
|
if (CurrV.isOwned()) {
|
|
|
|
|
|
|
|
if (isa<CallExpr>(S))
|
|
|
|
Msg = "Function call returns an object with a +1 retain count"
|
|
|
|
" (owning reference).";
|
|
|
|
else {
|
|
|
|
assert (isa<ObjCMessageExpr>(S));
|
|
|
|
Msg = "Method returns an object with a +1 retain count"
|
|
|
|
" (owning reference).";
|
|
|
|
}
|
|
|
|
}
|
2008-04-18 08:55:01 +04:00
|
|
|
else {
|
|
|
|
assert (CurrV.isNotOwned());
|
2008-05-05 21:53:17 +04:00
|
|
|
|
|
|
|
if (isa<CallExpr>(S))
|
|
|
|
Msg = "Function call returns an object with a +0 retain count"
|
|
|
|
" (non-owning reference).";
|
|
|
|
else {
|
|
|
|
assert (isa<ObjCMessageExpr>(S));
|
|
|
|
Msg = "Method returns an object with a +0 retain count"
|
|
|
|
" (non-owning reference).";
|
|
|
|
}
|
2008-04-18 08:55:01 +04:00
|
|
|
}
|
2008-05-05 21:53:17 +04:00
|
|
|
|
2008-04-18 08:55:01 +04:00
|
|
|
FullSourceLoc Pos(S->getLocStart(), BR.getContext().getSourceManager());
|
|
|
|
PathDiagnosticPiece* P = new PathDiagnosticPiece(Pos, Msg);
|
|
|
|
|
|
|
|
if (Expr* Exp = dyn_cast<Expr>(S))
|
|
|
|
P->addRange(Exp->getSourceRange());
|
|
|
|
|
|
|
|
return P;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine if the typestate has changed.
|
|
|
|
|
|
|
|
RefVal PrevV = PrevB.SlimFind(Sym)->getValue().second;
|
|
|
|
|
|
|
|
if (PrevV == CurrV)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// The typestate has changed.
|
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
|
|
|
switch (CurrV.getKind()) {
|
|
|
|
case RefVal::Owned:
|
|
|
|
case RefVal::NotOwned:
|
|
|
|
assert (PrevV.getKind() == CurrV.getKind());
|
|
|
|
|
|
|
|
if (PrevV.getCount() > CurrV.getCount())
|
|
|
|
os << "Reference count decremented.";
|
|
|
|
else
|
|
|
|
os << "Reference count incremented.";
|
|
|
|
|
2008-05-05 21:53:17 +04:00
|
|
|
if (unsigned Count = GetCount(CurrV)) {
|
|
|
|
|
|
|
|
os << " Object has +" << Count;
|
2008-04-18 09:32:44 +04:00
|
|
|
|
2008-05-05 21:53:17 +04:00
|
|
|
if (Count > 1)
|
|
|
|
os << " retain counts.";
|
2008-04-18 09:32:44 +04:00
|
|
|
else
|
2008-05-05 21:53:17 +04:00
|
|
|
os << " retain count.";
|
2008-04-18 09:32:44 +04:00
|
|
|
}
|
2008-04-18 08:55:01 +04:00
|
|
|
|
|
|
|
Msg = os.str().c_str();
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::Released:
|
|
|
|
Msg = "Object released.";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::ReturnedOwned:
|
2008-05-05 21:53:17 +04:00
|
|
|
Msg = "Object returned to caller as owning reference (single retain count"
|
|
|
|
" transferred to caller).";
|
2008-04-18 08:55:01 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::ReturnedNotOwned:
|
2008-05-05 21:53:17 +04:00
|
|
|
Msg = "Object returned to caller with a +0 (non-owning) retain count.";
|
2008-04-18 08:55:01 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
|
|
|
FullSourceLoc Pos(S->getLocStart(), BR.getContext().getSourceManager());
|
|
|
|
PathDiagnosticPiece* P = new PathDiagnosticPiece(Pos, Msg);
|
|
|
|
|
|
|
|
// Add the range by scanning the children of the statement for any bindings
|
|
|
|
// to Sym.
|
|
|
|
|
|
|
|
ValueStateManager& VSM = BR.getEngine().getStateManager();
|
|
|
|
|
|
|
|
for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
|
|
|
|
if (Expr* Exp = dyn_cast_or_null<Expr>(*I)) {
|
|
|
|
RVal X = VSM.GetRVal(CurrSt, Exp);
|
|
|
|
|
|
|
|
if (lval::SymbolVal* SV = dyn_cast<lval::SymbolVal>(&X))
|
|
|
|
if (SV->getSymbol() == Sym) {
|
|
|
|
P->addRange(Exp->getSourceRange()); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return P;
|
2008-04-18 07:39:05 +04:00
|
|
|
}
|
|
|
|
|
2008-05-02 03:13:35 +04:00
|
|
|
PathDiagnosticPiece* CFRefReport::getEndPath(BugReporter& BR,
|
2008-05-05 22:50:19 +04:00
|
|
|
ExplodedNode<ValueState>* EndN) {
|
2008-05-02 03:13:35 +04:00
|
|
|
|
|
|
|
if (!getBugType().isLeak())
|
2008-05-05 22:50:19 +04:00
|
|
|
return RangedBugReport::getEndPath(BR, EndN);
|
2008-05-02 03:13:35 +04:00
|
|
|
|
2008-05-05 21:53:17 +04:00
|
|
|
typedef CFRefCount::RefBindings RefBindings;
|
|
|
|
|
|
|
|
// Get the retain count.
|
|
|
|
unsigned long RetCount = 0;
|
|
|
|
|
|
|
|
{
|
2008-05-05 22:50:19 +04:00
|
|
|
ValueState* St = EndN->getState();
|
2008-05-05 21:53:17 +04:00
|
|
|
RefBindings B = RefBindings((RefBindings::TreeTy*) St->CheckerState);
|
|
|
|
RefBindings::TreeTy* T = B.SlimFind(Sym);
|
|
|
|
assert (T);
|
|
|
|
RetCount = GetCount(T->getValue().second);
|
|
|
|
}
|
|
|
|
|
2008-05-02 03:13:35 +04:00
|
|
|
// We are a leak. Walk up the graph to get to the first node where the
|
|
|
|
// symbol appeared.
|
|
|
|
|
2008-05-05 22:50:19 +04:00
|
|
|
ExplodedNode<ValueState>* N = EndN;
|
2008-05-02 03:13:35 +04:00
|
|
|
ExplodedNode<ValueState>* Last = N;
|
2008-05-05 22:50:19 +04:00
|
|
|
|
2008-05-03 00:53:50 +04:00
|
|
|
// Find the first node that referred to the tracked symbol. We also
|
|
|
|
// try and find the first VarDecl the value was stored to.
|
|
|
|
|
|
|
|
VarDecl* FirstDecl = 0;
|
2008-05-05 22:50:19 +04:00
|
|
|
|
2008-05-02 03:13:35 +04:00
|
|
|
while (N) {
|
|
|
|
ValueState* St = N->getState();
|
|
|
|
RefBindings B = RefBindings((RefBindings::TreeTy*) St->CheckerState);
|
2008-05-03 00:53:50 +04:00
|
|
|
RefBindings::TreeTy* T = B.SlimFind(Sym);
|
|
|
|
|
|
|
|
if (!T)
|
2008-05-02 03:13:35 +04:00
|
|
|
break;
|
2008-05-03 00:53:50 +04:00
|
|
|
|
|
|
|
VarDecl* VD = 0;
|
|
|
|
|
|
|
|
// Determine if there is an LVal binding to the symbol.
|
|
|
|
for (ValueState::vb_iterator I=St->vb_begin(), E=St->vb_end(); I!=E; ++I) {
|
|
|
|
if (!isa<lval::SymbolVal>(I->second) // Is the value a symbol?
|
|
|
|
|| cast<lval::SymbolVal>(I->second).getSymbol() != Sym)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (VD) { // Multiple decls map to this symbol.
|
|
|
|
VD = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
VD = I->first;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VD) FirstDecl = VD;
|
2008-05-02 03:13:35 +04:00
|
|
|
|
|
|
|
Last = N;
|
|
|
|
N = N->pred_empty() ? NULL : *(N->pred_begin());
|
|
|
|
}
|
|
|
|
|
2008-05-05 22:50:19 +04:00
|
|
|
// Get the allocate site.
|
2008-05-02 03:13:35 +04:00
|
|
|
|
|
|
|
assert (Last);
|
|
|
|
Stmt* FirstStmt = cast<PostStmt>(Last->getLocation()).getStmt();
|
|
|
|
|
2008-05-05 22:50:19 +04:00
|
|
|
SourceManager& SMgr = BR.getContext().getSourceManager();
|
|
|
|
unsigned AllocLine = SMgr.getLogicalLineNumber(FirstStmt->getLocStart());
|
|
|
|
|
|
|
|
// Get the leak site. We may have multiple ExplodedNodes (one with the
|
|
|
|
// leak) that occur on the same line number; if the node with the leak
|
|
|
|
// has any immediate predecessor nodes with the same line number, find
|
|
|
|
// any transitive-successors that have a different statement and use that
|
|
|
|
// line number instead. This avoids emiting a diagnostic like:
|
|
|
|
//
|
|
|
|
// // 'y' is leaked.
|
|
|
|
// int x = foo(y);
|
|
|
|
//
|
|
|
|
// instead we want:
|
|
|
|
//
|
|
|
|
// int x = foo(y);
|
|
|
|
// // 'y' is leaked.
|
|
|
|
|
|
|
|
Stmt* S = getStmt(BR); // This is the statement where the leak occured.
|
|
|
|
assert (S);
|
|
|
|
unsigned EndLine = SMgr.getLogicalLineNumber(S->getLocStart());
|
|
|
|
|
|
|
|
// Look in the *trimmed* graph at the immediate predecessor of EndN. Does
|
|
|
|
// it occur on the same line?
|
|
|
|
|
|
|
|
assert (!EndN->pred_empty()); // Not possible to have 0 predecessors.
|
|
|
|
N = *(EndN->pred_begin());
|
|
|
|
|
|
|
|
do {
|
|
|
|
ProgramPoint P = N->getLocation();
|
|
|
|
|
|
|
|
if (!isa<PostStmt>(P))
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Predecessor at same line?
|
|
|
|
|
|
|
|
Stmt* SPred = cast<PostStmt>(P).getStmt();
|
|
|
|
|
|
|
|
if (SMgr.getLogicalLineNumber(SPred->getLocStart()) != EndLine)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// The predecessor (where the object was not yet leaked) is a statement
|
|
|
|
// on the same line. Get the first successor statement that appears
|
|
|
|
// on a different line. For this operation, we can traverse the
|
|
|
|
// non-trimmed graph.
|
|
|
|
|
|
|
|
N = getEndNode(); // This is the node where the leak occured in the
|
|
|
|
// original graph.
|
|
|
|
|
|
|
|
while (!N->succ_empty()) {
|
|
|
|
|
|
|
|
N = *(N->succ_begin());
|
|
|
|
ProgramPoint P = N->getLocation();
|
|
|
|
|
|
|
|
if (!isa<PostStmt>(P))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Stmt* SSucc = cast<PostStmt>(P).getStmt();
|
|
|
|
|
|
|
|
if (SMgr.getLogicalLineNumber(SSucc->getLocStart()) != EndLine) {
|
|
|
|
S = SSucc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (false);
|
|
|
|
|
|
|
|
// Construct the location.
|
|
|
|
|
|
|
|
FullSourceLoc L(S->getLocStart(), SMgr);
|
|
|
|
|
|
|
|
// Generate the diagnostic.
|
2008-05-02 03:13:35 +04:00
|
|
|
std::ostringstream os;
|
2008-05-03 00:53:50 +04:00
|
|
|
|
2008-05-05 22:50:19 +04:00
|
|
|
os << "Object allocated on line " << AllocLine;
|
2008-05-03 00:53:50 +04:00
|
|
|
|
|
|
|
if (FirstDecl)
|
|
|
|
os << " and stored into '" << FirstDecl->getName() << '\'';
|
|
|
|
|
2008-05-05 21:53:17 +04:00
|
|
|
os << " is no longer referenced after this point and has a retain count of +"
|
|
|
|
<< RetCount << " (object leaked).";
|
2008-05-02 03:13:35 +04:00
|
|
|
|
2008-05-05 22:50:19 +04:00
|
|
|
return new PathDiagnosticPiece(L, os.str());
|
2008-05-02 03:13:35 +04:00
|
|
|
}
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
void UseAfterRelease::EmitWarnings(BugReporter& BR) {
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
for (CFRefCount::use_after_iterator I = TF.use_after_begin(),
|
|
|
|
E = TF.use_after_end(); I != E; ++I) {
|
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
CFRefReport report(*this, I->first, I->second.second);
|
|
|
|
report.addRange(I->second.first->getSourceRange());
|
2008-04-18 05:56:37 +04:00
|
|
|
BR.EmitWarning(report);
|
2008-04-09 05:10:13 +04:00
|
|
|
}
|
2008-04-10 03:49:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void BadRelease::EmitWarnings(BugReporter& BR) {
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
for (CFRefCount::bad_release_iterator I = TF.bad_release_begin(),
|
|
|
|
E = TF.bad_release_end(); I != E; ++I) {
|
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
CFRefReport report(*this, I->first, I->second.second);
|
|
|
|
report.addRange(I->second.first->getSourceRange());
|
|
|
|
BR.EmitWarning(report);
|
2008-04-10 03:49:11 +04:00
|
|
|
}
|
|
|
|
}
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-04-18 03:43:50 +04:00
|
|
|
void Leak::EmitWarnings(BugReporter& BR) {
|
|
|
|
|
|
|
|
for (CFRefCount::leaks_iterator I = TF.leaks_begin(),
|
|
|
|
E = TF.leaks_end(); I != E; ++I) {
|
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
std::vector<SymbolID>& SymV = *(I->second);
|
|
|
|
unsigned n = SymV.size();
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < n; ++i) {
|
|
|
|
CFRefReport report(*this, I->first, SymV[i]);
|
|
|
|
BR.EmitWarning(report);
|
|
|
|
}
|
2008-04-18 03:43:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-18 23:23:43 +04:00
|
|
|
void Leak::GetErrorNodes(std::vector<ExplodedNode<ValueState>*>& Nodes) {
|
|
|
|
for (CFRefCount::leaks_iterator I=TF.leaks_begin(), E=TF.leaks_end();
|
|
|
|
I!=E; ++I)
|
|
|
|
Nodes.push_back(I->first);
|
|
|
|
}
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-04-11 02:16:52 +04:00
|
|
|
// Transfer function creation for external clients.
|
2008-03-11 09:39:11 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-05-01 03:47:44 +04:00
|
|
|
GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
|
2008-05-02 22:01:49 +04:00
|
|
|
bool StandardWarnings,
|
2008-05-01 03:47:44 +04:00
|
|
|
const LangOptions& lopts) {
|
2008-05-02 22:01:49 +04:00
|
|
|
return new CFRefCount(Ctx, GCEnabled, StandardWarnings, lopts);
|
2008-04-11 02:58:08 +04:00
|
|
|
}
|