aboutsummaryrefslogtreecommitdiffstats
path: root/lib/StaticAnalyzer/Core/DynamicType.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Core/DynamicType.cpp')
-rw-r--r--lib/StaticAnalyzer/Core/DynamicType.cpp229
1 files changed, 229 insertions, 0 deletions
diff --git a/lib/StaticAnalyzer/Core/DynamicType.cpp b/lib/StaticAnalyzer/Core/DynamicType.cpp
new file mode 100644
index 000000000000..a78e0e05e903
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/DynamicType.cpp
@@ -0,0 +1,229 @@
+//===- DynamicType.cpp - Dynamic type related APIs --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines APIs that track and query dynamic type information. This
+// information can be used to devirtualize calls during the symbolic execution
+// or do type checking.
+//
+//===----------------------------------------------------------------------===//
+
+#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>
+
+/// The GDM component containing the dynamic type info. This is a map from a
+/// symbol to its most likely type.
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicTypeMap, const clang::ento::MemRegion *,
+ clang::ento::DynamicTypeInfo)
+
+/// A set factory of dynamic cast informations.
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicCastInfo)
+
+/// A map from symbols to cast informations.
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *,
+ CastSet)
+
+namespace clang {
+namespace ento {
+
+DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR) {
+ MR = MR->StripCasts();
+
+ // Look up the dynamic type in the GDM.
+ if (const DynamicTypeInfo *DTI = State->get<DynamicTypeMap>(MR))
+ return *DTI;
+
+ // Otherwise, fall back to what we know about the region.
+ if (const auto *TR = dyn_cast<TypedRegion>(MR))
+ return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/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);
+}
+
+const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
+ const MemRegion *MR,
+ QualType CastFromTy,
+ QualType CastToTy) {
+ const auto *Lookup = State->get<DynamicCastMap>().lookup(MR);
+ if (!Lookup)
+ return nullptr;
+
+ for (const DynamicCastInfo &Cast : *Lookup)
+ if (Cast.equals(CastFromTy, CastToTy))
+ return &Cast;
+
+ return nullptr;
+}
+
+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);
+ }
+
+ 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;
+}
+
+template <typename MapTy>
+ProgramStateRef removeDead(ProgramStateRef State, const MapTy &Map,
+ SymbolReaper &SR) {
+ for (const auto &Elem : Map)
+ if (!SR.isLiveRegion(Elem.first))
+ State = State->remove<DynamicCastMap>(Elem.first);
+
+ return State;
+}
+
+ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR) {
+ return removeDead(State, State->get<DynamicTypeMap>(), SR);
+}
+
+ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) {
+ return removeDead(State, State->get<DynamicCastMap>(), SR);
+}
+
+static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, unsigned int Space,
+ bool IsDot) {
+ Indent(Out, Space, IsDot) << "\"dynamic_types\": ";
+
+ const DynamicTypeMapTy &Map = State->get<DynamicTypeMap>();
+ if (Map.isEmpty()) {
+ Out << "null," << NL;
+ return;
+ }
+
+ ++Space;
+ Out << '[' << NL;
+ for (DynamicTypeMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
+ const MemRegion *MR = I->first;
+ const DynamicTypeInfo &DTI = I->second;
+ Indent(Out, Space, IsDot)
+ << "{ \"region\": \"" << MR << "\", \"dyn_type\": ";
+ if (!DTI.isValid()) {
+ Out << "null";
+ } else {
+ Out << '\"' << DTI.getType()->getPointeeType().getAsString()
+ << "\", \"sub_classable\": "
+ << (DTI.canBeASubClass() ? "true" : "false");
+ }
+ Out << " }";
+
+ if (std::next(I) != Map.end())
+ Out << ',';
+ Out << NL;
+ }
+
+ --Space;
+ Indent(Out, Space, IsDot) << "]," << NL;
+}
+
+static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, unsigned int Space,
+ bool IsDot) {
+ Indent(Out, Space, IsDot) << "\"dynamic_casts\": ";
+
+ const DynamicCastMapTy &Map = State->get<DynamicCastMap>();
+ if (Map.isEmpty()) {
+ Out << "null," << NL;
+ return;
+ }
+
+ ++Space;
+ Out << '[' << NL;
+ for (DynamicCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
+ const MemRegion *MR = I->first;
+ const CastSet &Set = I->second;
+
+ Indent(Out, Space, IsDot) << "{ \"region\": \"" << MR << "\", \"casts\": ";
+ if (Set.isEmpty()) {
+ Out << "null ";
+ } else {
+ ++Space;
+ Out << '[' << NL;
+ for (CastSet::iterator SI = Set.begin(); SI != Set.end(); ++SI) {
+ Indent(Out, Space, IsDot)
+ << "{ \"from\": \"" << SI->from().getAsString() << "\", \"to\": \""
+ << SI->to().getAsString() << "\", \"kind\": \""
+ << (SI->succeeds() ? "success" : "fail") << "\" }";
+
+ if (std::next(SI) != Set.end())
+ Out << ',';
+ Out << NL;
+ }
+ --Space;
+ Indent(Out, Space, IsDot) << ']';
+ }
+ Out << '}';
+
+ if (std::next(I) != Map.end())
+ Out << ',';
+ Out << NL;
+ }
+
+ --Space;
+ Indent(Out, Space, IsDot) << "]," << NL;
+}
+
+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);
+}
+
+} // namespace ento
+} // namespace clang