6
6
#include < swift/Basic/SourceManager.h>
7
7
#include < swift/Parse/Token.h>
8
8
9
- #include " swift/extractor/trap/TrapLabelStore.h"
10
9
#include " swift/extractor/trap/TrapDomain.h"
11
10
#include " swift/extractor/infra/SwiftTagTraits.h"
12
11
#include " swift/extractor/trap/generated/TrapClasses.h"
13
12
#include " swift/extractor/infra/SwiftLocationExtractor.h"
14
13
#include " swift/extractor/infra/SwiftBodyEmissionStrategy.h"
14
+ #include " swift/extractor/infra/SwiftMangledName.h"
15
15
#include " swift/extractor/config/SwiftExtractorState.h"
16
16
#include " swift/extractor/infra/log/SwiftLogging.h"
17
17
@@ -24,24 +24,31 @@ namespace codeql {
24
24
// node (AST nodes that are not types: declarations, statements, expressions, etc.).
25
25
class SwiftDispatcher {
26
26
// types to be supported by assignNewLabel/fetchLabel need to be listed here
27
- using Store = TrapLabelStore <const swift::Decl*,
28
- const swift::Stmt*,
29
- const swift::StmtCondition*,
30
- const swift::StmtConditionElement*,
31
- const swift::CaseLabelItem*,
32
- const swift::Expr*,
33
- const swift::Pattern*,
34
- const swift::TypeRepr*,
35
- const swift::TypeBase*,
36
- const swift::CapturedValue*,
37
- const swift::PoundAvailableInfo*,
38
- const swift::AvailabilitySpec*>;
27
+ using Handle = std::variant <const swift::Decl*,
28
+ const swift::Stmt*,
29
+ const swift::StmtCondition*,
30
+ const swift::StmtConditionElement*,
31
+ const swift::CaseLabelItem*,
32
+ const swift::Expr*,
33
+ const swift::Pattern*,
34
+ const swift::TypeRepr*,
35
+ const swift::TypeBase*,
36
+ const swift::CapturedValue*,
37
+ const swift::PoundAvailableInfo*,
38
+ const swift::AvailabilitySpec*>;
39
39
40
40
template <typename E>
41
- static constexpr bool IsStorable = std::is_constructible_v<Store:: Handle, const E&>;
41
+ static constexpr bool IsFetchable = std::is_constructible_v<Handle, const E&>;
42
42
43
43
template <typename E>
44
- static constexpr bool IsLocatable = std::is_base_of_v<LocatableTag, TrapTagOf<E>>;
44
+ static constexpr bool IsLocatable =
45
+ std::is_base_of_v<LocatableTag, TrapTagOf<E>> && !std::is_base_of_v<TypeTag, TrapTagOf<E>>;
46
+
47
+ template <typename E>
48
+ static constexpr bool IsDeclPointer = std::is_convertible_v<E, const swift::Decl*>;
49
+
50
+ template <typename E>
51
+ static constexpr bool IsTypePointer = std::is_convertible_v<E, const swift::TypeBase*>;
45
52
46
53
public:
47
54
// all references and pointers passed as parameters to this constructor are supposed to outlive
@@ -112,7 +119,7 @@ class SwiftDispatcher {
112
119
TrapLabel<UnspecifiedElementTag> emitUnspecified (std::optional<TrapLabel<ElementTag>>&& parent,
113
120
const char * property,
114
121
int index) {
115
- UnspecifiedElement entry{trap.createLabel <UnspecifiedElementTag>()};
122
+ UnspecifiedElement entry{trap.createTypedLabel <UnspecifiedElementTag>()};
116
123
entry.error = " element was unspecified by the extractor" ;
117
124
entry.parent = std::move (parent);
118
125
entry.property = property;
@@ -135,35 +142,22 @@ class SwiftDispatcher {
135
142
// This method gives a TRAP label for already emitted AST node.
136
143
// If the AST node was not emitted yet, then the emission is dispatched to a corresponding
137
144
// visitor (see `visit(T *)` methods below).
138
- template <typename E, typename ... Args, std::enable_if_t <IsStorable <E>>* = nullptr >
139
- TrapLabelOf<E> fetchLabel (const E& e, Args&&... args ) {
145
+ template <typename E, std::enable_if_t <IsFetchable <E>>* = nullptr >
146
+ TrapLabelOf<E> fetchLabel (const E& e, swift::Type type = {} ) {
140
147
if constexpr (std::is_constructible_v<bool , const E&>) {
141
148
if (!e) {
142
149
// this will be treated on emission
143
150
return undefined_label;
144
151
}
145
152
}
146
- // this is required so we avoid any recursive loop: a `fetchLabel` during the visit of `e` might
147
- // end up calling `fetchLabel` on `e` itself, so we want the visit of `e` to call `fetchLabel`
148
- // only after having called `assignNewLabel` on `e`.
149
- assert (std::holds_alternative<std::monostate>(waitingForNewLabel) &&
150
- " fetchLabel called before assignNewLabel" );
151
- if (auto l = store.get (e)) {
152
- return *l;
153
+ auto & stored = store[e];
154
+ if (!stored.valid ()) {
155
+ auto inserted = fetching.insert (e);
156
+ assert (inserted.second && " detected infinite fetchLabel recursion" );
157
+ stored = createLabel (e, type);
158
+ fetching.erase (e);
153
159
}
154
- waitingForNewLabel = e;
155
- // TODO: add tracing logs for visited stuff, maybe within the translators?
156
- visit (e, std::forward<Args>(args)...);
157
- Log::flush ();
158
- // TODO when everything is moved to structured C++ classes, this should be moved to createEntry
159
- if (auto l = store.get (e)) {
160
- if constexpr (IsLocatable<E>) {
161
- locationExtractor.attachLocation (sourceManager, e, *l);
162
- }
163
- return *l;
164
- }
165
- assert (!" assignNewLabel not called during visit" );
166
- return {};
160
+ return TrapLabelOf<E>::unsafeCreateFromUntyped (stored);
167
161
}
168
162
169
163
// convenience `fetchLabel` overload for `swift::Type` (which is just a wrapper for
@@ -174,39 +168,28 @@ class SwiftDispatcher {
174
168
return fetchLabelFromUnion<AstNodeTag>(node);
175
169
}
176
170
177
- template <typename E, std::enable_if_t <IsStorable <E*>>* = nullptr >
171
+ template <typename E, std::enable_if_t <IsFetchable <E*>>* = nullptr >
178
172
TrapLabelOf<E> fetchLabel (const E& e) {
179
173
return fetchLabel (&e);
180
174
}
181
175
182
- // Due to the lazy emission approach, we must assign a label to a corresponding AST node before
183
- // it actually gets emitted to handle recursive cases such as recursive calls, or recursive type
184
- // declarations
185
- template <typename E, typename ... Args, std::enable_if_t <IsStorable<E>>* = nullptr >
186
- TrapLabel<ConcreteTrapTagOf<E>> assignNewLabel (const E& e, Args&&... args) {
187
- assert (waitingForNewLabel == Store::Handle{e} && " assignNewLabel called on wrong entity" );
188
- auto label = trap.createLabel <ConcreteTrapTagOf<E>>(std::forward<Args>(args)...);
189
- store.insert (e, label);
190
- waitingForNewLabel = std::monostate{};
191
- return label;
192
- }
193
-
194
- template <typename E, typename ... Args, std::enable_if_t <IsStorable<E*>>* = nullptr >
195
- TrapLabel<ConcreteTrapTagOf<E>> assignNewLabel (const E& e, Args&&... args) {
196
- return assignNewLabel (&e, std::forward<Args>(args)...);
197
- }
198
-
199
176
// convenience methods for structured C++ creation
200
- template <typename E, typename ... Args>
201
- auto createEntry (const E& e, Args&&... args) {
202
- return TrapClassOf<E>{assignNewLabel (e, std::forward<Args>(args)...)};
177
+ template <typename E>
178
+ auto createEntry (const E& e) {
179
+ auto found = store.find (&e);
180
+ assert (found != store.end () && " createEntry called on non-fetched label" );
181
+ auto label = TrapLabel<ConcreteTrapTagOf<E>>::unsafeCreateFromUntyped (found->second );
182
+ if constexpr (IsLocatable<E>) {
183
+ locationExtractor.attachLocation (sourceManager, e, label);
184
+ }
185
+ return TrapClassOf<E>{label};
203
186
}
204
187
205
188
// used to create a new entry for entities that should not be cached
206
189
// an example is swift::Argument, that are created on the fly and thus have no stable pointer
207
- template <typename E, typename ... Args >
208
- auto createUncachedEntry (const E& e, Args&&... args ) {
209
- auto label = trap.createLabel <TrapTagOf<E>>(std::forward<Args>(args)... );
190
+ template <typename E>
191
+ auto createUncachedEntry (const E& e) {
192
+ auto label = trap.createTypedLabel <TrapTagOf<E>>();
210
193
locationExtractor.attachLocation (sourceManager, &e, label);
211
194
return TrapClassOf<E>{label};
212
195
}
@@ -243,31 +226,65 @@ class SwiftDispatcher {
243
226
trap.debug (args...);
244
227
}
245
228
246
- bool shouldEmitDeclBody (const swift::Decl& decl) {
247
- encounteredModules.insert (decl.getModuleContext ());
248
- return bodyEmissionStrategy.shouldEmitDeclBody (decl);
249
- }
250
-
251
229
void emitComment (swift::Token& comment) {
252
- CommentsTrap entry{trap.createLabel <CommentTag>(), comment.getRawText ().str ()};
230
+ CommentsTrap entry{trap.createTypedLabel <CommentTag>(), comment.getRawText ().str ()};
253
231
trap.emit (entry);
254
232
locationExtractor.attachLocation (sourceManager, comment, entry.id );
255
233
}
256
234
257
- void extractedDeclaration (const swift::Decl& decl) {
235
+ protected:
236
+ void visitPending () {
237
+ while (!toBeVisited.empty ()) {
238
+ auto [next, type] = toBeVisited.back ();
239
+ toBeVisited.pop_back ();
240
+ // TODO: add tracing logs for visited stuff, maybe within the translators?
241
+ std::visit ([this , type = type](const auto * e) { visit (e, type); }, next);
242
+ }
243
+ }
244
+
245
+ private:
246
+ template <typename E>
247
+ UntypedTrapLabel createLabel (const E& e, swift::Type type) {
248
+ if constexpr (IsDeclPointer<E> || IsTypePointer<E>) {
249
+ if (auto mangledName = name (e)) {
250
+ if (shouldVisit (e)) {
251
+ toBeVisited.emplace_back (e, type);
252
+ }
253
+ return trap.createLabel (mangledName);
254
+ }
255
+ }
256
+ // we always need to visit unnamed things
257
+ toBeVisited.emplace_back (e, type);
258
+ return trap.createLabel ();
259
+ }
260
+
261
+ template <typename E>
262
+ bool shouldVisit (const E& e) {
263
+ if constexpr (IsDeclPointer<E>) {
264
+ encounteredModules.insert (e->getModuleContext ());
265
+ if (bodyEmissionStrategy.shouldEmitDeclBody (*e)) {
266
+ extractedDeclaration (e);
267
+ return true ;
268
+ }
269
+ skippedDeclaration (e);
270
+ return false ;
271
+ }
272
+ return true ;
273
+ }
274
+
275
+ void extractedDeclaration (const swift::Decl* decl) {
258
276
if (isLazyDeclaration (decl)) {
259
- state.emittedDeclarations .insert (& decl);
277
+ state.emittedDeclarations .insert (decl);
260
278
}
261
279
}
262
- void skippedDeclaration (const swift::Decl& decl) {
280
+ void skippedDeclaration (const swift::Decl* decl) {
263
281
if (isLazyDeclaration (decl)) {
264
- state.pendingDeclarations .insert (& decl);
282
+ state.pendingDeclarations .insert (decl);
265
283
}
266
284
}
267
285
268
- private:
269
- bool isLazyDeclaration (const swift::Decl& decl) {
270
- swift::ModuleDecl* module = decl.getModuleContext ();
286
+ static bool isLazyDeclaration (const swift::Decl* decl) {
287
+ swift::ModuleDecl* module = decl->getModuleContext ();
271
288
return module ->isBuiltinModule () || module ->getName ().str () == " __ObjC" ||
272
289
module ->isNonSwiftModule ();
273
290
}
@@ -311,6 +328,8 @@ class SwiftDispatcher {
311
328
return false ;
312
329
}
313
330
331
+ virtual SwiftMangledName name (const swift::Decl* decl) = 0;
332
+ virtual SwiftMangledName name (const swift::TypeBase* type) = 0;
314
333
virtual void visit (const swift::Decl* decl) = 0;
315
334
virtual void visit (const swift::Stmt* stmt) = 0;
316
335
virtual void visit (const swift::StmtCondition* cond) = 0;
@@ -324,13 +343,19 @@ class SwiftDispatcher {
324
343
virtual void visit (const swift::TypeBase* type) = 0;
325
344
virtual void visit (const swift::CapturedValue* capture) = 0;
326
345
346
+ template <typename T, std::enable_if<!std::is_base_of_v<swift::TypeRepr, T>>* = nullptr >
347
+ void visit (const T* e, swift::Type) {
348
+ visit (e);
349
+ }
350
+
327
351
const swift::SourceManager& sourceManager;
328
352
SwiftExtractorState& state;
329
353
TrapDomain& trap;
330
- Store store;
354
+ std::unordered_map<Handle, UntypedTrapLabel> store;
355
+ std::unordered_set<Handle> fetching;
356
+ std::vector<std::pair<Handle, swift::Type>> toBeVisited;
331
357
SwiftLocationExtractor& locationExtractor;
332
358
SwiftBodyEmissionStrategy& bodyEmissionStrategy;
333
- Store::Handle waitingForNewLabel{std::monostate{}};
334
359
std::unordered_set<swift::ModuleDecl*> encounteredModules;
335
360
Logger logger{" dispatcher" };
336
361
};
0 commit comments