clang 22.0.0git
DynamicType.cpp
Go to the documentation of this file.
1//===- DynamicType.cpp - Dynamic type related APIs --------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines APIs that track and query dynamic type information. This
10// information can be used to devirtualize calls during the symbolic execution
11// or do type checking.
12//
13//===----------------------------------------------------------------------===//
14
17#include "clang/Basic/LLVM.h"
21#include "llvm/Support/raw_ostream.h"
22#include <cassert>
23
24/// The GDM component containing the dynamic type info. This is a map from a
25/// symbol to its most likely type.
28
29/// A set factory of dynamic cast informations.
31
32/// A map from symbols to cast informations.
34 CastSet)
35
36// A map from Class object symbols to the most likely pointed-to type.
37REGISTER_MAP_WITH_PROGRAMSTATE(DynamicClassObjectMap, clang::ento::SymbolRef,
38 clang::ento::DynamicTypeInfo)
39
40namespace clang {
41namespace ento {
42
43DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR) {
44 MR = MR->StripCasts();
45
46 // Look up the dynamic type in the GDM.
47 if (const DynamicTypeInfo *DTI = State->get<DynamicTypeMap>(MR))
48 return *DTI;
49
50 // Otherwise, fall back to what we know about the region.
51 if (const auto *TR = dyn_cast<TypedRegion>(MR))
52 return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false);
53
54 if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) {
55 SymbolRef Sym = SR->getSymbol();
56 return DynamicTypeInfo(Sym->getType());
57 }
58
59 return {};
60}
61
62const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State,
63 const MemRegion *MR) {
64 return State->get<DynamicTypeMap>(MR);
65}
66
67static void unbox(QualType &Ty) {
68 // FIXME: Why are we being fed references to pointers in the first place?
69 while (Ty->isReferenceType() || Ty->isPointerType())
70 Ty = Ty->getPointeeType();
71 Ty = Ty.getCanonicalType().getUnqualifiedType();
72}
73
74const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
75 const MemRegion *MR,
76 QualType CastFromTy,
77 QualType CastToTy) {
78 const auto *Lookup = State->get<DynamicCastMap>().lookup(MR);
79 if (!Lookup)
80 return nullptr;
81
82 unbox(CastFromTy);
83 unbox(CastToTy);
84
85 for (const DynamicCastInfo &Cast : *Lookup)
86 if (Cast.equals(CastFromTy, CastToTy))
87 return &Cast;
88
89 return nullptr;
90}
91
92DynamicTypeInfo getClassObjectDynamicTypeInfo(ProgramStateRef State,
93 SymbolRef Sym) {
94 const DynamicTypeInfo *DTI = State->get<DynamicClassObjectMap>(Sym);
95 return DTI ? *DTI : DynamicTypeInfo{};
96}
97
98ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
99 DynamicTypeInfo NewTy) {
100 State = State->set<DynamicTypeMap>(MR->StripCasts(), NewTy);
101 assert(State);
102 return State;
103}
104
105ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
106 QualType NewTy, bool CanBeSubClassed) {
107 return setDynamicTypeInfo(State, MR, DynamicTypeInfo(NewTy, CanBeSubClassed));
108}
109
110ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
111 const MemRegion *MR,
112 QualType CastFromTy,
113 QualType CastToTy,
114 bool CastSucceeds) {
115 if (!MR)
116 return State;
117
118 if (CastSucceeds) {
119 assert((CastToTy->isAnyPointerType() || CastToTy->isReferenceType()) &&
120 "DynamicTypeInfo should always be a pointer.");
121 State = State->set<DynamicTypeMap>(MR, CastToTy);
122 }
123
124 unbox(CastFromTy);
125 unbox(CastToTy);
126
127 DynamicCastInfo::CastResult ResultKind =
128 CastSucceeds ? DynamicCastInfo::CastResult::Success
129 : DynamicCastInfo::CastResult::Failure;
130
131 CastSet::Factory &F = State->get_context<CastSet>();
132
133 const CastSet *TempSet = State->get<DynamicCastMap>(MR);
134 CastSet Set = TempSet ? *TempSet : F.getEmptySet();
135
136 Set = F.add(Set, {CastFromTy, CastToTy, ResultKind});
137 State = State->set<DynamicCastMap>(MR, Set);
138
139 assert(State);
140 return State;
141}
142
143ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
144 SymbolRef Sym,
145 DynamicTypeInfo NewTy) {
146 State = State->set<DynamicClassObjectMap>(Sym, NewTy);
147 return State;
148}
149
150ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
151 SymbolRef Sym, QualType NewTy,
152 bool CanBeSubClassed) {
153 return setClassObjectDynamicTypeInfo(State, Sym,
154 DynamicTypeInfo(NewTy, CanBeSubClassed));
155}
156
157static bool isLive(SymbolReaper &SR, const MemRegion *MR) {
158 return SR.isLiveRegion(MR);
159}
160
161static bool isLive(SymbolReaper &SR, SymbolRef Sym) { return SR.isLive(Sym); }
162
163template <typename MapTy>
164static ProgramStateRef removeDeadImpl(ProgramStateRef State, SymbolReaper &SR) {
165 const auto &Map = State->get<MapTy>();
166
167 for (const auto &Elem : Map)
168 if (!isLive(SR, Elem.first))
169 State = State->remove<MapTy>(Elem.first);
170
171 return State;
172}
173
174ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR) {
175 return removeDeadImpl<DynamicTypeMap>(State, SR);
176}
177
178ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) {
179 return removeDeadImpl<DynamicCastMap>(State, SR);
180}
181
182ProgramStateRef removeDeadClassObjectTypes(ProgramStateRef State,
183 SymbolReaper &SR) {
184 return removeDeadImpl<DynamicClassObjectMap>(State, SR);
185}
186
187//===----------------------------------------------------------------------===//
188// Implementation of the 'printer-to-JSON' function
189//===----------------------------------------------------------------------===//
190
191static raw_ostream &printJson(const MemRegion *Region, raw_ostream &Out,
192 const char *NL, unsigned int Space, bool IsDot) {
193 return Out << "\"region\": \"" << Region << "\"";
194}
195
196static raw_ostream &printJson(const SymExpr *Symbol, raw_ostream &Out,
197 const char *NL, unsigned int Space, bool IsDot) {
198 return Out << "\"symbol\": \"" << Symbol << "\"";
199}
200
201static raw_ostream &printJson(const DynamicTypeInfo &DTI, raw_ostream &Out,
202 const char *NL, unsigned int Space, bool IsDot) {
203 Out << "\"dyn_type\": ";
204 if (!DTI.isValid()) {
205 Out << "null";
206 } else {
207 QualType ToPrint = DTI.getType();
208 if (ToPrint->isAnyPointerType())
209 ToPrint = ToPrint->getPointeeType();
210
211 Out << '\"' << ToPrint << "\", \"sub_classable\": "
212 << (DTI.canBeASubClass() ? "true" : "false");
213 }
214 return Out;
215}
216
217static raw_ostream &printJson(const DynamicCastInfo &DCI, raw_ostream &Out,
218 const char *NL, unsigned int Space, bool IsDot) {
219 return Out << "\"from\": \"" << DCI.from() << "\", \"to\": \"" << DCI.to()
220 << "\", \"kind\": \"" << (DCI.succeeds() ? "success" : "fail")
221 << "\"";
222}
223
224template <class T, class U>
225static raw_ostream &printJson(const std::pair<T, U> &Pair, raw_ostream &Out,
226 const char *NL, unsigned int Space, bool IsDot) {
227 printJson(Pair.first, Out, NL, Space, IsDot) << ", ";
228 return printJson(Pair.second, Out, NL, Space, IsDot);
229}
230
231template <class ContainerTy>
232static raw_ostream &printJsonContainer(const ContainerTy &Container,
233 raw_ostream &Out, const char *NL,
234 unsigned int Space, bool IsDot) {
235 if (Container.isEmpty()) {
236 return Out << "null";
237 }
238
239 ++Space;
240 Out << '[' << NL;
241 for (auto I = Container.begin(); I != Container.end(); ++I) {
242 const auto &Element = *I;
243
244 Indent(Out, Space, IsDot) << "{ ";
245 printJson(Element, Out, NL, Space, IsDot) << " }";
246
247 if (std::next(I) != Container.end())
248 Out << ',';
249 Out << NL;
250 }
251
252 --Space;
253 return Indent(Out, Space, IsDot) << "]";
254}
255
256static raw_ostream &printJson(const CastSet &Set, raw_ostream &Out,
257 const char *NL, unsigned int Space, bool IsDot) {
258 Out << "\"casts\": ";
259 return printJsonContainer(Set, Out, NL, Space, IsDot);
260}
261
262template <class MapTy>
263static void printJsonImpl(raw_ostream &Out, ProgramStateRef State,
264 const char *Name, const char *NL, unsigned int Space,
265 bool IsDot, bool PrintEvenIfEmpty = true) {
266 const auto &Map = State->get<MapTy>();
267 if (Map.isEmpty() && !PrintEvenIfEmpty)
268 return;
269
270 Indent(Out, Space, IsDot) << "\"" << Name << "\": ";
271 printJsonContainer(Map, Out, NL, Space, IsDot) << "," << NL;
272}
273
274static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
275 const char *NL, unsigned int Space,
276 bool IsDot) {
277 printJsonImpl<DynamicTypeMap>(Out, State, "dynamic_types", NL, Space, IsDot);
278}
279
280static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
281 const char *NL, unsigned int Space,
282 bool IsDot) {
283 printJsonImpl<DynamicCastMap>(Out, State, "dynamic_casts", NL, Space, IsDot);
284}
285
286static void printClassObjectDynamicTypesJson(raw_ostream &Out,
287 ProgramStateRef State,
288 const char *NL, unsigned int Space,
289 bool IsDot) {
290 // Let's print Class object type information only if we have something
291 // meaningful to print.
292 printJsonImpl<DynamicClassObjectMap>(Out, State, "class_object_types", NL,
293 Space, IsDot,
294 /*PrintEvenIfEmpty=*/false);
295}
296
297void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
298 const char *NL, unsigned int Space, bool IsDot) {
299 printDynamicTypesJson(Out, State, NL, Space, IsDot);
300 printDynamicCastsJson(Out, State, NL, Space, IsDot);
301 printClassObjectDynamicTypesJson(Out, State, NL, Space, IsDot);
302}
303
304} // namespace ento
305} // namespace clang
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
llvm::DenseMap< Stmt *, Stmt * > MapTy
Definition ParentMap.cpp:21
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
#define REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set type Name and registers the factory for such sets in the program state,...
Stores the currently inferred strictest bound on the runtime type of a region in a given state along ...
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:98
The JSON file list parser is used to communicate input to InstallAPI.