66#include < swift/Basic/SourceManager.h>
77#include < swift/Parse/Token.h>
88
9- #include " swift/extractor/trap/TrapLabelStore.h"
109#include " swift/extractor/trap/TrapDomain.h"
1110#include " swift/extractor/infra/SwiftTagTraits.h"
1211#include " swift/extractor/trap/generated/TrapClasses.h"
1312#include " swift/extractor/infra/SwiftLocationExtractor.h"
1413#include " swift/extractor/infra/SwiftBodyEmissionStrategy.h"
14+ #include " swift/extractor/infra/SwiftMangledName.h"
1515#include " swift/extractor/config/SwiftExtractorState.h"
1616#include " swift/extractor/infra/log/SwiftLogging.h"
1717
@@ -24,24 +24,31 @@ namespace codeql {
2424// node (AST nodes that are not types: declarations, statements, expressions, etc.).
2525class SwiftDispatcher {
2626 // 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*>;
3939
4040 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&>;
4242
4343 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*>;
4552
4653 public:
4754 // all references and pointers passed as parameters to this constructor are supposed to outlive
@@ -112,7 +119,7 @@ class SwiftDispatcher {
112119 TrapLabel<UnspecifiedElementTag> emitUnspecified (std::optional<TrapLabel<ElementTag>>&& parent,
113120 const char * property,
114121 int index) {
115- UnspecifiedElement entry{trap.createLabel <UnspecifiedElementTag>()};
122+ UnspecifiedElement entry{trap.createTypedLabel <UnspecifiedElementTag>()};
116123 entry.error = " element was unspecified by the extractor" ;
117124 entry.parent = std::move (parent);
118125 entry.property = property;
@@ -135,35 +142,22 @@ class SwiftDispatcher {
135142 // This method gives a TRAP label for already emitted AST node.
136143 // If the AST node was not emitted yet, then the emission is dispatched to a corresponding
137144 // 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 = {} ) {
140147 if constexpr (std::is_constructible_v<bool , const E&>) {
141148 if (!e) {
142149 // this will be treated on emission
143150 return undefined_label;
144151 }
145152 }
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);
153159 }
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);
167161 }
168162
169163 // convenience `fetchLabel` overload for `swift::Type` (which is just a wrapper for
@@ -174,39 +168,28 @@ class SwiftDispatcher {
174168 return fetchLabelFromUnion<AstNodeTag>(node);
175169 }
176170
177- template <typename E, std::enable_if_t <IsStorable <E*>>* = nullptr >
171+ template <typename E, std::enable_if_t <IsFetchable <E*>>* = nullptr >
178172 TrapLabelOf<E> fetchLabel (const E& e) {
179173 return fetchLabel (&e);
180174 }
181175
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-
199176 // 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};
203186 }
204187
205188 // used to create a new entry for entities that should not be cached
206189 // 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>>();
210193 locationExtractor.attachLocation (sourceManager, &e, label);
211194 return TrapClassOf<E>{label};
212195 }
@@ -243,31 +226,65 @@ class SwiftDispatcher {
243226 trap.debug (args...);
244227 }
245228
246- bool shouldEmitDeclBody (const swift::Decl& decl) {
247- encounteredModules.insert (decl.getModuleContext ());
248- return bodyEmissionStrategy.shouldEmitDeclBody (decl);
249- }
250-
251229 void emitComment (swift::Token& comment) {
252- CommentsTrap entry{trap.createLabel <CommentTag>(), comment.getRawText ().str ()};
230+ CommentsTrap entry{trap.createTypedLabel <CommentTag>(), comment.getRawText ().str ()};
253231 trap.emit (entry);
254232 locationExtractor.attachLocation (sourceManager, comment, entry.id );
255233 }
256234
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) {
258276 if (isLazyDeclaration (decl)) {
259- state.emittedDeclarations .insert (& decl);
277+ state.emittedDeclarations .insert (decl);
260278 }
261279 }
262- void skippedDeclaration (const swift::Decl& decl) {
280+ void skippedDeclaration (const swift::Decl* decl) {
263281 if (isLazyDeclaration (decl)) {
264- state.pendingDeclarations .insert (& decl);
282+ state.pendingDeclarations .insert (decl);
265283 }
266284 }
267285
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 ();
271288 return module ->isBuiltinModule () || module ->getName ().str () == " __ObjC" ||
272289 module ->isNonSwiftModule ();
273290 }
@@ -311,6 +328,8 @@ class SwiftDispatcher {
311328 return false ;
312329 }
313330
331+ virtual SwiftMangledName name (const swift::Decl* decl) = 0;
332+ virtual SwiftMangledName name (const swift::TypeBase* type) = 0;
314333 virtual void visit (const swift::Decl* decl) = 0;
315334 virtual void visit (const swift::Stmt* stmt) = 0;
316335 virtual void visit (const swift::StmtCondition* cond) = 0;
@@ -324,13 +343,19 @@ class SwiftDispatcher {
324343 virtual void visit (const swift::TypeBase* type) = 0;
325344 virtual void visit (const swift::CapturedValue* capture) = 0;
326345
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+
327351 const swift::SourceManager& sourceManager;
328352 SwiftExtractorState& state;
329353 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;
331357 SwiftLocationExtractor& locationExtractor;
332358 SwiftBodyEmissionStrategy& bodyEmissionStrategy;
333- Store::Handle waitingForNewLabel{std::monostate{}};
334359 std::unordered_set<swift::ModuleDecl*> encounteredModules;
335360 Logger logger{" dispatcher" };
336361};
0 commit comments