@@ -74,15 +74,21 @@ struct HostCapability {
74
74
///
75
75
/// The low level connection and the provider is injected by the client.
76
76
@_spi ( PluginMessage)
77
- public class CompilerPluginMessageListener < Connection: MessageConnection , Provider : PluginProvider > {
77
+ public class CompilerPluginMessageListener < Connection: MessageConnection , Handler : PluginMessageHandler > {
78
78
/// Message channel for bidirectional communication with the plugin host.
79
79
let connection : Connection
80
80
81
- let handler : CompilerPluginMessageHandler < Provider >
81
+ let handler : Handler
82
82
83
- public init ( connection: Connection , provider : Provider ) {
83
+ public init ( connection: Connection , messageHandler : Handler ) {
84
84
self . connection = connection
85
- self . handler = CompilerPluginMessageHandler ( provider: provider)
85
+ self . handler = messageHandler
86
+ }
87
+
88
+ public init < Provider: PluginProvider > ( connection: Connection , provider: Provider )
89
+ where Handler == PluginProviderMessageHandler < Provider > {
90
+ self . connection = connection
91
+ self . handler = PluginProviderMessageHandler ( provider: provider)
86
92
}
87
93
88
94
/// Run the main message listener loop.
@@ -91,11 +97,26 @@ public class CompilerPluginMessageListener<Connection: MessageConnection, Provid
91
97
/// On internal errors, such as I/O errors or JSON serialization errors, print
92
98
/// an error message and `exit(1)`
93
99
public func main( ) {
100
+ #if os(WASI)
101
+ // Rather than blocking on read(), let the host tell us when there's data.
102
+ readabilityHandler = { _ = self . handleNextMessage ( ) }
103
+ #else
104
+ while handleNextMessage ( ) { }
105
+ #endif
106
+ }
107
+
108
+ /// Receives and handles a single message from the plugin host.
109
+ ///
110
+ /// - Returns: `true` if there was a message to read, `false`
111
+ /// if the end-of-file was reached.
112
+ private func handleNextMessage( ) -> Bool {
94
113
do {
95
- while let message = try connection. waitForNextMessage ( HostToPluginMessage . self) {
96
- let result = handler. handleMessage ( message)
97
- try connection. sendMessage ( result)
114
+ guard let message = try connection. waitForNextMessage ( HostToPluginMessage . self) else {
115
+ return false
98
116
}
117
+ let result = handler. handleMessage ( message)
118
+ try connection. sendMessage ( result)
119
+ return true
99
120
} catch {
100
121
// Emit a diagnostic and indicate failure to the plugin host,
101
122
// and exit with an error code.
@@ -105,10 +126,18 @@ public class CompilerPluginMessageListener<Connection: MessageConnection, Provid
105
126
}
106
127
}
107
128
108
- /// 'CompilerPluginMessageHandler' is a type that handle a message and do the
109
- /// corresponding operation.
129
+ /// A type that handles a plugin message and returns a response.
130
+ ///
131
+ /// - SeeAlso: ``PluginProviderMessageHandler``
132
+ @_spi ( PluginMessage)
133
+ public protocol PluginMessageHandler {
134
+ /// Handles a single message received from the plugin host.
135
+ func handleMessage( _ message: HostToPluginMessage ) -> PluginToHostMessage
136
+ }
137
+
138
+ /// A `PluginMessageHandler` that uses a `PluginProvider`.
110
139
@_spi ( PluginMessage)
111
- public class CompilerPluginMessageHandler < Provider: PluginProvider > {
140
+ public class PluginProviderMessageHandler < Provider: PluginProvider > : PluginMessageHandler {
112
141
/// Object to provide actual plugin functions.
113
142
let provider : Provider
114
143
@@ -199,6 +228,10 @@ public class CompilerPluginMessageHandler<Provider: PluginProvider> {
199
228
}
200
229
}
201
230
231
+ @_spi ( PluginMessage)
232
+ @available ( * , deprecated, renamed: " PluginProviderMessageHandler " )
233
+ public typealias CompilerPluginMessageHandler < Provider: PluginProvider > = PluginProviderMessageHandler < Provider >
234
+
202
235
struct UnimplementedError : Error , CustomStringConvertible {
203
236
var description : String { " unimplemented " }
204
237
}
@@ -216,3 +249,31 @@ extension PluginProvider {
216
249
throw UnimplementedError ( )
217
250
}
218
251
}
252
+
253
+ #if compiler(>=6) && os(WASI)
254
+
255
+ /// A callback invoked by the Wasm Host when new data is available on `stdin`.
256
+ ///
257
+ /// This is safe to access without serialization as Wasm plugins are single-threaded.
258
+ nonisolated ( unsafe) private var readabilityHandler : ( ) -> Void = {
259
+ fatalError (
260
+ """
261
+ CompilerPlugin.main wasn't called. Did you annotate your plugin with '@main'?
262
+ """
263
+ )
264
+ }
265
+
266
+ @_expose ( wasm, " swift_wasm_macro_v1_pump " )
267
+ @_cdecl ( " swift_wasm_macro_v1_pump " )
268
+ func wasmPump( ) {
269
+ readabilityHandler ( )
270
+ }
271
+
272
+ // we can't nest the whole #if-#else in '#if os(WASI)' due to a bug where
273
+ // '#if compiler' directives have to be the top-level #if, otherwise
274
+ // the compiler doesn't skip unknown syntax.
275
+ #elseif os(WASI)
276
+
277
+ #error("Building swift-syntax for WebAssembly requires compiler version 6.0 or higher.")
278
+
279
+ #endif
0 commit comments