Skip to content

BridgeJS: Fix JSValue reference ownership across the bridge - #826

Open
krodak wants to merge 1 commit into
swiftwasm:mainfrom
PassiveLogic:kr/bridgejs-jsvalue-return-lifetime
Open

krodak wants to merge 1 commit into
swiftwasm:mainfrom
PassiveLogic:kr/bridgejs-jsvalue-return-lifetime

Conversation

@krodak

@krodak krodak commented Nov 3, 2026

Copy link
Copy Markdown
Member

Overview

Returning a JSValue that Swift created crashed:

@JS func makeOptions() -> JSValue {
    let options = JSObject()
    options["method"] = .string("POST")
    return .object(options)
}
exports.makeOptions(); // ReferenceError: Attempted to use invalid reference ...

JSValue.bridgeJSStackPush consumed self before it retained the payload, so the last reference was already released. The retain now runs inside withRawJSValue.

Values that came from JavaScript did not crash, because they leaked. The JS lower retained the payload and JSValue.bridgeJSLiftParameter retained it again. In the other direction, Swift retained a returned payload and the JS lift never released it. JSValue now follows the JSObject rule: the sender retains and the receiver takes ownership. The Swift lift no longer retains, and the JS lift of returned and container values releases through a new __bjs_jsValueTake helper. Direct Swift-to-JS parameters stay borrowed. A private JSValueKind enum now holds the kind tags, so the glue no longer repeats which kinds carry a reference.

Testing

Added makeJSValueString and makeJSValueObject, and asserts that no reference stays in the JS heap after roundTripJSValue and roundTripOptionalJSValueArray. Snapshot tests and make unittest pass.

@krodak krodak self-assigned this Nov 3, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant