Skip to content

BridgeJS: Unify thunk argument preparation - #22

Draft
krodak wants to merge 2 commits into
mainfrom
fix/struct-self-stack-order
Draft

BridgeJS: Unify thunk argument preparation#22
krodak wants to merge 2 commits into
mainfrom
fix/struct-self-stack-order

Conversation

@krodak

@krodak krodak commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Overview

This fixes incorrect results from struct methods and Swift closures by preparing their arguments before the call, in the order required by the shared stacks.

1. Struct methods

These methods compile, but their generated wrappers could read self before the argument or return a number instead of a Promise:

@JS struct Numbers {
    var values: [Int]

    @JS func subtract(_ other: [Int]) -> Int {
        values.reduce(0, +) - other.reduce(0, +)
    }

    @JS func total() async -> Int {
        values.reduce(0, +)
    }
}

@JS func numbers(_ values: [Int]) -> Numbers {
    Numbers(values: values)
}
const first = exports.numbers([10]);
const second = exports.numbers([20]);
first.subtract([1]); // Should return 9.
await Promise.all([first.total(), second.total()]); // Should resolve to [10, 20].

2. Closure arguments

Two array arguments could be passed to a Swift closure in the wrong order:

@JS func makeDifference() -> ([Int], [Int]) -> Int {
    { first, second in first.reduce(0, +) - second.reduce(0, +) }
}
const difference = exports.makeDifference();
difference([1, 2], [10, 20]); // Returned 27 instead of -27.

Changes

Lift arguments into local variables in reverse order, including self, before calling Swift code. For async calls, this also happens before starting the task:

let other = [Int].bridgeJSStackPop()
let _self = Numbers.bridgeJSLiftParameter()
let ret = _self.subtract(_: other)

Struct methods and closures now reuse the existing call builders. This removes the parameter-count special case and gives struct methods the normal Promise and error handling.

Generated sending callbacks use a named JSTypedClosure.sending(...) factory instead of another initializer. This fixes ambiguous initializer errors when an Int-returning async callback appears alongside an ordinary (Int) -> Void callback.

References

  • #479: struct method wrappers affected by the ordering and Promise/error-handling fixes.
  • #766: async closure handlers that left argument lifting inside the task.
  • #707: generated sending callbacks involved in the initializer ambiguity.
  • #578: added the array callback tests commented out. Follow-up #598 was closed by #610, but the tests remained disabled. This PR enables the array round trips.

Test Plan

  • Reproduced reversed closure arguments, invalid async struct results, and the initializer ambiguity before the fixes.
  • Runtime coverage for concurrent async struct calls, array callbacks in both directions, throwing struct methods, and ordinary and sending callbacks in the same module.
  • Enabled the existing array callback round-trip tests for [Int], [Double], [String], [JSValue], and [JSObject].
  • BridgeJS diagnostics and snapshots with SwiftSyntax 600, 601, 602, and 603.
  • Verified existing WASM ABI names and signatures remain unchanged.
  • ./Utilities/bridge-js-generate.sh
  • npm run check:bridgejs-dts
  • make unittest SWIFT_SDK_ID=swift-6.3-RELEASE_wasm BUILD_SYSTEM=native

@krodak krodak self-assigned this Sep 8, 2026
@krodak krodak changed the title BridgeJS: Lift struct method arguments before self BridgeJS: Unify thunk argument preparation Sep 8, 2026
@krodak
krodak force-pushed the fix/struct-self-stack-order branch 5 times, most recently from 67cf95d to 09dd7f3 Compare September 9, 2026 12:03
@krodak
krodak force-pushed the fix/struct-self-stack-order branch from 09dd7f3 to a894d36 Compare September 10, 2026 11:31
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