-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Swift: introduce dispatcher #9112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
AlexDenisov
merged 15 commits into
github:main
from
AlexDenisov:alexdenisov/introduce-dispatcher
May 13, 2022
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
cfd242e
Swift: add human readable string representation for Location and Unko…
AlexDenisov 8f8ece6
Swift: add extractor test for declarations
AlexDenisov d0e2e2b
Swift: introduce SwiftDispatcher
AlexDenisov ccc77fa
Merge branch 'main' into alexdenisov/introduce-dispatcher
redsun82 f52119d
Merge branch 'main' into alexdenisov/introduce-dispatcher
redsun82 efa4565
Swift: move generated code to `generated` directory
AlexDenisov 7b9f886
Swift: describe TrapTagTraits API and implementation
AlexDenisov e584afb
Swift: fix format
AlexDenisov 039aaec
Swift: make TrapLabelStore store untyped label internally
AlexDenisov 2f00945
Swift: change the return types of getCanonicalPointer
AlexDenisov c925766
Swift: change the return types of getCanonicalPointer
AlexDenisov d7f4c6f
Swift: add a comment about lifetime
AlexDenisov 043b1b9
Swift: resolve symlinks
AlexDenisov 35467bc
Swift: rely on llvm::sys::fs::real_path to get absolute path
AlexDenisov 43199fa
Swift: clarify getCanonicalPointer
AlexDenisov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| // generated by {{generator}} | ||
| // generated by {{generator}} from {{source}} | ||
| // clang-format off | ||
| #pragma once | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| // generated by {{generator}} | ||
| // generated by {{generator}} from {{source}} | ||
| // clang-format off | ||
| #pragma once | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| #pragma once | ||
|
|
||
| #include "swift/extractor/trap/TrapArena.h" | ||
| #include "swift/extractor/trap/TrapLabelStore.h" | ||
| #include "swift/extractor/trap/generated/TrapClasses.h" | ||
| #include "swift/extractor/SwiftTagTraits.h" | ||
| #include <swift/AST/SourceFile.h> | ||
| #include <swift/Basic/SourceManager.h> | ||
| #include <llvm/Support/FileSystem.h> | ||
|
|
||
| namespace codeql { | ||
|
|
||
| namespace detail { | ||
|
|
||
| // The following `getKindName`s are used within "TBD" TRAP entries to visually mark an AST node as | ||
| // not properly emitted yet. | ||
| // TODO: To be replaced with QL counterpart | ||
| template <typename Parent, typename Kind> | ||
| inline std::string getKindName(Kind kind) { | ||
| return Parent::getKindName(kind).str(); | ||
| } | ||
|
|
||
| template <> | ||
| inline std::string getKindName<swift::TypeBase, swift::TypeKind>(swift::TypeKind kind) { | ||
| switch (kind) { | ||
| #define TYPE(CLASS, PARENT) \ | ||
| case swift::TypeKind::CLASS: \ | ||
| return #CLASS; | ||
| #include "swift/AST/TypeNodes.def" | ||
| default: | ||
| return "Unknown"; | ||
| } | ||
| } | ||
|
|
||
| template <> | ||
| std::string inline getKindName<swift::TypeRepr, swift::TypeReprKind>(swift::TypeReprKind kind) { | ||
| switch (kind) { | ||
| #define TYPEREPR(CLASS, PARENT) \ | ||
| case swift::TypeReprKind::CLASS: \ | ||
| return #CLASS; | ||
| #include "swift/AST/TypeReprNodes.def" | ||
| default: | ||
| return "Unknown"; | ||
| } | ||
| } | ||
|
|
||
| } // namespace detail | ||
|
|
||
| // The main reponsibilities of the SwiftDispatcher are as follows: | ||
| // * redirect specific AST node emission to a corresponding visitor (statements, expressions, etc.) | ||
| // * storing TRAP labels for emitted AST nodes (in the TrapLabelStore) to avoid re-emission | ||
| // Since SwiftDispatcher sees all the AST nodes, it also attaches a location to every 'locatable' | ||
| // node (AST nodes that are not types: declarations, statements, expressions, etc.). | ||
| class SwiftDispatcher { | ||
| public: | ||
| // sourceManager, arena, and trap are supposed to outlive the SwiftDispatcher | ||
| SwiftDispatcher(const swift::SourceManager& sourceManager, TrapArena& arena, TrapOutput& trap) | ||
| : sourceManager{sourceManager}, arena{arena}, trap{trap} {} | ||
|
|
||
| template <typename T> | ||
| void extract(T* entity) { | ||
| fetchLabel(entity); | ||
| } | ||
|
|
||
| private: | ||
| // This method gives a TRAP label for already emitted AST node. | ||
| // If the AST node was not emitted yet, then the emission is dispatched to a corresponding | ||
| // visitor (see `visit(T *)` methods below). | ||
| template <typename E> | ||
| TrapLabel<ToTag<E>> fetchLabel(E* e) { | ||
| // this is required so we avoid any recursive loop: a `fetchLabel` during the visit of `e` might | ||
| // end up calling `fetchLabel` on `e` itself, so we want the visit of `e` to call `fetchLabel` | ||
| // only after having called `assignNewLabel` on `e` | ||
| assert(!waitingForNewLabel && "fetchLabel called before assignNewLabel"); | ||
| if (auto l = store.get(e)) { | ||
| return *l; | ||
| } | ||
| waitingForNewLabel = getCanonicalPointer(e); | ||
| visit(e); | ||
| if (auto l = store.get(e)) { | ||
| if constexpr (!std::is_base_of_v<swift::TypeBase, E>) { | ||
| attachLocation(e, *l); | ||
| } | ||
| return *l; | ||
| } | ||
| assert(!"assignNewLabel not called during visit"); | ||
| return {}; | ||
| } | ||
|
|
||
| // Due to the lazy emission approach, we must assign a label to a corresponding AST node before | ||
| // it actually gets emitted to handle recursive cases such as recursive calls, or recursive type | ||
| // declarations | ||
| template <typename E> | ||
| TrapLabel<ToTag<E>> assignNewLabel(E* e) { | ||
| assert(waitingForNewLabel == getCanonicalPointer(e) && "assignNewLabel called on wrong entity"); | ||
| auto label = getLabel<ToTag<E>>(); | ||
| trap.assignStar(label); | ||
| store.insert(e, label); | ||
| waitingForNewLabel = nullptr; | ||
| return label; | ||
| } | ||
|
|
||
| template <typename Tag> | ||
| TrapLabel<Tag> getLabel() { | ||
| return arena.allocateLabel<Tag>(); | ||
| } | ||
|
|
||
| // This is a helper method to emit TRAP entries for AST nodes that we don't fully support yet. | ||
| template <typename Parent, typename Child> | ||
AlexDenisov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| void TBD(Child* entity, const std::string& suffix) { | ||
AlexDenisov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| using namespace std::string_literals; | ||
| auto label = assignNewLabel(entity); | ||
| auto kind = detail::getKindName<Parent>(static_cast<const Parent*>(entity)->getKind()); | ||
| auto name = "TBD ("s + kind + suffix + ")"; | ||
| if constexpr (std::is_same_v<Parent, swift::TypeBase>) { | ||
| trap.emit(UnknownTypesTrap{label, name}); | ||
| } else { | ||
| trap.emit(UnknownAstNodesTrap{label, name}); | ||
| } | ||
| } | ||
|
|
||
| template <typename Locatable> | ||
| void attachLocation(Locatable locatable, TrapLabel<LocatableTag> locatableLabel) { | ||
| attachLocation(&locatable, locatableLabel); | ||
| } | ||
|
|
||
| // Emits a Location TRAP entry and attaches it to an AST node | ||
| template <typename Locatable> | ||
| void attachLocation(Locatable* locatable, TrapLabel<LocatableTag> locatableLabel) { | ||
| auto start = locatable->getStartLoc(); | ||
| auto end = locatable->getEndLoc(); | ||
| if (!start.isValid() || !end.isValid()) { | ||
| // invalid locations seem to come from entities synthesized by the compiler | ||
| return; | ||
| } | ||
| std::string filepath = getFilepath(start); | ||
| auto fileLabel = arena.allocateLabel<FileTag>(); | ||
| trap.assignKey(fileLabel, filepath); | ||
| // TODO: do not emit duplicate trap entries for Files | ||
| trap.emit(FilesTrap{fileLabel, filepath}); | ||
| auto [startLine, startColumn] = sourceManager.getLineAndColumnInBuffer(start); | ||
| auto [endLine, endColumn] = sourceManager.getLineAndColumnInBuffer(end); | ||
| auto locLabel = arena.allocateLabel<LocationTag>(); | ||
| trap.assignKey(locLabel, '{', fileLabel, "}:", startLine, ':', startColumn, ':', endLine, ':', | ||
| endColumn); | ||
| trap.emit(LocationsTrap{locLabel, fileLabel, startLine, startColumn, endLine, endColumn}); | ||
| trap.emit(LocatablesTrap{locatableLabel, locLabel}); | ||
| } | ||
|
|
||
| std::string getFilepath(swift::SourceLoc loc) { | ||
| // TODO: this needs more testing | ||
| // TODO: check canonicaliztion of names on a case insensitive filesystems | ||
| // TODO: make symlink resolution conditional on CODEQL_PRESERVE_SYMLINKS=true | ||
| auto displayName = sourceManager.getDisplayNameForLoc(loc); | ||
| llvm::SmallString<PATH_MAX> realPath; | ||
| if (std::error_code ec = llvm::sys::fs::real_path(displayName, realPath)) { | ||
| std::cerr << "Cannot get real path: '" << displayName.str() << "': " << ec.message() << "\n"; | ||
| return {}; | ||
| } | ||
| return realPath.str().str(); | ||
| } | ||
|
|
||
| // TODO: The following methods are supposed to redirect TRAP emission to correpsonding visitors, | ||
| // which are to be introduced in follow-up PRs | ||
| void visit(swift::Decl* decl) { TBD<swift::Decl>(decl, "Decl"); } | ||
| void visit(swift::Stmt* stmt) { TBD<swift::Stmt>(stmt, "Stmt"); } | ||
| void visit(swift::Expr* expr) { TBD<swift::Expr>(expr, "Expr"); } | ||
| void visit(swift::Pattern* pattern) { TBD<swift::Pattern>(pattern, "Pattern"); } | ||
| void visit(swift::TypeRepr* type) { TBD<swift::TypeRepr>(type, "TypeRepr"); } | ||
| void visit(swift::TypeBase* type) { TBD<swift::TypeBase>(type, "Type"); } | ||
|
|
||
| const swift::SourceManager& sourceManager; | ||
| TrapArena& arena; | ||
| TrapOutput& trap; | ||
| TrapLabelStore store; | ||
| const void* waitingForNewLabel{nullptr}; | ||
| }; | ||
|
|
||
| } // namespace codeql | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| #pragma once | ||
AlexDenisov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // This file implements the mapping needed by the API defined in the TrapTagTraits.h | ||
| #include <swift/AST/ASTVisitor.h> | ||
| #include "swift/extractor/trap/TrapTagTraits.h" | ||
| #include "swift/extractor/trap/generated/TrapTags.h" | ||
|
|
||
| namespace codeql { | ||
|
|
||
| // codegen goes with QL acronym convention (Sil instead of SIL), we need to remap it to Swift's | ||
| // convention | ||
| using SILBlockStorageTypeTag = SilBlockStorageTypeTag; | ||
| using SILBoxTypeTag = SilBoxTypeTag; | ||
| using SILFunctionTypeTag = SilFunctionTypeTag; | ||
| using SILTokenTypeTag = SilTokenTypeTag; | ||
|
|
||
| #define MAP_TYPE_TO_TAG(TYPE, TAG) \ | ||
| template <> \ | ||
| struct detail::ToTagFunctor<swift::TYPE> { \ | ||
| using type = TAG; \ | ||
| } | ||
| #define MAP_TAG(TYPE) MAP_TYPE_TO_TAG(TYPE, TYPE##Tag) | ||
| #define MAP_SUBTAG(TYPE, PARENT) \ | ||
| MAP_TAG(TYPE); \ | ||
| static_assert(std::is_base_of_v<PARENT##Tag, TYPE##Tag>, \ | ||
| #PARENT "Tag must be a base of " #TYPE "Tag"); | ||
|
|
||
| #define OVERRIDE_TAG(TYPE, TAG) \ | ||
| template <> \ | ||
| struct detail::ToTagOverride<swift::TYPE> { \ | ||
| using type = TAG; \ | ||
| }; \ | ||
| static_assert(std::is_base_of_v<TYPE##Tag, TAG>, "override is not a subtag"); | ||
|
|
||
| MAP_TAG(Stmt); | ||
| #define ABSTRACT_STMT(CLASS, PARENT) MAP_SUBTAG(CLASS##Stmt, PARENT) | ||
| #define STMT(CLASS, PARENT) ABSTRACT_STMT(CLASS, PARENT) | ||
| #include "swift/AST/StmtNodes.def" | ||
|
|
||
| MAP_TAG(Expr); | ||
| #define ABSTRACT_EXPR(CLASS, PARENT) MAP_SUBTAG(CLASS##Expr, PARENT) | ||
| #define EXPR(CLASS, PARENT) ABSTRACT_EXPR(CLASS, PARENT) | ||
| #include "swift/AST/ExprNodes.def" | ||
|
|
||
| MAP_TAG(Decl); | ||
| #define ABSTRACT_DECL(CLASS, PARENT) MAP_SUBTAG(CLASS##Decl, PARENT) | ||
| #define DECL(CLASS, PARENT) ABSTRACT_DECL(CLASS, PARENT) | ||
| #include "swift/AST/DeclNodes.def" | ||
|
|
||
| MAP_TAG(Pattern); | ||
| #define ABSTRACT_PATTERN(CLASS, PARENT) MAP_SUBTAG(CLASS##Pattern, PARENT) | ||
| #define PATTERN(CLASS, PARENT) ABSTRACT_PATTERN(CLASS, PARENT) | ||
| #include "swift/AST/PatternNodes.def" | ||
|
|
||
| MAP_TAG(TypeRepr); | ||
| MAP_TYPE_TO_TAG(TypeBase, TypeTag); | ||
| #define ABSTRACT_TYPE(CLASS, PARENT) MAP_SUBTAG(CLASS##Type, PARENT) | ||
| #define TYPE(CLASS, PARENT) ABSTRACT_TYPE(CLASS, PARENT) | ||
| #include "swift/AST/TypeNodes.def" | ||
|
|
||
| OVERRIDE_TAG(FuncDecl, ConcreteFuncDeclTag); | ||
| OVERRIDE_TAG(VarDecl, ConcreteVarDeclTag); | ||
|
|
||
| #undef MAP_TAG | ||
| #undef MAP_SUBTAG | ||
| #undef MAP_TYPE_TO_TAG | ||
| #undef OVERRIDE_TAG | ||
|
|
||
| // All the other macros defined here are undefined by the .def files | ||
|
|
||
| } // namespace codeql | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.