-
-
Notifications
You must be signed in to change notification settings - Fork 63
BridgeJS: Add JSTypedClosure API
#578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
3f1660a to
4dffde8
Compare
cc2776a to
ccd8250
Compare
The new API allows managing JS closures converted from Swift closures from Swift side. It allows us to get the underlying `JSObject` and manage its lifetime manually from Swift. ```swift @js func makeIntToInt() throws(JSException) -> JSTypedClosure<(Int) -> Int> { return JSTypedClosure { x in return x + 1 } } @JSFunction func takeIntToInt(_ transform: JSTypedClosure<(Int) -> Int>) throws(JSException) let closure = JSTypedClosure<(Int) -> Int> { x in return x * 2 } defer { closure.release() } try takeIntToInt(closure) ``` Likewise to `JSClosure`, API users are responsible for "manually" releasing the closure when it's no longer needed by calling `release()`. After releasing, the closure becomes unusable and calling it will throw a JS exception (note that we will not segfault even if the closure is called after releasing). ```swift let closure = JSTypedClosure<(Int) -> Int> { x in return x * 2 } closure.release() try closure(10) // "JSException: Attempted to call a released JSTypedClosure created at <file>:<line>" ``` As a belt-and-suspenders measure, the underlying JS closure is also registered with a `FinalizationRegistry` to ensure that the Swift closure box is released when the JS closure is garbage collected, in case the API user forgets to call `release()`. However, relying on this mechanism is **not recommended** because the timing of garbage collection is non-deterministic and it's not guaranteed that it will happen in a timely manner. ---- On the top of the new API, this commit also fixes memory leak issues of closures exported to JS.
ccd8250 to
0919be8
Compare
krodak
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙌🏻 🙇🏻
| switch returnType { | ||
| case .closure(let signature): | ||
| append("return _BJS_Closure_\(raw: signature.mangleName).bridgeJSLower(ret)") | ||
| case .closure(_, useJSTypedClosure: false): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: For useJSTypedClosure: true we intentionally fall-through to default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I'm intentionally trying to use the default case (ret.bridgeJSLowerReturn()) as much as possible to simplify things. JSTypedClosure has bridgeJSLowerReturn definition for this purpose.
| // let obj3 = JSObject() | ||
| // let result = try ClosureSupportImports.jsApplyArrayJSObject([obj1, obj2, obj3], transform) | ||
| // XCTAssertEqual(result, [obj1, obj2, obj3]) | ||
| // } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Do we need some TODO or linked issue for those commented ones?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes for sure #598
BridgeJS: Add
JSTypedClosureAPIThe new API allows managing JS closures converted from Swift closures from Swift side. It allows us to get the underlying
JSObject(#540) and manage its lifetime manually from Swift.Likewise to
JSClosure, API users are responsible for "manually" releasing the closure when it's no longer needed by callingrelease(). After releasing, the closure becomes unusable and calling it will throw a JS exception (note that we will not segfault even if the closure is called after releasing).As a belt-and-suspenders measure, the underlying JS closure is also registered with a
FinalizationRegistryto ensure that the Swift closure box is released when the JS closure is garbage collected, in case the API userforgets to call
release(). However, relying on this mechanism is not recommended because the timing of garbage collection is non-deterministic and it's not guaranteed that it will happen in a timely manner.On the top of the new API, this commit also fixes memory leak issues of
closures exported to JS.
Close #536 #540 #541
Future TODO
JSTypedClosurein parameters of exported Swift functions and results of imported JS functions.throws(JSException)