Skip to content

Commit 92aa94f

Browse files
committed
Remove the split between SourceKitD and DynamicallyLoadedSourceKitD
There is only one real class that implements the `SourceKitD` protocol, so there really isn’t any need for the protocol + class split at all. Unify them to make code simpler to reason about.
1 parent e474354 commit 92aa94f

22 files changed

+128
-230
lines changed

Sources/BuildSystemIntegration/BuildSystemMessageDependencyTracker.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import BuildServerProtocol
1414
package import LanguageServerProtocol
1515
import LanguageServerProtocolExtensions
1616
import SKLogging
17-
package import SwiftExtensions
17+
import SwiftExtensions
1818

1919
/// A lightweight way of describing tasks that are created from handling BSP
2020
/// requests or notifications for the purpose of dependency tracking.

Sources/Diagnose/RunSourcekitdRequestCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ package struct RunSourceKitdRequestCommand: AsyncParsableCommand {
5454
print("Did not find sourcekitd in the toolchain. Specify path to sourcekitd manually by passing --sourcekitd")
5555
throw ExitCode(1)
5656
}
57-
let sourcekitd = try await DynamicallyLoadedSourceKitD.getOrCreate(
57+
let sourcekitd = try await SourceKitD.getOrCreate(
5858
dylibPath: sourcekitdPath,
5959
pluginPaths: nil
6060
)

Sources/SKTestSupport/SkipUnless.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ package actor SkipUnless {
253253
guard let sourcekitdPath = await ToolchainRegistry.forTesting.default?.sourcekitd else {
254254
throw GenericError("Could not find SourceKitD")
255255
}
256-
let sourcekitd = try await DynamicallyLoadedSourceKitD.getOrCreate(
256+
let sourcekitd = try await SourceKitD.getOrCreate(
257257
dylibPath: sourcekitdPath,
258258
pluginPaths: try sourceKitPluginPaths
259259
)

Sources/SourceKitD/DynamicallyLoadedSourceKitD.swift

Lines changed: 18 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -55,26 +55,37 @@ fileprivate func setenv(name: String, value: String, override: Bool) throws {
5555
///
5656
/// Users of this class should not call the api functions `initialize`, `shutdown`, or
5757
/// `set_notification_handler`, which are global state managed internally by this class.
58-
package actor DynamicallyLoadedSourceKitD: SourceKitD {
58+
package actor SourceKitD {
5959
/// The path to the sourcekitd dylib.
6060
package let path: URL
6161

6262
/// The handle to the dylib.
6363
let dylib: DLHandle
6464

6565
/// The sourcekitd API functions.
66-
package let api: sourcekitd_api_functions_t
66+
nonisolated package let api: sourcekitd_api_functions_t
6767

68-
private let pluginApiResult: Result<sourcekitd_plugin_api_functions_t, Error>
68+
/// General API for the SourceKit service and client framework, eg. for plugin initialization and to set up custom
69+
/// variant functions.
70+
///
71+
/// This must not be referenced outside of `SwiftSourceKitPlugin`, `SwiftSourceKitPluginCommon`, or
72+
/// `SwiftSourceKitClientPlugin`.
6973
package nonisolated var pluginApi: sourcekitd_plugin_api_functions_t { try! pluginApiResult.get() }
74+
private let pluginApiResult: Result<sourcekitd_plugin_api_functions_t, Error>
7075

71-
private let servicePluginApiResult: Result<sourcekitd_service_plugin_api_functions_t, Error>
76+
/// The API with which the SourceKit plugin handles requests.
77+
///
78+
/// This must not be referenced outside of `SwiftSourceKitPlugin`.
7279
package nonisolated var servicePluginApi: sourcekitd_service_plugin_api_functions_t {
7380
try! servicePluginApiResult.get()
7481
}
82+
private let servicePluginApiResult: Result<sourcekitd_service_plugin_api_functions_t, Error>
7583

76-
private let ideApiResult: Result<sourcekitd_ide_api_functions_t, Error>
84+
/// The API with which the SourceKit plugin communicates with the type-checker in-process.
85+
///
86+
/// This must not be referenced outside of `SwiftSourceKitPlugin`.
7787
package nonisolated var ideApi: sourcekitd_ide_api_functions_t { try! ideApiResult.get() }
88+
private let ideApiResult: Result<sourcekitd_ide_api_functions_t, Error>
7889

7990
/// Convenience for accessing known keys.
8091
///
@@ -128,7 +139,7 @@ package actor DynamicallyLoadedSourceKitD: SourceKitD {
128139
)
129140
}
130141
#endif
131-
return try DynamicallyLoadedSourceKitD(dylib: dylibPath, pluginPaths: pluginPaths)
142+
return try SourceKitD(dylib: dylibPath, pluginPaths: pluginPaths)
132143
}
133144
}
134145

@@ -224,60 +235,11 @@ package actor DynamicallyLoadedSourceKitD: SourceKitD {
224235
try await body()
225236
}
226237

227-
package func didSend(request: SKDRequestDictionary) {
238+
func didSend(request: SKDRequestDictionary) {
228239
for hook in requestHandlingHooks.values {
229240
hook(request)
230241
}
231242
}
232-
233-
package nonisolated func log(request: SKDRequestDictionary) {
234-
logger.info(
235-
"""
236-
Sending sourcekitd request:
237-
\(request.forLogging)
238-
"""
239-
)
240-
}
241-
242-
package nonisolated func log(response: SKDResponse) {
243-
logger.log(
244-
level: (response.error == nil || response.error == .requestCancelled) ? .debug : .error,
245-
"""
246-
Received sourcekitd response:
247-
\(response.forLogging)
248-
"""
249-
)
250-
}
251-
252-
package nonisolated func log(crashedRequest req: SKDRequestDictionary, fileContents: String?) {
253-
let log = """
254-
Request:
255-
\(req.description)
256-
257-
File contents:
258-
\(fileContents ?? "<nil>")
259-
"""
260-
let chunks = splitLongMultilineMessage(message: log)
261-
for (index, chunk) in chunks.enumerated() {
262-
logger.fault(
263-
"""
264-
sourcekitd crashed (\(index + 1)/\(chunks.count))
265-
\(chunk)
266-
"""
267-
)
268-
}
269-
}
270-
271-
package nonisolated func logRequestCancellation(request: SKDRequestDictionary) {
272-
// We don't need to log which request has been cancelled because we can associate the cancellation log message with
273-
// the send message via the log
274-
logger.info(
275-
"""
276-
Cancelling sourcekitd request:
277-
\(request.forLogging)
278-
"""
279-
)
280-
}
281243
}
282244

283245
struct WeakSKDNotificationHandler: Sendable {

Sources/SourceKitD/SKDRequestArray.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import Bionic
2626

2727
extension SourceKitD {
2828
/// Create a `SKDRequestArray` from the given array.
29-
package func array(_ array: [SKDRequestValue]) -> SKDRequestArray {
29+
nonisolated package func array(_ array: [SKDRequestValue]) -> SKDRequestArray {
3030
let result = SKDRequestArray(sourcekitd: self)
3131
for element in array {
3232
result.append(element)

Sources/SourceKitD/SKDRequestDictionary.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ extension Optional: SKDRequestValue where Wrapped: SKDRequestValue {}
4444

4545
extension SourceKitD {
4646
/// Create a `SKDRequestDictionary` from the given dictionary.
47-
package func dictionary(_ dict: [sourcekitd_api_uid_t: SKDRequestValue]) -> SKDRequestDictionary {
47+
nonisolated package func dictionary(_ dict: [sourcekitd_api_uid_t: SKDRequestValue]) -> SKDRequestDictionary {
4848
let result = SKDRequestDictionary(sourcekitd: self)
4949
for (key, value) in dict {
5050
result.set(key, to: value)

Sources/SourceKitD/SourceKitD.swift

Lines changed: 36 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
package import Csourcekitd
14-
import Dispatch
13+
import Csourcekitd
1514
package import Foundation
1615
import SKLogging
1716
import SwiftExtensions
@@ -39,76 +38,6 @@ package struct PluginPaths: Equatable, CustomLogStringConvertible {
3938
}
4039
}
4140

42-
/// Access to sourcekitd API, taking care of initialization, shutdown, and notification handler
43-
/// multiplexing.
44-
///
45-
/// *Users* of this protocol should not call the api functions `initialize`, `shutdown`, or
46-
/// `set_notification_handler`, which are global state managed internally by this class.
47-
///
48-
/// *Implementors* are expected to handle initialization and shutdown, e.g. during `init` and
49-
/// `deinit` or by wrapping an existing sourcekitd session that outlives this object.
50-
package protocol SourceKitD: AnyObject, Sendable {
51-
/// The sourcekitd API functions.
52-
var api: sourcekitd_api_functions_t { get }
53-
54-
/// General API for the SourceKit service and client framework, eg. for plugin initialization and to set up custom
55-
/// variant functions.
56-
///
57-
/// This must not be referenced outside of `SwiftSourceKitPlugin`, `SwiftSourceKitPluginCommon`, or
58-
/// `SwiftSourceKitClientPlugin`.
59-
var pluginApi: sourcekitd_plugin_api_functions_t { get }
60-
61-
/// The API with which the SourceKit plugin handles requests.
62-
///
63-
/// This must not be referenced outside of `SwiftSourceKitPlugin`.
64-
var servicePluginApi: sourcekitd_service_plugin_api_functions_t { get }
65-
66-
/// The API with which the SourceKit plugin communicates with the type-checker in-process.
67-
///
68-
/// This must not be referenced outside of `SwiftSourceKitPlugin`.
69-
var ideApi: sourcekitd_ide_api_functions_t { get }
70-
71-
/// Convenience for accessing known keys.
72-
var keys: sourcekitd_api_keys { get }
73-
74-
/// Convenience for accessing known keys.
75-
var requests: sourcekitd_api_requests { get }
76-
77-
/// Convenience for accessing known keys.
78-
var values: sourcekitd_api_values { get }
79-
80-
/// Adds a new notification handler, which will be weakly referenced.
81-
func addNotificationHandler(_ handler: SKDNotificationHandler) async
82-
83-
/// Removes a previously registered notification handler.
84-
func removeNotificationHandler(_ handler: SKDNotificationHandler) async
85-
86-
/// A function that gets called after the request has been sent to sourcekitd but but does not wait for results to be
87-
/// received. This can be used by clients to implement hooks that should be executed for every sourcekitd request.
88-
func didSend(request: SKDRequestDictionary) async
89-
90-
/// Log the given request.
91-
///
92-
/// This log call is issued during normal operation. It is acceptable for the logger to truncate the log message
93-
/// to achieve good performance.
94-
func log(request: SKDRequestDictionary)
95-
96-
/// Log the given request and file contents, ensuring they do not get truncated.
97-
///
98-
/// This log call is used when a request has crashed. In this case we want the log to contain the entire request to be
99-
/// able to reproduce it.
100-
func log(crashedRequest: SKDRequestDictionary, fileContents: String?)
101-
102-
/// Log the given response.
103-
///
104-
/// This log call is issued during normal operation. It is acceptable for the logger to truncate the log message
105-
/// to achieve good performance.
106-
func log(response: SKDResponse)
107-
108-
/// Log that the given request has been cancelled.
109-
func logRequestCancellation(request: SKDRequestDictionary)
110-
}
111-
11241
package enum SKDError: Error, Equatable {
11342
/// The service has crashed.
11443
case connectionInterrupted
@@ -130,8 +59,6 @@ package enum SKDError: Error, Equatable {
13059
}
13160

13261
extension SourceKitD {
133-
// MARK: - Convenience API for requests.
134-
13562
/// - Parameters:
13663
/// - request: The request to send to sourcekitd.
13764
/// - timeout: The maximum duration how long to wait for a response. If no response is returned within this time,
@@ -145,7 +72,12 @@ extension SourceKitD {
14572
) async throws -> SKDResponseDictionary {
14673
let sourcekitdResponse = try await withTimeout(timeout) {
14774
return try await withCancellableCheckedThrowingContinuation { (continuation) -> SourceKitDRequestHandle? in
148-
self.log(request: request)
75+
logger.info(
76+
"""
77+
Sending sourcekitd request:
78+
\(request.forLogging)
79+
"""
80+
)
14981
var handle: sourcekitd_api_request_handle_t? = nil
15082
self.api.send_request(request.dict, &handle) { response in
15183
continuation.resume(returning: SKDResponse(response!, sourcekitd: self))
@@ -159,17 +91,43 @@ extension SourceKitD {
15991
return nil
16092
} cancel: { (handle: SourceKitDRequestHandle?) in
16193
if let handle {
162-
self.logRequestCancellation(request: request)
94+
logger.info(
95+
"""
96+
Cancelling sourcekitd request:
97+
\(request.forLogging)
98+
"""
99+
)
163100
self.api.cancel_request(handle.handle)
164101
}
165102
}
166103
}
167104

168-
log(response: sourcekitdResponse)
105+
logger.log(
106+
level: (sourcekitdResponse.error == nil || sourcekitdResponse.error == .requestCancelled) ? .debug : .error,
107+
"""
108+
Received sourcekitd response:
109+
\(sourcekitdResponse.forLogging)
110+
"""
111+
)
169112

170113
guard let dict = sourcekitdResponse.value else {
171114
if sourcekitdResponse.error == .connectionInterrupted {
172-
log(crashedRequest: request, fileContents: fileContents)
115+
let log = """
116+
Request:
117+
\(request.description)
118+
119+
File contents:
120+
\(fileContents ?? "<nil>")
121+
"""
122+
let chunks = splitLongMultilineMessage(message: log)
123+
for (index, chunk) in chunks.enumerated() {
124+
logger.fault(
125+
"""
126+
sourcekitd crashed (\(index + 1)/\(chunks.count))
127+
\(chunk)
128+
"""
129+
)
130+
}
173131
}
174132
if sourcekitdResponse.error == .requestCancelled && !Task.isCancelled {
175133
throw SKDError.timedOut

Sources/SourceKitD/SourceKitDRegistry.swift

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,25 @@ import SKLogging
2222
/// * To remove an existing instance, use `remove("path")`, but be aware that if there are any other
2323
/// references to the instances in the program, it can be resurrected if `getOrAdd` is called with
2424
/// the same path. See note on `remove(_:)`
25-
package actor SourceKitDRegistry {
25+
///
26+
/// `SourceKitDType` is usually `SourceKitD` but can be substituted for a different type for testing purposes.
27+
package actor SourceKitDRegistry<SourceKitDType: AnyObject> {
2628

2729
/// Mapping from path to active SourceKitD instance.
28-
private var active: [URL: (pluginPaths: PluginPaths?, sourcekitd: SourceKitD)] = [:]
30+
private var active: [URL: (pluginPaths: PluginPaths?, sourcekitd: SourceKitDType)] = [:]
2931

3032
/// Instances that have been unregistered, but may be resurrected if accessed before destruction.
31-
private var cemetary: [URL: (pluginPaths: PluginPaths?, sourcekitd: WeakSourceKitD)] = [:]
33+
private var cemetary: [URL: (pluginPaths: PluginPaths?, sourcekitd: WeakSourceKitD<SourceKitDType>)] = [:]
3234

3335
/// Initialize an empty registry.
3436
package init() {}
3537

36-
/// The global shared SourceKitD registry.
37-
package static let shared: SourceKitDRegistry = SourceKitDRegistry()
38-
3938
/// Returns the existing SourceKitD for the given path, or creates it and registers it.
4039
package func getOrAdd(
4140
_ key: URL,
4241
pluginPaths: PluginPaths?,
43-
create: () throws -> SourceKitD
44-
) async rethrows -> SourceKitD {
42+
create: () throws -> SourceKitDType
43+
) async rethrows -> SourceKitDType {
4544
if let existing = active[key] {
4645
if existing.pluginPaths != pluginPaths {
4746
logger.fault(
@@ -72,7 +71,7 @@ package actor SourceKitDRegistry {
7271
/// is converted to a weak reference until it is no longer referenced anywhere by the program. If
7372
/// the same path is looked up again before the original service is deinitialized, the original
7473
/// service is resurrected rather than creating a new instance.
75-
package func remove(_ key: URL) -> SourceKitD? {
74+
package func remove(_ key: URL) -> SourceKitDType? {
7675
let existing = active.removeValue(forKey: key)
7776
if let existing = existing {
7877
assert(self.cemetary[key]?.sourcekitd.value == nil)
@@ -82,6 +81,11 @@ package actor SourceKitDRegistry {
8281
}
8382
}
8483

85-
fileprivate struct WeakSourceKitD {
86-
weak var value: SourceKitD?
84+
extension SourceKitDRegistry<SourceKitD> {
85+
/// The global shared SourceKitD registry.
86+
package static let shared: SourceKitDRegistry = SourceKitDRegistry()
87+
}
88+
89+
fileprivate struct WeakSourceKitD<SourceKitDType: AnyObject> {
90+
weak var value: SourceKitDType?
8791
}

0 commit comments

Comments
 (0)