#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include "clang/Basic/JsonSupport.h"
#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
REGISTER_MAP_WITH_PROGRAMSTATE(DynamicTypeMap, const clang::ento::MemRegion *,
clang::ento::DynamicTypeInfo)
REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicCastInfo)
REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *,
CastSet)
REGISTER_MAP_WITH_PROGRAMSTATE(DynamicClassObjectMap, clang::ento::SymbolRef,
clang::ento::DynamicTypeInfo)
namespace clang {
namespace ento {
DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR) {
MR = MR->StripCasts();
if (const DynamicTypeInfo *DTI = State->get<DynamicTypeMap>(MR))
return *DTI;
if (const auto *TR = dyn_cast<TypedRegion>(MR))
return DynamicTypeInfo(TR->getLocationType(), false);
if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) {
SymbolRef Sym = SR->getSymbol();
return DynamicTypeInfo(Sym->getType());
}
return {};
}
const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State,
const MemRegion *MR) {
return State->get<DynamicTypeMap>(MR);
}
static void unbox(QualType &Ty) {
while (Ty->isReferenceType() || Ty->isPointerType())
Ty = Ty->getPointeeType();
Ty = Ty.getCanonicalType().getUnqualifiedType();
}
const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
const MemRegion *MR,
QualType CastFromTy,
QualType CastToTy) {
const auto *Lookup = State->get<DynamicCastMap>().lookup(MR);
if (!Lookup)
return nullptr;
unbox(CastFromTy);
unbox(CastToTy);
for (const DynamicCastInfo &Cast : *Lookup)
if (Cast.equals(CastFromTy, CastToTy))
return &Cast;
return nullptr;
}
DynamicTypeInfo getClassObjectDynamicTypeInfo(ProgramStateRef State,
SymbolRef Sym) {
const DynamicTypeInfo *DTI = State->get<DynamicClassObjectMap>(Sym);
return DTI ? *DTI : DynamicTypeInfo{};
}
ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
DynamicTypeInfo NewTy) {
State = State->set<DynamicTypeMap>(MR->StripCasts(), NewTy);
assert(State);
return State;
}
ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
QualType NewTy, bool CanBeSubClassed) {
return setDynamicTypeInfo(State, MR, DynamicTypeInfo(NewTy, CanBeSubClassed));
}
ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
const MemRegion *MR,
QualType CastFromTy,
QualType CastToTy,
bool CastSucceeds) {
if (!MR)
return State;
if (CastSucceeds) {
assert((CastToTy->isAnyPointerType() || CastToTy->isReferenceType()) &&
"DynamicTypeInfo should always be a pointer.");
State = State->set<DynamicTypeMap>(MR, CastToTy);
}
unbox(CastFromTy);
unbox(CastToTy);
DynamicCastInfo::CastResult ResultKind =
CastSucceeds ? DynamicCastInfo::CastResult::Success
: DynamicCastInfo::CastResult::Failure;
CastSet::Factory &F = State->get_context<CastSet>();
const CastSet *TempSet = State->get<DynamicCastMap>(MR);
CastSet Set = TempSet ? *TempSet : F.getEmptySet();
Set = F.add(Set, {CastFromTy, CastToTy, ResultKind});
State = State->set<DynamicCastMap>(MR, Set);
assert(State);
return State;
}
ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
SymbolRef Sym,
DynamicTypeInfo NewTy) {
State = State->set<DynamicClassObjectMap>(Sym, NewTy);
return State;
}
ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
SymbolRef Sym, QualType NewTy,
bool CanBeSubClassed) {
return setClassObjectDynamicTypeInfo(State, Sym,
DynamicTypeInfo(NewTy, CanBeSubClassed));
}
static bool isLive(SymbolReaper &SR, const MemRegion *MR) {
return SR.isLiveRegion(MR);
}
static bool isLive(SymbolReaper &SR, SymbolRef Sym) { return SR.isLive(Sym); }
template <typename MapTy>
static ProgramStateRef removeDeadImpl(ProgramStateRef State, SymbolReaper &SR) {
const auto &Map = State->get<MapTy>();
for (const auto &Elem : Map)
if (!isLive(SR, Elem.first))
State = State->remove<MapTy>(Elem.first);
return State;
}
ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR) {
return removeDeadImpl<DynamicTypeMap>(State, SR);
}
ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) {
return removeDeadImpl<DynamicCastMap>(State, SR);
}
ProgramStateRef removeDeadClassObjectTypes(ProgramStateRef State,
SymbolReaper &SR) {
return removeDeadImpl<DynamicClassObjectMap>(State, SR);
}
static raw_ostream &printJson(const MemRegion *Region, raw_ostream &Out,
const char *NL, unsigned int Space, bool IsDot) {
return Out << "\"region\": \"" << Region << "\"";
}
static raw_ostream &printJson(const SymExpr *Symbol, raw_ostream &Out,
const char *NL, unsigned int Space, bool IsDot) {
return Out << "\"symbol\": \"" << Symbol << "\"";
}
static raw_ostream &printJson(const DynamicTypeInfo &DTI, raw_ostream &Out,
const char *NL, unsigned int Space, bool IsDot) {
Out << "\"dyn_type\": ";
if (!DTI.isValid()) {
Out << "null";
} else {
QualType ToPrint = DTI.getType();
if (ToPrint->isAnyPointerType())
ToPrint = ToPrint->getPointeeType();
Out << '\"' << ToPrint << "\", \"sub_classable\": "
<< (DTI.canBeASubClass() ? "true" : "false");
}
return Out;
}
static raw_ostream &printJson(const DynamicCastInfo &DCI, raw_ostream &Out,
const char *NL, unsigned int Space, bool IsDot) {
return Out << "\"from\": \"" << DCI.from() << "\", \"to\": \"" << DCI.to()
<< "\", \"kind\": \"" << (DCI.succeeds() ? "success" : "fail")
<< "\"";
}
template <class T, class U>
static raw_ostream &printJson(const std::pair<T, U> &Pair, raw_ostream &Out,
const char *NL, unsigned int Space, bool IsDot) {
printJson(Pair.first, Out, NL, Space, IsDot) << ", ";
return printJson(Pair.second, Out, NL, Space, IsDot);
}
template <class ContainerTy>
static raw_ostream &printJsonContainer(const ContainerTy &Container,
raw_ostream &Out, const char *NL,
unsigned int Space, bool IsDot) {
if (Container.isEmpty()) {
return Out << "null";
}
++Space;
Out << '[' << NL;
for (auto I = Container.begin(); I != Container.end(); ++I) {
const auto &Element = *I;
Indent(Out, Space, IsDot) << "{ ";
printJson(Element, Out, NL, Space, IsDot) << " }";
if (std::next(I) != Container.end())
Out << ',';
Out << NL;
}
--Space;
return Indent(Out, Space, IsDot) << "]";
}
static raw_ostream &printJson(const CastSet &Set, raw_ostream &Out,
const char *NL, unsigned int Space, bool IsDot) {
Out << "\"casts\": ";
return printJsonContainer(Set, Out, NL, Space, IsDot);
}
template <class MapTy>
static void printJsonImpl(raw_ostream &Out, ProgramStateRef State,
const char *Name, const char *NL, unsigned int Space,
bool IsDot, bool PrintEvenIfEmpty = true) {
const auto &Map = State->get<MapTy>();
if (Map.isEmpty() && !PrintEvenIfEmpty)
return;
Indent(Out, Space, IsDot) << "\"" << Name << "\": ";
printJsonContainer(Map, Out, NL, Space, IsDot) << "," << NL;
}
static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
const char *NL, unsigned int Space,
bool IsDot) {
printJsonImpl<DynamicTypeMap>(Out, State, "dynamic_types", NL, Space, IsDot);
}
static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
const char *NL, unsigned int Space,
bool IsDot) {
printJsonImpl<DynamicCastMap>(Out, State, "dynamic_casts", NL, Space, IsDot);
}
static void printClassObjectDynamicTypesJson(raw_ostream &Out,
ProgramStateRef State,
const char *NL, unsigned int Space,
bool IsDot) {
printJsonImpl<DynamicClassObjectMap>(Out, State, "class_object_types", NL,
Space, IsDot,
false);
}
void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
const char *NL, unsigned int Space, bool IsDot) {
printDynamicTypesJson(Out, State, NL, Space, IsDot);
printDynamicCastsJson(Out, State, NL, Space, IsDot);
printClassObjectDynamicTypesJson(Out, State, NL, Space, IsDot);
}
} }