- Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathSwiftASTManager.h
335 lines (295 loc) · 14.2 KB
/
SwiftASTManager.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
//===--- SwiftASTManager.h - ------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// The SwiftASTManager stack consists of four essential classes:
// - SwiftASTManager: Global object that is the entry point into AST building
// - ASTProducer: Produces ASTs for a single compiler invocation. Keeps track
// of old ASTs and determines if the olds ASTs are suitable to serve a
// SwiftASTConsumer (through SwiftASTConsumer::canUseASTWithSnapshots).
// - ASTBuildOperation: If ASTProducer decides that an AST needs to be built,
// it creates an ASTBuildOperation. It keeps track of all consumers that
// depend on it and supports AST build cancellation if all interested
// consumers disappear.
// - SwiftASTConsumer: Correlates to a SourceKit request. It just wants an AST
// and doesn't care if/how the AST is built or whether it's served from a
// cache.
//
// There is a strict memory ownership hierarchy. Classes higher up hold strong
// references to once further down. Callbacks that call upwards only hold weak
// references.
// Exception: Everything can hold a strong reference to the SwiftASTManager,
// because it's expected to stay alive anyway. The SwiftASTManager is
// responsible to occasionally clean up all strong references to classes lower
// down.
//
// Adding a new consumer:
// ----------------------
// To add a new consumer, SwiftASTManager::processASTAsync is called. It finds
// a suitable ASTProducer, which in turn looks up an ASTBuildOperation that
// either contains the same snapshot state that the consumer requested or one
// that is close enough to the consumer's expectation that it can serve it.
// If there already exists one, the consumer is added to it. Should the build
// operation already be finished, the consumer is directly called with the
// result. Otherwise, a new ASTBuildOperation is created, the consumer is added
// to it and the ASTBuildOperation is scheduled on
// SwiftASTManager::Implementation::ASTBuildQueue. This ensures that only one
// AST is built at a time.
// The SwiftASTManager keeps a weak reference to the consumer, so that the
// consumer can be cancelled if new requests come in (see implementation of
// processASTAsync).
//
// Building an AST:
// ----------------
// If it's a ASTBuildOperation's turn to build an AST (guarded by
// ASTBuildQueue), it checks whether any consumers are attached to it. If none
// are, there is no work to do and it finishes.
// Otherwise, it builds the AST, informs the consumers about the result and
// saves the result in a member variable so that it can serve future
// SwiftASTConsumers from it.
//
// Cancellation:
// -------------
// Only SwiftASTConsumers can be cancelled. The cancellation of the AST build
// only happens if no more consumers are interested in it.
// If requestCancellation is called on a SwiftASTConsumer, it informs the
// ASTBuildOperation, it is scheduled on, that it's no longer interested in the
// result. In theory, the ASTBuildOperation might or might not honour that
// request. In practice, it will always call SwiftASTConsumer::cancelled and
// remove the consumer from the list of consumers interested in its result. If
// no more consumers are interested in the ASTBuildOperation's result, it
// cancels the AST build by setting a flag that causes the type checker to fail
// with a "expression is too complex" error at the next suitable opportunity.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SOURCEKIT_LIB_SWIFTLANG_SWIFTASTMANAGER_H
#defineLLVM_SOURCEKIT_LIB_SWIFTLANG_SWIFTASTMANAGER_H
#include"SourceKit/Core/Context.h"
#include"SourceKit/Core/LLVM.h"
#include"SourceKit/Support/CancellationToken.h"
#include"SwiftInvocation.h"
#include"swift/Frontend/FrontendOptions.h"
#include"llvm/ADT/ArrayRef.h"
#include"llvm/ADT/IntrusiveRefCntPtr.h"
#include"llvm/ADT/StringRef.h"
#include"llvm/Support/Mutex.h"
#include"llvm/Support/VirtualFileSystem.h"
#include<functional>
#include<string>
namespacellvm {
classMemoryBuffer;
}
namespaceswift {
classCompilerInstance;
classCompilerInvocation;
classDiagnosticEngine;
classPluginRegistry;
classSourceFile;
classSourceManager;
}
namespaceSourceKit {
classContext;
structDiagnosticEntryInfo;
classImmutableTextSnapshot;
typedef RefPtr<ImmutableTextSnapshot> ImmutableTextSnapshotRef;
classSwiftEditorDocumentFileMap;
classSwiftLangSupport;
classSwiftInvocation;
structSwiftStatistics;
classGlobalConfig;
typedef RefPtr<SwiftInvocation> SwiftInvocationRef;
classEditorDiagConsumer;
classASTUnit : publicSourceKit::ThreadSafeRefCountedBase<ASTUnit> {
public:
structImplementation;
Implementation &Impl;
explicitASTUnit(uint64_t Generation, std::shared_ptr<SwiftStatistics> Stats);
~ASTUnit();
swift::CompilerInstance &getCompilerInstance() const;
uint64_tgetGeneration() const;
ArrayRef<ImmutableTextSnapshotRef> getSnapshots() const;
EditorDiagConsumer &getEditorDiagConsumer() const;
swift::SourceFile &getPrimarySourceFile() const;
/// Perform \p Fn asynchronously while preventing concurrent access to the
/// AST.
voidperformAsync(std::function<void()> Fn);
};
typedef IntrusiveRefCntPtr<ASTUnit> ASTUnitRef;
classSwiftASTConsumer : publicstd::enable_shared_from_this<SwiftASTConsumer> {
/// Mutex guarding all accesses to \c CancellationRequestCallback and \c
/// IsCancelled.
llvm::sys::Mutex CancellationRequestCallbackAndIsCancelledMtx;
/// A callback that informs the \c ASTBuildOperation, which is producing the
/// AST for this consumer, that the consumer is no longer of interest. Calling
/// this callback will eventually call \c cancelled on this consumer.
/// If the consumer isn't associated with any \c ASTBuildOperation at the
/// moment (e.g. if it hasn't been scheduled on one yet or if the build
/// operation has already informed the ASTConsumer), the callback is \c None.
std::optional<std::function<void(std::shared_ptr<SwiftASTConsumer>)>>
CancellationRequestCallback;
bool IsCancelled = false;
public:
virtual~SwiftASTConsumer() { }
/// Whether `handlePrimaryAST` should be executed with the same stack size as
/// the main thread.
///
/// By default, it is assumed that `handlePrimaryAST` does not do a lot of
/// work and it is sufficient to run it on a background thread's stack with
/// reduced size. Set this to `true` if the consumer can perform additional
/// work that might require more stack size, such as invoking SwiftParser.
virtualboolrequiresDeepStack() { returnfalse; }
// MARK: Cancellation
/// The result of this consumer is no longer of interest to the SourceKit
/// client.
/// This will cause \c cancelled to be called on this \c SwiftASTConsumer and
/// cause the \c ASTBuildOperation to be cancelled if no other consumer is
/// depending on it.
voidrequestCancellation() {
std::optional<std::function<void(std::shared_ptr<SwiftASTConsumer>)>>
CallbackToCall;
{
llvm::sys::ScopedLock L(CancellationRequestCallbackAndIsCancelledMtx);
IsCancelled = true;
CallbackToCall = CancellationRequestCallback;
CancellationRequestCallback = std::nullopt;
}
if (CallbackToCall.has_value()) {
(*CallbackToCall)(shared_from_this());
}
}
/// Set a cancellation request callback that informs a \c ASTBuildOperation
/// when this \c SwiftASTConsumer is cancelled. Asserts that there is
/// currently no callback set.
/// The cancellation request callback will automatically be removed when the
/// SwiftASTManager is cancelled.
/// If this \c SwiftASTConsumer has already been cancelled when this method is
/// called, \c NewCallback will be called immediately.
voidsetCancellationRequestCallback(
std::function<void(std::shared_ptr<SwiftASTConsumer>)> NewCallback) {
bool ShouldCallCallback = false;
{
llvm::sys::ScopedLock L(CancellationRequestCallbackAndIsCancelledMtx);
assert(!CancellationRequestCallback.has_value() &&
"Can't set two cancellation callbacks on a SwiftASTConsumer");
if (IsCancelled) {
ShouldCallCallback = true;
} else {
CancellationRequestCallback = NewCallback;
}
}
if (ShouldCallCallback) {
NewCallback(shared_from_this());
}
}
/// Removes the cancellation request callback previously set by \c
/// setCancellationRequestCallback.
voidremoveCancellationRequestCallback() {
llvm::sys::ScopedLock L(CancellationRequestCallbackAndIsCancelledMtx);
CancellationRequestCallback = std::nullopt;
}
// MARK: Result methods
// Exactly one of these will be called for every consumer.
/// An AST was produced that the consumer should handle.
virtualvoidhandlePrimaryAST(ASTUnitRef AstUnit) = 0;
/// Creation of the AST failed due to \p Error. The request corresponding to
/// this consumer should fail.
virtualvoidfailed(StringRef Error) = 0;
/// The consumer was cancelled by the \c requestCancellation method and the \c
/// ASTBuildOperation creating the AST for this consumer honored the request.
virtualvoidcancelled() {}
// MARK: Use ASTs from older snapshots
/// Typically a \c SwiftASTConsumer expects an AST for a specific source
/// state, namely the one on disk when the request was created.
/// If an AST for a similar source state has already been built, it might be
/// sufficient to serve this consumer.
///
/// An \c ASTProducer might ask a consumer if it can also handle an AST
/// constructed from the source state described by \p Snapshots.
/// If the consumer returns \c true from this method, it should expect to
/// receive an AST constructed from the source state described by \p
/// Snapshots. It might need to adjust its internal state (e.g. requested
/// offset for this new source state).
virtualboolcanUseASTWithSnapshots(
ArrayRef<ImmutableTextSnapshotRef> Snapshots) {
returnfalse;
}
};
typedef std::shared_ptr<SwiftASTConsumer> SwiftASTConsumerRef;
typedef std::weak_ptr<SwiftASTConsumer> SwiftASTConsumerWeakRef;
classSwiftASTManager : publicstd::enable_shared_from_this<SwiftASTManager> {
public:
explicitSwiftASTManager(std::shared_ptr<SwiftEditorDocumentFileMap>,
std::shared_ptr<GlobalConfig> Config,
std::shared_ptr<SwiftStatistics> Stats,
std::shared_ptr<RequestTracker> ReqTracker,
std::shared_ptr<swift::PluginRegistry> Plugins,
StringRef SwiftExecutablePath,
StringRef RuntimeResourcePath,
StringRef DiagnosticDocumentationPath);
~SwiftASTManager();
SwiftInvocationRef getTypecheckInvocation(ArrayRef<constchar *> Args,
StringRef PrimaryFile,
std::string &Error);
/// Same as the previous `getInvocation`, but allows the caller to specify a
/// custom `FileSystem` to be used throughout the invocation.
SwiftInvocationRef getTypecheckInvocation(
ArrayRef<constchar *> Args, StringRef PrimaryFile,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
std::string &Error);
/// Provides the AST associated with an invocation to the AST consumer,
/// asynchronously.
/// \param OncePerASTToken if non-null, a previous query with the same value
/// token, that is enqueued waiting to be executed on the same AST, will be
/// cancelled.
void
processASTAsync(SwiftInvocationRef Invok, SwiftASTConsumerRef ASTConsumer,
constvoid *OncePerASTToken,
SourceKitCancellationToken CancellationToken,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fileSystem);
std::unique_ptr<llvm::MemoryBuffer>
getMemoryBuffer(StringRef Filename,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
std::string &Error);
boolinitCompilerInvocation(swift::CompilerInvocation &Invocation,
ArrayRef<constchar *> Args,
swift::FrontendOptions::ActionType Action,
swift::DiagnosticEngine &Diags,
StringRef PrimaryFile, std::string &Error);
/// Same as the previous `initCompilerInvocation`, but allows the caller to
/// specify a custom `FileSystem` to be used throughout the invocation.
boolinitCompilerInvocation(
swift::CompilerInvocation &Invocation, ArrayRef<constchar *> Args,
swift::FrontendOptions::ActionType Action, swift::DiagnosticEngine &Diags,
StringRef PrimaryFile,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
std::string &Error);
boolinitCompilerInvocation(swift::CompilerInvocation &CompInvok,
ArrayRef<constchar *> OrigArgs,
swift::FrontendOptions::ActionType Action,
StringRef PrimaryFile, std::string &Error);
/// Initializes \p Invocation as if for typechecking, but with no inputs.
///
/// If \p AllowInputs is false, it is an error for \p OrigArgs to contain any
/// input files.
boolinitCompilerInvocationNoInputs(swift::CompilerInvocation &Invocation,
ArrayRef<constchar *> OrigArgs,
swift::FrontendOptions::ActionType Action,
swift::DiagnosticEngine &Diags,
std::string &Error,
bool AllowInputs = true);
voidremoveCachedAST(SwiftInvocationRef Invok);
voidcancelBuildsForCachedAST(SwiftInvocationRef Invok);
structImplementation;
Implementation &Impl;
};
typedef std::shared_ptr<SwiftASTManager> SwiftASTManagerRef;
} // namespace SourceKit
#endif