Skip to content

Commit ded0e57

Browse files
committed
Swift: extract lazy declarations
1 parent fed504c commit ded0e57

11 files changed

+97
-111
lines changed

swift/extractor/SwiftBuiltinSymbols.h

Lines changed: 0 additions & 73 deletions
This file was deleted.

swift/extractor/SwiftExtractor.cpp

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010

1111
#include "swift/extractor/translators/SwiftVisitor.h"
1212
#include "swift/extractor/infra/TargetDomains.h"
13-
#include "swift/extractor/SwiftBuiltinSymbols.h"
1413
#include "swift/extractor/infra/file/Path.h"
1514
#include "swift/extractor/infra/SwiftLocationExtractor.h"
1615
#include "swift/extractor/infra/SwiftBodyEmissionStrategy.h"
16+
#include "swift/extractor/mangler/SwiftMangler.h"
1717

1818
using namespace codeql;
1919
using namespace std::string_literals;
@@ -43,10 +43,16 @@ static void archiveFile(const SwiftExtractorConfiguration& config, swift::Source
4343
}
4444
}
4545

46-
static fs::path getFilename(swift::ModuleDecl& module, swift::SourceFile* primaryFile) {
46+
static fs::path getFilename(swift::ModuleDecl& module,
47+
swift::SourceFile* primaryFile,
48+
const swift::Decl* lazyDeclaration) {
4749
if (primaryFile) {
4850
return resolvePath(primaryFile->getFilename());
4951
}
52+
if (lazyDeclaration) {
53+
static SwiftMangler mangler;
54+
return mangler.mangledName(*lazyDeclaration);
55+
}
5056
// PCM clang module
5157
if (module.isNonSwiftModule()) {
5258
// Several modules with different names might come from .pcm (clang module) files
@@ -72,57 +78,48 @@ static fs::path getFilename(swift::ModuleDecl& module, swift::SourceFile* primar
7278
return resolvePath(filename);
7379
}
7480

75-
/* The builtin module is special, as it does not publish any top-level declaration
76-
* It creates (and caches) declarations on demand when a lookup is carried out
77-
* (see BuiltinUnit in swift/AST/FileUnit.h for the cache details, and getBuiltinValueDecl in
78-
* swift/AST/Builtins.h for the creation details)
79-
* As we want to create the Builtin trap file once and for all so that it works for other
80-
* extraction runs, rather than collecting what we need we pre-populate the builtin trap with
81-
* what we expect. This list might need thus to be expanded.
82-
* Notice, that while swift/AST/Builtins.def has a list of builtin symbols, it does not contain
83-
* all information required to instantiate builtin variants.
84-
* Other possible approaches:
85-
* * create one trap per builtin declaration when encountered
86-
* * expand the list to all possible builtins (of which there are a lot)
87-
*/
88-
static void getBuiltinDecls(swift::ModuleDecl& builtinModule,
89-
llvm::SmallVector<swift::Decl*>& decls) {
90-
llvm::SmallVector<swift::ValueDecl*> values;
91-
for (auto symbol : swiftBuiltins) {
92-
builtinModule.lookupValue(builtinModule.getASTContext().getIdentifier(symbol),
93-
swift::NLKind::QualifiedLookup, values);
94-
}
95-
decls.insert(decls.end(), values.begin(), values.end());
96-
}
97-
9881
static llvm::SmallVector<swift::Decl*> getTopLevelDecls(swift::ModuleDecl& module,
99-
swift::SourceFile* primaryFile = nullptr) {
82+
swift::SourceFile* primaryFile,
83+
const swift::Decl* lazyDeclaration) {
10084
llvm::SmallVector<swift::Decl*> ret;
85+
if (lazyDeclaration) {
86+
ret.push_back(const_cast<swift::Decl*>(lazyDeclaration));
87+
return ret;
88+
}
10189
ret.push_back(&module);
10290
if (primaryFile) {
10391
primaryFile->getTopLevelDecls(ret);
104-
} else if (module.isBuiltinModule()) {
105-
getBuiltinDecls(module, ret);
10692
} else {
10793
module.getTopLevelDecls(ret);
10894
}
10995
return ret;
11096
}
11197

98+
static TrapType getTrapType(swift::SourceFile* primaryFile, const swift::Decl* lazyDeclaration) {
99+
if (primaryFile) {
100+
return TrapType::source;
101+
}
102+
if (lazyDeclaration) {
103+
return TrapType::lazy_declarations;
104+
}
105+
return TrapType::module;
106+
}
107+
112108
static std::unordered_set<swift::ModuleDecl*> extractDeclarations(
113109
SwiftExtractorState& state,
114110
swift::CompilerInstance& compiler,
115111
swift::ModuleDecl& module,
116-
swift::SourceFile* primaryFile = nullptr) {
117-
auto filename = getFilename(module, primaryFile);
112+
swift::SourceFile* primaryFile,
113+
const swift::Decl* lazyDeclaration) {
114+
auto filename = getFilename(module, primaryFile, lazyDeclaration);
118115
if (primaryFile) {
119116
state.sourceFiles.push_back(filename);
120117
}
121118

122119
// The extractor can be called several times from different processes with
123120
// the same input file(s). Using `TargetFile` the first process will win, and the following
124121
// will just skip the work
125-
const auto trapType = primaryFile ? TrapType::source : TrapType::module;
122+
const auto trapType = getTrapType(primaryFile, lazyDeclaration);
126123
auto trap = createTargetTrapDomain(state, filename, trapType);
127124
if (!trap) {
128125
// another process arrived first, nothing to do for us
@@ -143,9 +140,10 @@ static std::unordered_set<swift::ModuleDecl*> extractDeclarations(
143140

144141
SwiftLocationExtractor locationExtractor(*trap);
145142
locationExtractor.emitFile(primaryFile);
146-
SwiftBodyEmissionStrategy bodyEmissionStrategy(module, primaryFile);
147-
SwiftVisitor visitor(compiler.getSourceMgr(), *trap, locationExtractor, bodyEmissionStrategy);
148-
auto topLevelDecls = getTopLevelDecls(module, primaryFile);
143+
SwiftBodyEmissionStrategy bodyEmissionStrategy(module, primaryFile, lazyDeclaration);
144+
SwiftVisitor visitor(compiler.getSourceMgr(), state, *trap, locationExtractor,
145+
bodyEmissionStrategy);
146+
auto topLevelDecls = getTopLevelDecls(module, primaryFile, lazyDeclaration);
149147
for (auto decl : topLevelDecls) {
150148
visitor.extract(decl);
151149
}
@@ -198,10 +196,10 @@ void codeql::extractSwiftFiles(SwiftExtractorState& state, swift::CompilerInstan
198196
continue;
199197
}
200198
archiveFile(state.configuration, *sourceFile);
201-
encounteredModules = extractDeclarations(state, compiler, *module, sourceFile);
199+
encounteredModules = extractDeclarations(state, compiler, *module, sourceFile, nullptr);
202200
}
203201
if (!isFromSourceFile) {
204-
encounteredModules = extractDeclarations(state, compiler, *module);
202+
encounteredModules = extractDeclarations(state, compiler, *module, nullptr, nullptr);
205203
}
206204
for (auto encountered : encounteredModules) {
207205
if (state.encounteredModules.count(encountered) == 0) {
@@ -211,3 +209,40 @@ void codeql::extractSwiftFiles(SwiftExtractorState& state, swift::CompilerInstan
211209
}
212210
}
213211
}
212+
213+
static void cleanupPendingDeclarations(SwiftExtractorState& state) {
214+
std::vector<const swift::Decl*> worklist;
215+
std::copy(std::begin(state.pendingDeclarations), std::end(state.pendingDeclarations),
216+
std::back_inserter(worklist));
217+
218+
for (auto decl : worklist) {
219+
if (state.emittedDeclarations.count(decl)) {
220+
state.pendingDeclarations.erase(decl);
221+
}
222+
}
223+
}
224+
225+
static void extractLazy(SwiftExtractorState& state, swift::CompilerInstance& compiler) {
226+
cleanupPendingDeclarations(state);
227+
std::vector<const swift::Decl*> worklist;
228+
std::copy(std::begin(state.pendingDeclarations), std::end(state.pendingDeclarations),
229+
std::back_inserter(worklist));
230+
231+
for (auto pending : worklist) {
232+
extractDeclarations(state, compiler, *pending->getModuleContext(), nullptr, pending);
233+
}
234+
}
235+
236+
void codeql::extractExtractLazyDeclarations(SwiftExtractorState& state,
237+
swift::CompilerInstance& compiler) {
238+
// Just in case
239+
const int upperBound = 100;
240+
int iteration = 0;
241+
while (!state.pendingDeclarations.empty() && iteration++ < upperBound) {
242+
extractLazy(state, compiler);
243+
}
244+
if (iteration >= upperBound) {
245+
std::cerr << "Swift extractor reach upper bound while extracting lazy declarations\n";
246+
abort();
247+
}
248+
}

swift/extractor/SwiftExtractor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77

88
namespace codeql {
99
void extractSwiftFiles(SwiftExtractorState& state, swift::CompilerInstance& compiler);
10+
void extractExtractLazyDeclarations(SwiftExtractorState& state, swift::CompilerInstance& compiler);
1011
} // namespace codeql

swift/extractor/config/SwiftExtractorState.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ struct SwiftExtractorState {
2424

2525
// The path for the modules outputted by the underlying frontend run, ignoring path redirection
2626
std::vector<std::filesystem::path> originalOutputModules;
27+
28+
std::unordered_set<const swift::Decl*> emittedDeclarations;
29+
std::unordered_set<const swift::Decl*> pendingDeclarations;
2730
};
2831

2932
} // namespace codeql

swift/extractor/infra/SwiftBodyEmissionStrategy.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ bool SwiftBodyEmissionStrategy::shouldEmitDeclBody(const swift::Decl& decl) {
1616
if (module != &currentModule) {
1717
return false;
1818
}
19+
if (currentLazyDeclaration && currentLazyDeclaration != &decl) {
20+
return false;
21+
}
1922
// ModuleDecl is a special case: if it passed the previous test, it is the current module
2023
// but it never has a source file, so we short circuit to emit it in any case
2124
if (!currentPrimarySourceFile || decl.getKind() == swift::DeclKind::Module) {

swift/extractor/infra/SwiftBodyEmissionStrategy.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ namespace codeql {
88
class SwiftBodyEmissionStrategy {
99
public:
1010
SwiftBodyEmissionStrategy(swift::ModuleDecl& currentModule,
11-
swift::SourceFile* currentPrimarySourceFile)
12-
: currentModule(currentModule), currentPrimarySourceFile(currentPrimarySourceFile) {}
11+
swift::SourceFile* currentPrimarySourceFile,
12+
const swift::Decl* currentLazyDeclaration)
13+
: currentModule(currentModule),
14+
currentPrimarySourceFile(currentPrimarySourceFile),
15+
currentLazyDeclaration(currentLazyDeclaration) {}
1316
bool shouldEmitDeclBody(const swift::Decl& decl);
1417

1518
private:
1619
swift::ModuleDecl& currentModule;
1720
swift::SourceFile* currentPrimarySourceFile;
21+
const swift::Decl* currentLazyDeclaration;
1822
};
1923

2024
} // namespace codeql

swift/extractor/infra/SwiftDispatcher.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "swift/extractor/trap/generated/TrapClasses.h"
1313
#include "swift/extractor/infra/SwiftLocationExtractor.h"
1414
#include "swift/extractor/infra/SwiftBodyEmissionStrategy.h"
15+
#include "swift/extractor/config/SwiftExtractorState.h"
1516

1617
namespace codeql {
1718

@@ -45,10 +46,12 @@ class SwiftDispatcher {
4546
// all references and pointers passed as parameters to this constructor are supposed to outlive
4647
// the SwiftDispatcher
4748
SwiftDispatcher(const swift::SourceManager& sourceManager,
49+
SwiftExtractorState& state,
4850
TrapDomain& trap,
4951
SwiftLocationExtractor& locationExtractor,
5052
SwiftBodyEmissionStrategy& bodyEmissionStrategy)
5153
: sourceManager{sourceManager},
54+
state{state},
5255
trap{trap},
5356
locationExtractor{locationExtractor},
5457
bodyEmissionStrategy{bodyEmissionStrategy} {}
@@ -57,6 +60,9 @@ class SwiftDispatcher {
5760
return std::move(encounteredModules);
5861
}
5962

63+
void extractedDeclaration(const swift::Decl* decl) { state.emittedDeclarations.insert(decl); }
64+
void skippedDeclaration(const swift::Decl* decl) { state.pendingDeclarations.insert(decl); }
65+
6066
template <typename Entry>
6167
void emit(Entry&& entry) {
6268
bool valid = true;
@@ -302,6 +308,7 @@ class SwiftDispatcher {
302308
virtual void visit(const swift::CapturedValue* capture) = 0;
303309

304310
const swift::SourceManager& sourceManager;
311+
SwiftExtractorState& state;
305312
TrapDomain& trap;
306313
Store store;
307314
SwiftLocationExtractor& locationExtractor;

swift/extractor/infra/TargetDomains.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ static const char* typeToStr(TrapType type) {
1313
return "invocations";
1414
case TrapType::linkage:
1515
return "linkage";
16+
case TrapType::lazy_declarations:
17+
return "lazy_decls";
1618
default:
1719
return "";
1820
}

swift/extractor/infra/TargetDomains.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ enum class TrapType {
1212
module,
1313
invocation,
1414
linkage,
15+
lazy_declarations,
1516
};
1617

1718
std::filesystem::path getTrapPath(const SwiftExtractorState& state,

swift/extractor/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class Observer : public swift::FrontendObserver {
9292
void performedSemanticAnalysis(swift::CompilerInstance& compiler) override {
9393
codeql::extractSwiftFiles(state, compiler);
9494
codeql::extractSwiftInvocation(state, compiler, invocationTrap);
95+
codeql::extractExtractLazyDeclarations(state, compiler);
9596
}
9697

9798
private:

0 commit comments

Comments
 (0)