BridgeJS: Always provide swift_js_closure_unregister intrinsic in generated bridge-js.js by krodak · Pull Request #599 · swiftwasm/JavaScriptKit
Overview
PR #578 added JSTypedClosure API which introduced a new swift_js_closure_unregister BridgeJS intrinsic. The @_extern(wasm) declaration in BridgeJSIntrinsics.swift is unconditional, so the WASM binary always imports this symbol from the bjs module. However, the generated bridge-js.js only provides it when closure signatures are detected in the API surface.
Issue
When a project uses BridgeJS but does not use any closure types, the generated bridge-js.js omits swift_js_closure_unregister. Since the WASM binary unconditionally imports it, instantiation fails with:
LinkError: WebAssembly.instantiate(): Import #36 module="bjs" function="swift_js_closure_unregister": function import requires a callable
The instantiate.js template has unexpectedBjsCall stubs for all intrinsics (including swift_js_closure_unregister), but these stubs are only used when HAS_BRIDGE is false. When BridgeJS IS active, the template imports from bridge-js.js instead — which may not include the intrinsic.
Fix
- Added an unconditional no-op default for
swift_js_closure_unregisterinBridgeJSLink.swift, alongside other always-present intrinsics likeswift_js_throw,swift_js_retain,swift_js_release - When closures ARE used, the real implementation overrides the no-op later in the generation
- Updated all BridgeJSLinkTests snapshots to include the new unconditional intrinsic