expo-modules-jsi 58.0.7 → 58.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/apple/Benchmarks/RuntimeCacheBenchmarks.swift +71 -0
- package/apple/Benchmarks/UnownedDecodeBenchmarks.swift +121 -0
- package/apple/Sources/ExpoModulesJSI/Coding/JavaScriptCodable+Containers.swift +52 -10
- package/apple/Sources/ExpoModulesJSI/Coding/JavaScriptCodable+Data.swift +5 -0
- package/apple/Sources/ExpoModulesJSI/Coding/JavaScriptCodable+Date.swift +27 -5
- package/apple/Sources/ExpoModulesJSI/Coding/JavaScriptCodable+Primitives.swift +75 -0
- package/apple/Sources/ExpoModulesJSI/Coding/JavaScriptDecodable.swift +34 -5
- package/apple/Sources/ExpoModulesJSI/Coding/JavaScriptValueKinds.swift +73 -0
- package/apple/Sources/ExpoModulesJSI/Protocols/JSIRepresentable.swift +4 -2
- package/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntime.swift +18 -13
- package/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntimeCache.swift +74 -0
- package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptArray.swift +26 -0
- package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptObject.swift +22 -0
- package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptPromise.swift +14 -14
- package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptUnownedValue.swift +17 -0
- package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptValue.swift +54 -1
- package/apple/Sources/ExpoModulesJSI/Utilities/String+JSI.swift +4 -3
- package/apple/Sources/ExpoModulesJSI-Cxx/include/JSIUtils.h +27 -0
- package/apple/Tests/JavaScriptDecodableKindsTests.swift +132 -0
- package/apple/Tests/JavaScriptRuntimeCacheTests.swift +117 -0
- package/apple/Tests/JavaScriptRuntimeTests.swift +59 -0
- package/apple/Tests/JavaScriptUnownedDecodeTests.swift +99 -0
- package/apple/Tests/JavaScriptUnownedElementsTests.swift +76 -0
- package/apple/scripts/build-xcframework.sh +15 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 58.0.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [iOS] Add `decodableKinds` to `JavaScriptDecodable`: the kinds of JavaScript value (`JavaScriptValueKinds`) that `decode` can accept, so code that picks between several types can skip the ones that can't match. ([#50905](https://github.com/expo/expo/pull/50905) by [@tsapeta](https://github.com/tsapeta))
|
|
8
|
+
- [iOS] Fix crash reports symbolicated on the device showing no function names for `ExpoModulesJSI` frames. ([#50698](https://github.com/expo/expo/pull/50698) by [@tsapeta](https://github.com/tsapeta))
|
|
9
|
+
- Return strings, objects and arrays from host functions and host object getters without cloning the engine handle, and build short ASCII strings from JS inline. ([#50937](https://github.com/expo/expo/pull/50937) by [@tsapeta](https://github.com/tsapeta))
|
|
10
|
+
- [iOS] Add `JavaScriptRuntime.cached(_:_:)` with typed `JavaScriptRuntime.Cache.Key`s, to create a value once per runtime and reuse it, for example a JavaScript constructor or a property name. A lookup reads one array slot, about 5× faster than the string-keyed `JavaScriptPropNameID.cached(_:_:)`. ([#50888](https://github.com/expo/expo/pull/50888) by [@tsapeta](https://github.com/tsapeta))
|
|
11
|
+
- [iOS] Add `JavaScriptValue.withUnownedValue(in:_:)`, and give the owning `JavaScriptDecodable.decode` a default that borrows the value and decodes it through the `JavaScriptUnownedValue` overload, so a conformer can implement only that one. Arrays, dictionaries, dates, records and enums now decode unowned values without copying them first. ([#50960](https://github.com/expo/expo/pull/50960) by [@tsapeta](https://github.com/tsapeta))
|
|
12
|
+
- [iOS] Add `JavaScriptArray.mapUnowned(_:)`, `JavaScriptObject.withUnownedProperty(_:_:)`, and `isArray()` and `getArray(in:)` on `JavaScriptUnownedValue`. Arrays, dictionaries and dates now decode through their unowned overload without copying the value or wrapping each element in a `JavaScriptValue`, and their owning decodes forward to it. ([#50980](https://github.com/expo/expo/pull/50980) by [@tsapeta](https://github.com/tsapeta))
|
|
13
|
+
- [iOS] Keep checkout paths out of the Swift compilation cache key so modules importing ExpoModulesCore can reuse cached compilation results across checkouts and worktrees. ([#50354](https://github.com/expo/expo/pull/50354) by [@janicduplessis](https://github.com/janicduplessis))
|
|
14
|
+
|
|
3
15
|
## 58.0.7
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Copyright 2026-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesJSI
|
|
4
|
+
import Testing
|
|
5
|
+
|
|
6
|
+
private let answerPropNameKey = JavaScriptRuntime.Cache.Key<JavaScriptPropNameID>()
|
|
7
|
+
|
|
8
|
+
/// Benchmarks for values cached per runtime with ``JavaScriptRuntime/cached(_:_:)``, read against
|
|
9
|
+
/// the string-keyed ``JavaScriptPropNameID/cached(_:_:)`` registry.
|
|
10
|
+
extension JSIBenchmarks {
|
|
11
|
+
@Test
|
|
12
|
+
func `runtime cache hit`() async throws {
|
|
13
|
+
try await benchmarkCase { runtime in
|
|
14
|
+
_ = runtime.cached(answerPropNameKey) { JavaScriptPropNameID(runtime, string: "answer") }
|
|
15
|
+
try benchmark("JavaScriptRuntime.cached(_:_:): hit", runtime: runtime) { iterations in
|
|
16
|
+
for _ in 0..<iterations {
|
|
17
|
+
_ = runtime.cached(answerPropNameKey) { JavaScriptPropNameID(runtime, string: "answer") }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@Test
|
|
24
|
+
func `object property by runtime-cached PropNameID`() async throws {
|
|
25
|
+
try await benchmarkCase { runtime in
|
|
26
|
+
let object = try runtime.eval("({ answer: 42 })").getObject()
|
|
27
|
+
try benchmark("JavaScriptObject.getProperty(_:): runtime-cached PropNameID", runtime: runtime) { iterations in
|
|
28
|
+
for _ in 0..<iterations {
|
|
29
|
+
_ = object.getProperty(runtime.cached(answerPropNameKey) { JavaScriptPropNameID(runtime, string: "answer") })
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Test
|
|
36
|
+
func `string-keyed PropNameID cache hit`() async throws {
|
|
37
|
+
try await benchmarkCase { runtime in
|
|
38
|
+
try benchmark("JavaScriptPropNameID.cached(_:_:): hit", runtime: runtime) { iterations in
|
|
39
|
+
for _ in 0..<iterations {
|
|
40
|
+
_ = JavaScriptPropNameID.cached(runtime, "answer")
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Test
|
|
47
|
+
func `object property by string-keyed cached PropNameID`() async throws {
|
|
48
|
+
try await benchmarkCase { runtime in
|
|
49
|
+
let object = try runtime.eval("({ answer: 42 })").getObject()
|
|
50
|
+
try benchmark("JavaScriptObject.getProperty(_:): string-cached PropNameID", runtime: runtime) { iterations in
|
|
51
|
+
for _ in 0..<iterations {
|
|
52
|
+
_ = object.getProperty(.cached(runtime, "answer"))
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Test
|
|
59
|
+
func `object property by prebuilt PropNameID`() async throws {
|
|
60
|
+
try await benchmarkCase { runtime in
|
|
61
|
+
let object = try runtime.eval("({ answer: 42 })").getObject()
|
|
62
|
+
let propName = JavaScriptPropNameID(runtime, string: "answer")
|
|
63
|
+
try benchmark("JavaScriptObject.getProperty(_:): prebuilt PropNameID", runtime: runtime) { iterations in
|
|
64
|
+
for _ in 0..<iterations {
|
|
65
|
+
_ = object.getProperty(propName)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
withExtendedLifetime(propName) {}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Copyright 2026-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesJSI
|
|
4
|
+
import Foundation
|
|
5
|
+
import Testing
|
|
6
|
+
|
|
7
|
+
/// Decoding from a borrowed `JavaScriptUnownedValue`, the path a `@JS` argument takes, and from an owning
|
|
8
|
+
/// `JavaScriptValue`, the path a nested value takes. A type without its own unowned decode copies the
|
|
9
|
+
/// value into an owning `JavaScriptValue` first.
|
|
10
|
+
extension JSIBenchmarks {
|
|
11
|
+
@Test
|
|
12
|
+
func `unowned decode of an array`() async throws {
|
|
13
|
+
try await benchmarkCase { runtime in
|
|
14
|
+
let buffer = JavaScriptValuesBuffer.allocate(in: runtime, with: try runtime.eval("[1, 2, 3, 4, 5, 6, 7, 8]"))
|
|
15
|
+
try benchmark("[Double].decode(unowned): 8 elements", runtime: runtime) { iterations in
|
|
16
|
+
for _ in 0..<iterations {
|
|
17
|
+
_ = try [Double].decode(buffer.unownedValue(at: 0), in: runtime)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@Test
|
|
24
|
+
func `unowned decode of a dictionary`() async throws {
|
|
25
|
+
try await benchmarkCase { runtime in
|
|
26
|
+
let buffer = JavaScriptValuesBuffer.allocate(in: runtime, with: try runtime.eval("({ a: 1, b: 2, c: 3, d: 4 })"))
|
|
27
|
+
try benchmark("[String: Double].decode(unowned): 4 entries", runtime: runtime) { iterations in
|
|
28
|
+
for _ in 0..<iterations {
|
|
29
|
+
_ = try [String: Double].decode(buffer.unownedValue(at: 0), in: runtime)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Test
|
|
36
|
+
func `unowned decode of a date`() async throws {
|
|
37
|
+
try await benchmarkCase { runtime in
|
|
38
|
+
let buffer = JavaScriptValuesBuffer.allocate(in: runtime, with: try runtime.eval("1700000000000"))
|
|
39
|
+
try benchmark("Date.decode(unowned): number", runtime: runtime) { iterations in
|
|
40
|
+
for _ in 0..<iterations {
|
|
41
|
+
_ = try Date.decode(buffer.unownedValue(at: 0), in: runtime)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Test
|
|
48
|
+
func `owning decode of an array`() async throws {
|
|
49
|
+
try await benchmarkCase { runtime in
|
|
50
|
+
let value = try runtime.eval("[1, 2, 3, 4, 5, 6, 7, 8]")
|
|
51
|
+
try benchmark("[Double].decode(owning): 8 elements", runtime: runtime) { iterations in
|
|
52
|
+
for _ in 0..<iterations {
|
|
53
|
+
_ = try [Double].decode(value, in: runtime)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@Test
|
|
60
|
+
func `owning decode of a dictionary`() async throws {
|
|
61
|
+
try await benchmarkCase { runtime in
|
|
62
|
+
let value = try runtime.eval("({ a: 1, b: 2, c: 3, d: 4 })")
|
|
63
|
+
try benchmark("[String: Double].decode(owning): 4 entries", runtime: runtime) { iterations in
|
|
64
|
+
for _ in 0..<iterations {
|
|
65
|
+
_ = try [String: Double].decode(value, in: runtime)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Test
|
|
72
|
+
func `owning decode of nested arrays`() async throws {
|
|
73
|
+
try await benchmarkCase { runtime in
|
|
74
|
+
let value = try runtime.eval("[[1, 2], [3, 4], [5, 6], [7, 8]]")
|
|
75
|
+
try benchmark("[[Double]].decode(owning): 4 x 2 elements", runtime: runtime) { iterations in
|
|
76
|
+
for _ in 0..<iterations {
|
|
77
|
+
_ = try [[Double]].decode(value, in: runtime)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@Test
|
|
84
|
+
func `unowned decode of nested arrays`() async throws {
|
|
85
|
+
try await benchmarkCase { runtime in
|
|
86
|
+
let buffer = JavaScriptValuesBuffer.allocate(
|
|
87
|
+
in: runtime,
|
|
88
|
+
with: try runtime.eval("[[1, 2], [3, 4], [5, 6], [7, 8]]")
|
|
89
|
+
)
|
|
90
|
+
try benchmark("[[Double]].decode(unowned): 4 x 2 elements", runtime: runtime) { iterations in
|
|
91
|
+
for _ in 0..<iterations {
|
|
92
|
+
_ = try [[Double]].decode(buffer.unownedValue(at: 0), in: runtime)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Test
|
|
99
|
+
func `unowned decode of a date string`() async throws {
|
|
100
|
+
try await benchmarkCase { runtime in
|
|
101
|
+
let buffer = JavaScriptValuesBuffer.allocate(in: runtime, with: try runtime.eval("'2026-10-02T12:00:00Z'"))
|
|
102
|
+
try benchmark("Date.decode(unowned): ISO string", runtime: runtime) { iterations in
|
|
103
|
+
for _ in 0..<iterations {
|
|
104
|
+
_ = try Date.decode(buffer.unownedValue(at: 0), in: runtime)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@Test
|
|
111
|
+
func `unowned decode of a date object`() async throws {
|
|
112
|
+
try await benchmarkCase { runtime in
|
|
113
|
+
let buffer = JavaScriptValuesBuffer.allocate(in: runtime, with: try runtime.eval("new Date(1700000000000)"))
|
|
114
|
+
try benchmark("Date.decode(unowned): Date object", runtime: runtime) { iterations in
|
|
115
|
+
for _ in 0..<iterations {
|
|
116
|
+
_ = try Date.decode(buffer.unownedValue(at: 0), in: runtime)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -10,19 +10,36 @@
|
|
|
10
10
|
// MARK: - Array
|
|
11
11
|
|
|
12
12
|
extension Array: JavaScriptDecodable where Element: JavaScriptDecodable {
|
|
13
|
+
// A non-array value is decoded as a single-element array, so it's accepted when the element is.
|
|
14
|
+
@inlinable
|
|
15
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
16
|
+
return Element.decodableKinds.union(.object)
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
@JavaScriptActor
|
|
14
20
|
@inlinable
|
|
15
21
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
16
22
|
-> [Element]
|
|
23
|
+
{
|
|
24
|
+
// Forwards to the unowned overload, which holds the implementation.
|
|
25
|
+
let runtime = copy runtime
|
|
26
|
+
return try value.withUnownedValue(in: runtime) { unownedValue in
|
|
27
|
+
return try decode(unownedValue, in: runtime)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@JavaScriptActor
|
|
32
|
+
@inlinable
|
|
33
|
+
public static func decode(_ value: borrowing JavaScriptUnownedValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
34
|
+
-> [Element]
|
|
17
35
|
{
|
|
18
36
|
// A non-array value is "arrayized" into a single-element array, so a caller that passes a
|
|
19
37
|
// scalar where an array is expected still works.
|
|
20
38
|
guard value.isArray() else {
|
|
21
39
|
return [try Element.decode(value, in: runtime)]
|
|
22
40
|
}
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
return try value.getArray().map { element in
|
|
41
|
+
// Each element is lent out unowned, so it's decoded without a `JavaScriptValue` per element.
|
|
42
|
+
return try value.getArray(in: runtime).mapUnowned { element in
|
|
26
43
|
return try Element.decode(element, in: runtime)
|
|
27
44
|
}
|
|
28
45
|
}
|
|
@@ -45,6 +62,11 @@ extension Array: JavaScriptEncodable where Element: JavaScriptEncodable {
|
|
|
45
62
|
// MARK: - Optional
|
|
46
63
|
|
|
47
64
|
extension Optional: JavaScriptDecodable where Wrapped: JavaScriptDecodable {
|
|
65
|
+
@inlinable
|
|
66
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
67
|
+
return Wrapped.decodableKinds.union([.null, .undefined])
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
// Optional copies nothing itself, so it overrides the zero-copy overload too and forwards the
|
|
49
71
|
// borrowed value straight through — a wrapped primitive argument stays fully zero-copy.
|
|
50
72
|
@JavaScriptActor
|
|
@@ -87,22 +109,42 @@ extension Optional: JavaScriptEncodable where Wrapped: JavaScriptEncodable {
|
|
|
87
109
|
// MARK: - Dictionary
|
|
88
110
|
|
|
89
111
|
extension Dictionary: JavaScriptDecodable where Key == String, Value: JavaScriptDecodable {
|
|
112
|
+
@inlinable
|
|
113
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
114
|
+
return .object
|
|
115
|
+
}
|
|
116
|
+
|
|
90
117
|
@JavaScriptActor
|
|
91
118
|
@inlinable
|
|
92
119
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
93
120
|
-> [String: Value]
|
|
94
121
|
{
|
|
95
|
-
|
|
122
|
+
// Forwards to the unowned overload, which holds the implementation.
|
|
123
|
+
let runtime = copy runtime
|
|
124
|
+
return try value.withUnownedValue(in: runtime) { unownedValue in
|
|
125
|
+
return try decode(unownedValue, in: runtime)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@JavaScriptActor
|
|
130
|
+
@inlinable
|
|
131
|
+
public static func decode(_ value: borrowing JavaScriptUnownedValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
132
|
+
-> [String: Value]
|
|
133
|
+
{
|
|
134
|
+
// Reads the object straight from the borrowed value and lends each property out unowned, so it's
|
|
135
|
+
// decoded without a `JavaScriptValue` per property.
|
|
136
|
+
let object = try value.asObject(in: runtime)
|
|
96
137
|
let keys = object.getPropertyNames()
|
|
97
138
|
var result = [String: Value](minimumCapacity: keys.count)
|
|
98
139
|
for key in keys {
|
|
99
|
-
let
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
140
|
+
let decoded: Value? = try object.withUnownedProperty(key) { property in
|
|
141
|
+
// Treat an `undefined`-valued property as an absent entry. Without this a non-optional
|
|
142
|
+
// `Value` would reject an object that simply omits the property as `undefined`.
|
|
143
|
+
return property.isUndefined() ? nil : try Value.decode(property, in: runtime)
|
|
144
|
+
}
|
|
145
|
+
if let decoded {
|
|
146
|
+
result[key] = decoded
|
|
104
147
|
}
|
|
105
|
-
result[key] = try Value.decode(property, in: runtime)
|
|
106
148
|
}
|
|
107
149
|
return result
|
|
108
150
|
}
|
|
@@ -9,6 +9,11 @@ import Foundation
|
|
|
9
9
|
// read/write rather than a single accessor.
|
|
10
10
|
|
|
11
11
|
extension Data: JavaScriptCodable {
|
|
12
|
+
@inlinable
|
|
13
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
14
|
+
return .object
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
@JavaScriptActor
|
|
13
18
|
@inlinable
|
|
14
19
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Data
|
|
@@ -9,22 +9,44 @@ import Foundation
|
|
|
9
9
|
// A `Date` is an absolute instant with no timezone/calendar; resolution is milliseconds.
|
|
10
10
|
|
|
11
11
|
extension Date: JavaScriptCodable {
|
|
12
|
+
@inlinable
|
|
13
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
14
|
+
return [.number, .string, .object]
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
@JavaScriptActor
|
|
13
18
|
@inlinable
|
|
14
19
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Date
|
|
15
20
|
{
|
|
16
|
-
//
|
|
17
|
-
|
|
21
|
+
// Forwards to the unowned overload, which holds the implementation.
|
|
22
|
+
let runtime = copy runtime
|
|
23
|
+
return try value.withUnownedValue(in: runtime) { unownedValue in
|
|
24
|
+
return try decode(unownedValue, in: runtime)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@JavaScriptActor
|
|
29
|
+
@inlinable
|
|
30
|
+
public static func decode(_ value: borrowing JavaScriptUnownedValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
31
|
+
-> Date
|
|
32
|
+
{
|
|
33
|
+
// The cheap tag checks come before the `instanceof` check, which needs a global lookup; a number or
|
|
34
|
+
// a string can't be a `Date`, so the order is behavior-neutral. Only the string branch copies the
|
|
35
|
+
// value, since the `Date` constructor takes owning arguments; a string is the rare input.
|
|
18
36
|
if value.isNumber() {
|
|
19
37
|
return try dateFromMilliseconds(value.getDouble())
|
|
20
38
|
}
|
|
21
39
|
if value.isString() {
|
|
22
40
|
let dateConstructor = try runtime.global().getPropertyAsFunction("Date")
|
|
23
|
-
let constructed = try dateConstructor.callAsConstructor(value.
|
|
41
|
+
let constructed = try dateConstructor.callAsConstructor(value.copied(in: runtime)).asObject()
|
|
24
42
|
return try dateFromMilliseconds(constructed.callFunction("getTime").asDouble())
|
|
25
43
|
}
|
|
26
|
-
if value.
|
|
27
|
-
|
|
44
|
+
if value.isObject() {
|
|
45
|
+
let dateConstructor = try runtime.global().getPropertyAsFunction("Date")
|
|
46
|
+
let object = value.getObject(in: runtime)
|
|
47
|
+
if object.instanceOf(dateConstructor) {
|
|
48
|
+
return try dateFromMilliseconds(object.callFunction("getTime").asDouble())
|
|
49
|
+
}
|
|
28
50
|
}
|
|
29
51
|
throw InvalidDateException()
|
|
30
52
|
}
|
|
@@ -15,6 +15,11 @@ import CoreGraphics
|
|
|
15
15
|
// MARK: - Bool
|
|
16
16
|
|
|
17
17
|
extension Bool: JavaScriptCodable {
|
|
18
|
+
@inlinable
|
|
19
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
20
|
+
return .bool
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
@JavaScriptActor
|
|
19
24
|
@inlinable
|
|
20
25
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Bool
|
|
@@ -40,6 +45,11 @@ extension Bool: JavaScriptCodable {
|
|
|
40
45
|
// MARK: - String
|
|
41
46
|
|
|
42
47
|
extension String: JavaScriptCodable {
|
|
48
|
+
@inlinable
|
|
49
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
50
|
+
return .string
|
|
51
|
+
}
|
|
52
|
+
|
|
43
53
|
@JavaScriptActor
|
|
44
54
|
@inlinable
|
|
45
55
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
@@ -74,6 +84,11 @@ extension String: JavaScriptCodable {
|
|
|
74
84
|
// undefined behavior — a native crash, not a catchable error.
|
|
75
85
|
|
|
76
86
|
extension Double: JavaScriptCodable {
|
|
87
|
+
@inlinable
|
|
88
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
89
|
+
return .number
|
|
90
|
+
}
|
|
91
|
+
|
|
77
92
|
@JavaScriptActor
|
|
78
93
|
@inlinable
|
|
79
94
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
@@ -100,6 +115,11 @@ extension Double: JavaScriptCodable {
|
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
extension Float: JavaScriptCodable {
|
|
118
|
+
@inlinable
|
|
119
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
120
|
+
return .number
|
|
121
|
+
}
|
|
122
|
+
|
|
103
123
|
@JavaScriptActor
|
|
104
124
|
@inlinable
|
|
105
125
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Float
|
|
@@ -123,6 +143,11 @@ extension Float: JavaScriptCodable {
|
|
|
123
143
|
}
|
|
124
144
|
|
|
125
145
|
extension CGFloat: JavaScriptCodable {
|
|
146
|
+
@inlinable
|
|
147
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
148
|
+
return .number
|
|
149
|
+
}
|
|
150
|
+
|
|
126
151
|
@JavaScriptActor
|
|
127
152
|
@inlinable
|
|
128
153
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
@@ -170,6 +195,11 @@ extension CGFloat: JavaScriptCodable {
|
|
|
170
195
|
// back, and a JS caller may pass either form.
|
|
171
196
|
|
|
172
197
|
extension Int: JavaScriptCodable {
|
|
198
|
+
@inlinable
|
|
199
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
200
|
+
return [.number, .bigint]
|
|
201
|
+
}
|
|
202
|
+
|
|
173
203
|
@JavaScriptActor
|
|
174
204
|
@inlinable
|
|
175
205
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Int {
|
|
@@ -192,6 +222,11 @@ extension Int: JavaScriptCodable {
|
|
|
192
222
|
}
|
|
193
223
|
|
|
194
224
|
extension Int8: JavaScriptCodable {
|
|
225
|
+
@inlinable
|
|
226
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
227
|
+
return .number
|
|
228
|
+
}
|
|
229
|
+
|
|
195
230
|
@JavaScriptActor
|
|
196
231
|
@inlinable
|
|
197
232
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Int8
|
|
@@ -215,6 +250,11 @@ extension Int8: JavaScriptCodable {
|
|
|
215
250
|
}
|
|
216
251
|
|
|
217
252
|
extension Int16: JavaScriptCodable {
|
|
253
|
+
@inlinable
|
|
254
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
255
|
+
return .number
|
|
256
|
+
}
|
|
257
|
+
|
|
218
258
|
@JavaScriptActor
|
|
219
259
|
@inlinable
|
|
220
260
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Int16
|
|
@@ -238,6 +278,11 @@ extension Int16: JavaScriptCodable {
|
|
|
238
278
|
}
|
|
239
279
|
|
|
240
280
|
extension Int32: JavaScriptCodable {
|
|
281
|
+
@inlinable
|
|
282
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
283
|
+
return .number
|
|
284
|
+
}
|
|
285
|
+
|
|
241
286
|
@JavaScriptActor
|
|
242
287
|
@inlinable
|
|
243
288
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Int32
|
|
@@ -261,6 +306,11 @@ extension Int32: JavaScriptCodable {
|
|
|
261
306
|
}
|
|
262
307
|
|
|
263
308
|
extension Int64: JavaScriptCodable {
|
|
309
|
+
@inlinable
|
|
310
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
311
|
+
return [.number, .bigint]
|
|
312
|
+
}
|
|
313
|
+
|
|
264
314
|
@JavaScriptActor
|
|
265
315
|
@inlinable
|
|
266
316
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Int64
|
|
@@ -284,6 +334,11 @@ extension Int64: JavaScriptCodable {
|
|
|
284
334
|
}
|
|
285
335
|
|
|
286
336
|
extension UInt: JavaScriptCodable {
|
|
337
|
+
@inlinable
|
|
338
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
339
|
+
return [.number, .bigint]
|
|
340
|
+
}
|
|
341
|
+
|
|
287
342
|
@JavaScriptActor
|
|
288
343
|
@inlinable
|
|
289
344
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> UInt
|
|
@@ -307,6 +362,11 @@ extension UInt: JavaScriptCodable {
|
|
|
307
362
|
}
|
|
308
363
|
|
|
309
364
|
extension UInt8: JavaScriptCodable {
|
|
365
|
+
@inlinable
|
|
366
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
367
|
+
return .number
|
|
368
|
+
}
|
|
369
|
+
|
|
310
370
|
@JavaScriptActor
|
|
311
371
|
@inlinable
|
|
312
372
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> UInt8
|
|
@@ -330,6 +390,11 @@ extension UInt8: JavaScriptCodable {
|
|
|
330
390
|
}
|
|
331
391
|
|
|
332
392
|
extension UInt16: JavaScriptCodable {
|
|
393
|
+
@inlinable
|
|
394
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
395
|
+
return .number
|
|
396
|
+
}
|
|
397
|
+
|
|
333
398
|
@JavaScriptActor
|
|
334
399
|
@inlinable
|
|
335
400
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
@@ -356,6 +421,11 @@ extension UInt16: JavaScriptCodable {
|
|
|
356
421
|
}
|
|
357
422
|
|
|
358
423
|
extension UInt32: JavaScriptCodable {
|
|
424
|
+
@inlinable
|
|
425
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
426
|
+
return .number
|
|
427
|
+
}
|
|
428
|
+
|
|
359
429
|
@JavaScriptActor
|
|
360
430
|
@inlinable
|
|
361
431
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
@@ -382,6 +452,11 @@ extension UInt32: JavaScriptCodable {
|
|
|
382
452
|
}
|
|
383
453
|
|
|
384
454
|
extension UInt64: JavaScriptCodable {
|
|
455
|
+
@inlinable
|
|
456
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
457
|
+
return [.number, .bigint]
|
|
458
|
+
}
|
|
459
|
+
|
|
385
460
|
@JavaScriptActor
|
|
386
461
|
@inlinable
|
|
387
462
|
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
public protocol JavaScriptDecodable {
|
|
14
14
|
/// Decodes an owning `JavaScriptValue` into `Self`.
|
|
15
15
|
///
|
|
16
|
-
///
|
|
17
|
-
///
|
|
18
|
-
///
|
|
16
|
+
/// Defaulted (see the extension below) to borrow the value and decode it through the
|
|
17
|
+
/// `JavaScriptUnownedValue` overload, so a conformer may implement just that one. A conformer must
|
|
18
|
+
/// implement at least one of the two overloads: each default forwards to the other, so with neither
|
|
19
|
+
/// implemented a decode recurses until the stack overflows.
|
|
19
20
|
@JavaScriptActor
|
|
20
21
|
static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Self
|
|
21
22
|
|
|
@@ -24,13 +25,35 @@ public protocol JavaScriptDecodable {
|
|
|
24
25
|
///
|
|
25
26
|
/// This is the argument-decode fast path: the value borrows the argument the
|
|
26
27
|
/// `JavaScriptValuesBuffer` already owns for the duration of the call. It is defaulted (see the
|
|
27
|
-
/// extension below)
|
|
28
|
-
/// can read straight from the borrowed value
|
|
28
|
+
/// extension below) to materialize an owning value and decode that, for conformers that implement
|
|
29
|
+
/// only the owning overload; types that can read straight from the borrowed value implement it to
|
|
30
|
+
/// stay zero-copy.
|
|
29
31
|
@JavaScriptActor
|
|
30
32
|
static func decode(_ value: borrowing JavaScriptUnownedValue, in runtime: borrowing JavaScriptRuntime) throws -> Self
|
|
33
|
+
|
|
34
|
+
/// The kinds of JavaScript value `decode` can accept.
|
|
35
|
+
///
|
|
36
|
+
/// A kind outside the set is definitive: `decode` would throw for a value of that kind. A kind in the
|
|
37
|
+
/// set only means the type may decode it, so `decode` can still throw, for example for an
|
|
38
|
+
/// out-of-range number or a missing record field. Code that picks between several types, like a
|
|
39
|
+
/// union, reads it to skip the ones that can't match without paying for a thrown error. Defaults to
|
|
40
|
+
/// `.all`, so a type that doesn't declare it is always tried.
|
|
41
|
+
static var decodableKinds: JavaScriptValueKinds { get }
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
extension JavaScriptDecodable {
|
|
45
|
+
/// Default owning decode: borrow the value and forward to the unowned overload, so a conformer that
|
|
46
|
+
/// implements only that one decodes owning values without a copy.
|
|
47
|
+
@JavaScriptActor
|
|
48
|
+
@inlinable
|
|
49
|
+
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Self
|
|
50
|
+
{
|
|
51
|
+
let runtime = copy runtime
|
|
52
|
+
return try value.withUnownedValue(in: runtime) { unownedValue in
|
|
53
|
+
return try decode(unownedValue, in: runtime)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
34
57
|
/// Default fast-path implementation: materialize an owning value and forward to the requirement.
|
|
35
58
|
/// This is the "explicitly copy" behavior for types that cannot (or need not) read directly from
|
|
36
59
|
/// the borrowed value.
|
|
@@ -44,4 +67,10 @@ extension JavaScriptDecodable {
|
|
|
44
67
|
{
|
|
45
68
|
return try decode(value.copied(in: runtime), in: runtime)
|
|
46
69
|
}
|
|
70
|
+
|
|
71
|
+
/// Default: every kind, so a conformer that doesn't declare its kinds is always tried.
|
|
72
|
+
@inlinable
|
|
73
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
74
|
+
return .all
|
|
75
|
+
}
|
|
47
76
|
}
|