@@ -74,15 +74,21 @@ struct HostCapability {
7474///
7575/// The low level connection and the provider is injected by the client.
7676@_spi ( PluginMessage)
77- public class CompilerPluginMessageListener < Connection: MessageConnection , Provider : PluginProvider > {
77+ public class CompilerPluginMessageListener < Connection: MessageConnection , Handler : PluginMessageHandler > {
7878 /// Message channel for bidirectional communication with the plugin host.
7979 let connection : Connection
8080
81- let handler : CompilerPluginMessageHandler < Provider >
81+ let handler : Handler
8282
83- public init ( connection: Connection , provider : Provider ) {
83+ public init ( connection: Connection , messageHandler : Handler ) {
8484 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)
8692 }
8793
8894 /// Run the main message listener loop.
@@ -91,11 +97,26 @@ public class CompilerPluginMessageListener<Connection: MessageConnection, Provid
9197 /// On internal errors, such as I/O errors or JSON serialization errors, print
9298 /// an error message and `exit(1)`
9399 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 {
94113 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
98116 }
117+ let result = handler. handleMessage ( message)
118+ try connection. sendMessage ( result)
119+ return true
99120 } catch {
100121 // Emit a diagnostic and indicate failure to the plugin host,
101122 // and exit with an error code.
@@ -105,10 +126,18 @@ public class CompilerPluginMessageListener<Connection: MessageConnection, Provid
105126 }
106127}
107128
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`.
110139@_spi ( PluginMessage)
111- public class CompilerPluginMessageHandler < Provider: PluginProvider > {
140+ public class PluginProviderMessageHandler < Provider: PluginProvider > : PluginMessageHandler {
112141 /// Object to provide actual plugin functions.
113142 let provider : Provider
114143
@@ -199,6 +228,10 @@ public class CompilerPluginMessageHandler<Provider: PluginProvider> {
199228 }
200229}
201230
231+ @_spi ( PluginMessage)
232+ @available ( * , deprecated, renamed: " PluginProviderMessageHandler " )
233+ public typealias CompilerPluginMessageHandler < Provider: PluginProvider > = PluginProviderMessageHandler < Provider >
234+
202235struct UnimplementedError : Error , CustomStringConvertible {
203236 var description : String { " unimplemented " }
204237}
@@ -216,3 +249,31 @@ extension PluginProvider {
216249 throw UnimplementedError ( )
217250 }
218251}
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