Skip to content

Commit f084e25

Browse files
committed
JSCWasmPlugin: memoize, improve errors
1 parent 89158e2 commit f084e25

File tree

1 file changed

+27
-18
lines changed

1 file changed

+27
-18
lines changed

tools/swift-plugin-server/Sources/swift-wasm-plugin-server/JSCWasmPlugin.swift

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import JavaScriptCore
14-
import SwiftPluginServerSupport
1514

16-
// returns: Promise<(ArrayBuffer) => ArrayBuffer>
15+
// returns: (wasm: ArrayBuffer) => Promise<(input: ArrayBuffer) => ArrayBuffer>
1716
private let js = """
18-
(async () => {
17+
async (wasmData) => {
1918
const mod = await WebAssembly.compile(wasmData);
2019
// stub WASI imports
2120
const imports = WebAssembly.Module.imports(mod)
@@ -38,39 +37,49 @@ private let js = """
3837
api.wacro_free(outAddr);
3938
return copy.buffer;
4039
}
41-
})()
40+
}
4241
"""
4342

4443
@available(macOS 10.15, *)
4544
final class JSCWasmPlugin: WasmPlugin {
45+
private static let factory = JSContext()?.evaluateScript(js)
46+
4647
private let handler: JSValue
4748

4849
@MainActor init(wasm data: Data) async throws {
49-
guard let context = JSContext() else { throw PluginServerError(message: "Could not create JSContext") }
50-
51-
let jsBuf = try JSValue(newBufferWithData: data, in: context)
52-
context.globalObject.setObject(jsBuf, forKeyedSubscript: "wasmData")
53-
54-
guard let promise = context.evaluateScript(js) else {
55-
throw PluginServerError(message: "Failed to load plugin")
50+
guard let factory = Self.factory, let context = factory.context else {
51+
throw JSCWasmError(message: "Failed to load plugin")
5652
}
5753

58-
if let error = context.exception {
59-
throw PluginServerError(message: "Failed to load plugin: \(error)")
54+
let jsBuf = try JSValue(newBufferWithData: data, in: context)
55+
guard let promise = factory.call(withArguments: [jsBuf]), context.exception == nil else {
56+
throw JSCWasmError(message: "Failed to load plugin", value: context.exception)
6057
}
6158

6259
handler = try await promise.promiseValue
6360
}
6461

6562
@MainActor func handleMessage(_ json: Data) throws -> Data {
66-
let jsonJS = try JSValue(newBufferWithData: json, in: handler.context)
63+
guard let context = handler.context else {
64+
throw JSCWasmError(message: "Failed to invoke plugin")
65+
}
66+
let jsonJS = try JSValue(newBufferWithData: json, in: context)
6767
guard let result = handler.call(withArguments: [jsonJS]) else {
68-
throw PluginServerError(message: "Wasm plugin did not provide a valid response")
68+
throw JSCWasmError(message: "Wasm plugin did not provide a valid response", value: context.exception)
6969
}
7070
return result.arrayBufferData()
7171
}
7272
}
7373

74+
public struct JSCWasmError: Error, CustomStringConvertible {
75+
public let value: JSValue?
76+
public let description: String
77+
public init(message: String, value: JSValue? = nil) {
78+
self.value = value
79+
self.description = "\(message)\(value.map { ": \($0)" } ?? "")"
80+
}
81+
}
82+
7483
extension JSValue {
7584
fileprivate convenience init(newBufferWithData data: Data, in context: JSContext) throws {
7685
let copy = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: data.count)
@@ -105,18 +114,18 @@ extension JSValue {
105114
let rejectFunc = JSValue(
106115
object: { error in
107116
continuation.resume(
108-
throwing: PluginServerError(message: "\(error)")
117+
throwing: JSCWasmError(message: "Promise rejected", value: error)
109118
)
110119
} as @convention(block) (JSValue) -> Void,
111120
in: context
112121
)
113122
guard let successFunc, let rejectFunc else {
114-
continuation.resume(throwing: PluginServerError(message: "Could not await promise"))
123+
continuation.resume(throwing: JSCWasmError(message: "Could not await promise"))
115124
return
116125
}
117126
invokeMethod("then", withArguments: [successFunc, rejectFunc])
118127
if let exception = context.exception {
119-
continuation.resume(throwing: PluginServerError(message: "\(exception)"))
128+
continuation.resume(throwing: JSCWasmError(message: "Promise.then threw", value: exception))
120129
}
121130
}
122131
}

0 commit comments

Comments
 (0)