-
Notifications
You must be signed in to change notification settings - Fork 439
Add support for WebAssembly Macros #2623
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
158ddd5
385cc0a
4dd83b3
0b6a5a6
7a6b576
f5d82ef
9f455cb
18814f1
a38baa4
a582c2f
85e0374
921b7ea
9fab7cc
d3495f2
76de84f
76f7d8f
3e2e4d4
cf060a4
4cd128c
84f3230
2242b64
054e5e3
595b0ae
c7466de
0a054ad
0e65034
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -74,15 +74,21 @@ struct HostCapability { | |
/// | ||
/// The low level connection and the provider is injected by the client. | ||
@_spi(PluginMessage) | ||
public class CompilerPluginMessageListener<Connection: MessageConnection, Provider: PluginProvider> { | ||
public class CompilerPluginMessageListener<Connection: MessageConnection, Handler: PluginMessageHandler> { | ||
/// Message channel for bidirectional communication with the plugin host. | ||
let connection: Connection | ||
|
||
let handler: CompilerPluginMessageHandler<Provider> | ||
let handler: Handler | ||
|
||
public init(connection: Connection, provider: Provider) { | ||
public init(connection: Connection, messageHandler: Handler) { | ||
self.connection = connection | ||
self.handler = CompilerPluginMessageHandler(provider: provider) | ||
self.handler = messageHandler | ||
} | ||
|
||
public init<Provider: PluginProvider>(connection: Connection, provider: Provider) | ||
where Handler == PluginProviderMessageHandler<Provider> { | ||
self.connection = connection | ||
self.handler = PluginProviderMessageHandler(provider: provider) | ||
} | ||
|
||
/// Run the main message listener loop. | ||
|
@@ -91,11 +97,26 @@ public class CompilerPluginMessageListener<Connection: MessageConnection, Provid | |
/// On internal errors, such as I/O errors or JSON serialization errors, print | ||
/// an error message and `exit(1)` | ||
public func main() { | ||
#if os(WASI) | ||
// Rather than blocking on read(), let the host tell us when there's data. | ||
readabilityHandler = { _ = self.handleNextMessage() } | ||
#else | ||
while handleNextMessage() {} | ||
#endif | ||
} | ||
|
||
/// Receives and handles a single message from the plugin host. | ||
/// | ||
/// - Returns: `true` if there was a message to read, `false` | ||
/// if the end-of-file was reached. | ||
private func handleNextMessage() -> Bool { | ||
do { | ||
while let message = try connection.waitForNextMessage(HostToPluginMessage.self) { | ||
let result = handler.handleMessage(message) | ||
try connection.sendMessage(result) | ||
guard let message = try connection.waitForNextMessage(HostToPluginMessage.self) else { | ||
return false | ||
} | ||
let result = handler.handleMessage(message) | ||
try connection.sendMessage(result) | ||
return true | ||
} catch { | ||
// Emit a diagnostic and indicate failure to the plugin host, | ||
// and exit with an error code. | ||
|
@@ -105,10 +126,18 @@ public class CompilerPluginMessageListener<Connection: MessageConnection, Provid | |
} | ||
} | ||
|
||
/// 'CompilerPluginMessageHandler' is a type that handle a message and do the | ||
/// corresponding operation. | ||
/// A type that handles a plugin message and returns a response. | ||
/// | ||
/// - SeeAlso: ``PluginProviderMessageHandler`` | ||
@_spi(PluginMessage) | ||
public protocol PluginMessageHandler { | ||
/// Handles a single message received from the plugin host. | ||
func handleMessage(_ message: HostToPluginMessage) -> PluginToHostMessage | ||
} | ||
|
||
/// A `PluginMessageHandler` that uses a `PluginProvider`. | ||
@_spi(PluginMessage) | ||
public class CompilerPluginMessageHandler<Provider: PluginProvider> { | ||
public class PluginProviderMessageHandler<Provider: PluginProvider>: PluginMessageHandler { | ||
/// Object to provide actual plugin functions. | ||
let provider: Provider | ||
|
||
|
@@ -199,6 +228,10 @@ public class CompilerPluginMessageHandler<Provider: PluginProvider> { | |
} | ||
} | ||
|
||
@_spi(PluginMessage) | ||
@available(*, deprecated, renamed: "PluginProviderMessageHandler") | ||
public typealias CompilerPluginMessageHandler<Provider: PluginProvider> = PluginProviderMessageHandler<Provider> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is anything in this repo or the compiler repo using this? If not, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re-added this since it's used by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, my bad. Thanks! |
||
|
||
struct UnimplementedError: Error, CustomStringConvertible { | ||
var description: String { "unimplemented" } | ||
} | ||
|
@@ -216,3 +249,31 @@ extension PluginProvider { | |
throw UnimplementedError() | ||
} | ||
} | ||
|
||
#if compiler(>=6) && os(WASI) | ||
|
||
/// A callback invoked by the Wasm Host when new data is available on `stdin`. | ||
/// | ||
/// This is safe to access without serialization as Wasm plugins are single-threaded. | ||
nonisolated(unsafe) private var readabilityHandler: () -> Void = { | ||
fatalError( | ||
""" | ||
CompilerPlugin.main wasn't called. Did you annotate your plugin with '@main'? | ||
""" | ||
) | ||
} | ||
|
||
@_expose(wasm, "swift_wasm_macro_v1_pump") | ||
@_cdecl("swift_wasm_macro_v1_pump") | ||
func wasmPump() { | ||
readabilityHandler() | ||
} | ||
|
||
// we can't nest the whole #if-#else in '#if os(WASI)' due to a bug where | ||
// '#if compiler' directives have to be the top-level #if, otherwise | ||
// the compiler doesn't skip unknown syntax. | ||
#elseif os(WASI) | ||
|
||
#error("Building swift-syntax for WebAssembly requires compiler version 6.0 or higher.") | ||
|
||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this abstraction is needed anyway, so this is okay. But TBH, I'm not a fan of
WasmInterceptingMessageHandler
in swiftlang/swift#73031.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'm not the biggest fan either :/ but that's the only approach I could think of which allows us to send some messages from
swift-plugin-server
toLibraryPluginProvider
while intercepting others. The alternative is to have an entirely separateswift-wasm-plugin-server
(so that the primary plugin server can useLibraryPluginProvider
directly) but both Doug and Yuta feel that having a unified plugin server is preferable (see swiftlang/swift#73031 (comment)), and I agree that the benefits outweigh the downsides atm.Curious to hear if you have any alternative suggestions though.