You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e305448
Browse filesBrowse the repository at this point in the historyBrowse files
feat: preserve identity across all return shapes; add Symbol.dispose hook
Two improvements to .swift mode based on user review.
1. Optional<SwiftIdentityClass> identity is now preserved. Previously,
.some(x) returned a fresh wrapper each call because the Optional-of-
heap-object bridge dispatched through the default
_BridgedSwiftHeapObject.bridgeJSLowerReturn (passRetained + bare
pointer) instead of our per-method thunk. Fix: emit a per-class
override of bridgeJSLowerReturn that routes through the identity
table, symmetric to the existing bridgeJSStackPush override. Array,
Optional, and scalar returns now all go through the same handshake.
Side benefit: the per-method "case .swiftHeapObject where isSwift…"
branch in ExportedThunkBuilder.lowerReturnValue is now redundant —
ret.bridgeJSLowerReturn() picks up the override automatically. The
builder's isSwiftIdentityMode predicate is gone; codegen is simpler.
2. Symbol.dispose hook on .swift-mode wrappers. Allows:
using b = exports.getBuilding()
b.name
// release() runs automatically at the end of the scope
Zero runtime cost for consumers that don't use the `using` keyword —
just an extra method on the prototype. Tracks ECMAScript Explicit
Resource Management (Stage 3, V8 12+).
165/165 tests green, including a new testSwiftModeUsingAutoReleases
that exercises Symbol.dispose and verifies the Swift deinit fires.
The existing Optional identity test is upgraded from observability-only
to strict === assertion.
Copy file name to clipboardExpand all lines: Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Identity-Modes-For-Exported-Classes.md
+16-3Lines changed: 16 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -69,12 +69,26 @@ The key differences vs `.pointer`:
69
69
```javascript
70
70
constb=exports.getBuilding(); // allocates a wrapper (if fresh)
71
71
b.name; // stable === with any future getBuilding() that returns the same Swift object
72
-
b.release(); //mandatory for swift mode — frees the Swift heap object and the wrapper slot
72
+
b.release(); // frees the Swift heap object and the wrapper slot
73
73
// after release(): b.name throws "Attempted to call a member on a released Building"
74
74
```
75
75
76
76
Double-release is a safe no-op. Static members (class-level methods, constructors) are not affected by release.
77
77
78
+
### Auto-release via `using`
79
+
80
+
`.swift`-mode wrappers implement `Symbol.dispose`, so `using` scopes call `release()` automatically:
81
+
82
+
```javascript
83
+
{
84
+
using b =exports.getBuilding();
85
+
b.name;
86
+
// release() runs automatically at the end of the block
87
+
}
88
+
```
89
+
90
+
This works with JavaScript's [Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management) proposal (Stage 3, shipping in V8 12+). For environments that don't support `using` syntax yet, you can invoke the dispose hook directly: `b[Symbol.dispose]()`.
91
+
78
92
## Benchmarks (summary)
79
93
80
94
Full results in [`Benchmarks/results/swift-side-cache/Benchmarks.md`](../../../../../Benchmarks/results/swift-side-cache/Benchmarks.md). Baseline arm64-macOS, Swift 6.3, Node 22, release build, 500k iters per scenario, median ms:
@@ -91,8 +105,7 @@ Full results in [`Benchmarks/results/swift-side-cache/Benchmarks.md`](../../../.
91
105
92
106
## Known limitations
93
107
94
-
-**Optional<SwiftIdentityClass> identity is not preserved.** If your Swift API returns `SomeClass?`, `.some(x)` produces a fresh wrapper each call even with `.swift` mode. Lifecycle is still correct, but `a === b` where both come from an optional return will be `false`. Workaround: wrap in `[SomeClass]` (array element identity IS preserved).
95
-
-**No automatic cleanup.** You must call `release()` explicitly. A future version may add a Swift-side timeout or GC-assisted cleanup.
108
+
-**No GC-driven cleanup.** Wrappers live until `release()` (or `Symbol.dispose`) is called. Dropping all JS references to a wrapper without releasing leaks the Swift heap object. Use `using` scopes to make this automatic, or stay on `.pointer` mode if you rely on GC for cleanup.
96
109
-**Wasm-only.** Like all of BridgeJS, identity modes only activate on `wasm32`. On the host, the macro expands to no-op code paths so your test harness can still compile.
0 commit comments