Conversation
tsapeta
added this pull request to stack #65
October 1, 2026 13:42
tsapeta
marked this pull request as ready for review
October 2, 2026 08:57
tsapeta
removed this pull request from stack #65
October 2, 2026 09:08
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
@Uniononly emitteddecode(_: borrowing JavaScriptValue, in:). TheJavaScriptUnownedValueoverload fell back to theJavaScriptDecodabledefault in expo-modules-jsi, which callsvalue.copied(in: runtime)before decoding. Every union argument decoded through the argument fast path paid for that copy, even when the matching case was a primitive that can read the borrowed value.How
The macro now emits both decode overloads, with the same case order.
In the unowned overload, a case whose payload has its own unowned
decodereads the borrowed value directly. The macro recognizes those payloads by spelling:Bool,String, the numeric types,ArrayBuffer, and an optional of any of them. Every other case (a record, an array, a dictionary, or any type the macro doesn't recognize, like a typealias or aSharedObjectsubclass) would copy the value through the protocol default, so these cases share one owning copy instead. The copy is made the first time such a case runs, and the mismatch error reuses it.Copies per call of the unowned overload:
A type missing from the list only costs that one shared copy; the result is still correct.
No core API change is needed.
Test plan
UnionMacroTests. TheInputfixture (Point,[Point],[String: Point],Int?) now shows the shared copy for the first three cases and the borrowed value forInt?. Unions with only builtin payloads emit no copy.swift test: 105 + 181 tests pass.node_modules:MacroUnionTests(a union with a record case, so it goes through the shared copy) compile and pass: 12 tests.SQLiteBindValue.decode(_: JavaScriptUnownedValue, in:), and expo-sqlite's 50 unit tests pass (checked on the first revision of this PR).