expo-modules-jsi 58.0.5 → 58.0.7
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 +15 -0
- package/apple/Benchmarks/FreeFormBenchmarks.swift +61 -0
- package/apple/Benchmarks/HostObjectBenchmarks.swift +57 -0
- package/apple/Sources/ExpoModulesJSI/Coding/JavaScriptCodable+Any.swift +500 -0
- package/apple/Sources/ExpoModulesJSI/Coding/JavaScriptCodable+Primitives.swift +4 -6
- package/apple/Sources/ExpoModulesJSI/Protocols/JSIRepresentable.swift +2 -2
- package/apple/Sources/ExpoModulesJSI/Protocols/JavaScriptRepresentable.swift +2 -3
- package/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntime.swift +24 -17
- package/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntimeHandle.swift +67 -0
- package/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptValuesBuffer.swift +3 -2
- package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptArray.swift +53 -35
- package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptFunction.swift +2 -2
- package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptObject.swift +86 -58
- package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptValue.swift +73 -47
- package/apple/Sources/ExpoModulesJSI/Utilities/String+JSI.swift +10 -0
- package/apple/Sources/ExpoModulesJSI-Cxx/include/HostObject.h +2 -2
- package/apple/Sources/ExpoModulesJSI-Cxx/include/HostObjectCallbacks.h +17 -8
- package/apple/Sources/ExpoModulesJSI-Cxx/include/JSIUtils.h +20 -0
- package/apple/Tests/JavaScriptCodableAnyTests.swift +531 -0
- package/apple/Tests/JavaScriptRuntimeTests.swift +40 -0
- package/apple/Tests/JavaScriptValueTests.swift +18 -0
- package/apple/Tests/JavaScriptValuesBufferTests.swift +14 -0
- package/package.json +1 -1
|
@@ -6,32 +6,53 @@ internal import jsi
|
|
|
6
6
|
/// A Swift representation of a JavaScript object. Provides access to JavaScript object properties and methods,
|
|
7
7
|
/// supporting property access, modification, enumeration, prototype manipulation, and function calling.
|
|
8
8
|
public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
9
|
-
|
|
9
|
+
/// Handle to the runtime the object belongs to.
|
|
10
|
+
internal let runtimeHandle: JavaScriptRuntimeHandle
|
|
10
11
|
internal var pointee: facebook.jsi.Object
|
|
11
12
|
|
|
13
|
+
/// The runtime the object belongs to, or `nil` if it has been deallocated. Prefer ``jsiRuntime`` on
|
|
14
|
+
/// hot paths: it costs no reference counting.
|
|
15
|
+
internal var runtime: JavaScriptRuntime? {
|
|
16
|
+
return runtimeHandle.runtime
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// The engine runtime the object belongs to, or `nil` if the runtime has been deallocated.
|
|
20
|
+
internal var jsiRuntime: facebook.jsi.IRuntime? {
|
|
21
|
+
return runtimeHandle.pointee
|
|
22
|
+
}
|
|
23
|
+
|
|
12
24
|
/// Creates a new object in the given runtime.
|
|
13
|
-
public init(_ runtime: JavaScriptRuntime) {
|
|
25
|
+
public init(_ runtime: borrowing JavaScriptRuntime) {
|
|
14
26
|
self.init(runtime, facebook.jsi.Object(runtime.pointee))
|
|
15
27
|
}
|
|
16
28
|
|
|
17
29
|
/// Creates a new object from the dictionary whose values are representable in JS.
|
|
18
|
-
public init<DictValue: JavaScriptRepresentable>(
|
|
19
|
-
|
|
30
|
+
public init<DictValue: JavaScriptRepresentable>(
|
|
31
|
+
_ runtime: borrowing JavaScriptRuntime,
|
|
32
|
+
_ dictionary: [String: DictValue]
|
|
33
|
+
) {
|
|
34
|
+
self.runtimeHandle = runtime.handle
|
|
20
35
|
self.pointee = dictionary.toJavaScriptValue(in: runtime).getObject().pointee
|
|
21
36
|
}
|
|
22
37
|
|
|
23
38
|
/// Creates a new object from existing JSI object.
|
|
24
|
-
internal init(_ runtime: JavaScriptRuntime, _ object: consuming facebook.jsi.Object) {
|
|
25
|
-
self.
|
|
39
|
+
internal init(_ runtime: borrowing JavaScriptRuntime, _ object: consuming facebook.jsi.Object) {
|
|
40
|
+
self.runtimeHandle = runtime.handle
|
|
41
|
+
self.pointee = object
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Creates a new object from existing JSI object, which belongs to the runtime behind `runtimeHandle`.
|
|
45
|
+
internal init(_ runtimeHandle: JavaScriptRuntimeHandle, _ object: consuming facebook.jsi.Object) {
|
|
46
|
+
self.runtimeHandle = runtimeHandle
|
|
26
47
|
self.pointee = object
|
|
27
48
|
}
|
|
28
49
|
|
|
29
50
|
/// Result of `object instanceof constructor`, which tests if the prototype property of a constructor appears anywhere in the prototype chain of an object.
|
|
30
51
|
public func instanceOf(_ constructor: borrowing JavaScriptFunction) -> Bool {
|
|
31
|
-
guard let
|
|
52
|
+
guard let jsiRuntime else {
|
|
32
53
|
FatalError.runtimeLost()
|
|
33
54
|
}
|
|
34
|
-
return pointee.instanceOf(
|
|
55
|
+
return pointee.instanceOf(jsiRuntime, constructor.pointee)
|
|
35
56
|
}
|
|
36
57
|
|
|
37
58
|
/// Result of `object instanceof constructor`, which tests if the prototype property of a constructor appears anywhere in the prototype chain of an object.
|
|
@@ -41,34 +62,42 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
41
62
|
|
|
42
63
|
/// Equivalent to `Array.isArray()` in JS. If it returns `true`, then `getArray()` will succeed.
|
|
43
64
|
public func isArray() -> Bool {
|
|
44
|
-
guard let
|
|
65
|
+
guard let jsiRuntime else {
|
|
45
66
|
FatalError.runtimeLost()
|
|
46
67
|
}
|
|
47
|
-
return pointee.isArray(
|
|
68
|
+
return pointee.isArray(jsiRuntime)
|
|
48
69
|
}
|
|
49
70
|
|
|
50
71
|
/// Returns `true` if the object is callable. If so, then `getFunction()` will succeed.
|
|
51
72
|
public func isFunction() -> Bool {
|
|
52
|
-
guard let
|
|
73
|
+
guard let jsiRuntime else {
|
|
53
74
|
FatalError.runtimeLost()
|
|
54
75
|
}
|
|
55
|
-
return pointee.isFunction(
|
|
76
|
+
return pointee.isFunction(jsiRuntime)
|
|
56
77
|
}
|
|
57
78
|
|
|
58
79
|
/// Returns `true` if the object is backed by a `jsi::HostObject`, including host objects
|
|
59
80
|
/// created via `JavaScriptRuntime.createHostObject` and ones produced by other native code.
|
|
60
81
|
public func isHostObject() -> Bool {
|
|
61
|
-
guard let
|
|
82
|
+
guard let jsiRuntime else {
|
|
62
83
|
FatalError.runtimeLost()
|
|
63
84
|
}
|
|
64
|
-
return expo.isHostObject(
|
|
85
|
+
return expo.isHostObject(jsiRuntime, pointee)
|
|
65
86
|
}
|
|
66
87
|
|
|
67
88
|
public func isArrayBuffer() -> Bool {
|
|
89
|
+
guard let jsiRuntime else {
|
|
90
|
+
FatalError.runtimeLost()
|
|
91
|
+
}
|
|
92
|
+
return pointee.isArrayBuffer(jsiRuntime)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// Returns `true` if the object is a typed array, such as `Uint8Array`.
|
|
96
|
+
internal func isTypedArray() -> Bool {
|
|
68
97
|
guard let runtime else {
|
|
69
98
|
FatalError.runtimeLost()
|
|
70
99
|
}
|
|
71
|
-
return
|
|
100
|
+
return expo.isTypedArray(runtime.pointee, pointee)
|
|
72
101
|
}
|
|
73
102
|
|
|
74
103
|
/// Returns the object as an array buffer, or asserts if not an array buffer.
|
|
@@ -82,11 +111,11 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
82
111
|
|
|
83
112
|
/// Returns the object as an array, or asserts if not an array.
|
|
84
113
|
public func getArray() -> JavaScriptArray {
|
|
85
|
-
guard let
|
|
114
|
+
guard let jsiRuntime else {
|
|
86
115
|
FatalError.runtimeLost()
|
|
87
116
|
}
|
|
88
117
|
assert(isArray(), "Object is not an array")
|
|
89
|
-
return JavaScriptArray(
|
|
118
|
+
return JavaScriptArray(runtimeHandle, pointee.getArray(jsiRuntime))
|
|
90
119
|
}
|
|
91
120
|
|
|
92
121
|
/// Returns the object as a function, or asserts if not a function.
|
|
@@ -102,28 +131,28 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
102
131
|
|
|
103
132
|
/// Checks whether the object has a property with the given name.
|
|
104
133
|
public func hasProperty(_ name: String) -> Bool {
|
|
105
|
-
guard let
|
|
134
|
+
guard let jsiRuntime else {
|
|
106
135
|
FatalError.runtimeLost()
|
|
107
136
|
}
|
|
108
|
-
return pointee.hasProperty(
|
|
137
|
+
return pointee.hasProperty(jsiRuntime, name.toJSIPropNameID(in: jsiRuntime))
|
|
109
138
|
}
|
|
110
139
|
|
|
111
140
|
/// Returns the property of the object with the given name,
|
|
112
141
|
/// or `undefined` value if the name is not a property of the object.
|
|
113
142
|
public func getProperty(_ name: String) -> JavaScriptValue {
|
|
114
|
-
guard let
|
|
143
|
+
guard let jsiRuntime else {
|
|
115
144
|
FatalError.runtimeLost()
|
|
116
145
|
}
|
|
117
|
-
return JavaScriptValue(
|
|
146
|
+
return JavaScriptValue(runtimeHandle, pointee.getProperty(jsiRuntime, name.toJSIPropNameID(in: jsiRuntime)))
|
|
118
147
|
}
|
|
119
148
|
|
|
120
149
|
/// Returns the property of the object with the given prop name id,
|
|
121
150
|
/// or `undefined` value if the name is not a property of the object.
|
|
122
151
|
public func getProperty(_ propName: JavaScriptPropNameID) -> JavaScriptValue {
|
|
123
|
-
guard let
|
|
152
|
+
guard let jsiRuntime else {
|
|
124
153
|
FatalError.runtimeLost()
|
|
125
154
|
}
|
|
126
|
-
return JavaScriptValue(
|
|
155
|
+
return JavaScriptValue(runtimeHandle, pointee.getProperty(jsiRuntime, propName.pointee))
|
|
127
156
|
}
|
|
128
157
|
|
|
129
158
|
/// Accesses nested properties in a single subscript operation by traversing the object chain.
|
|
@@ -138,16 +167,15 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
138
167
|
/// - Note: Each intermediate value in the chain (except the last) must be an object.
|
|
139
168
|
/// If any intermediate value is not an object, the behavior is undefined and may crash.
|
|
140
169
|
public subscript(_ key: String, _ nestedKeys: String...) -> JavaScriptValue {
|
|
141
|
-
guard let
|
|
170
|
+
guard let jsiRuntime else {
|
|
142
171
|
FatalError.runtimeLost()
|
|
143
172
|
}
|
|
144
|
-
let jsiRuntime = runtime.pointee
|
|
145
173
|
var value = pointee.getProperty(jsiRuntime, key)
|
|
146
174
|
|
|
147
175
|
for key in nestedKeys {
|
|
148
176
|
value = value.getObject(jsiRuntime).getProperty(jsiRuntime, key)
|
|
149
177
|
}
|
|
150
|
-
return JavaScriptValue(
|
|
178
|
+
return JavaScriptValue(runtimeHandle, value)
|
|
151
179
|
}
|
|
152
180
|
|
|
153
181
|
/// Returns an array of the object's own enumerable property names.
|
|
@@ -156,7 +184,7 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
156
184
|
///
|
|
157
185
|
/// - Returns: An array of property names as strings
|
|
158
186
|
public func getPropertyNames() -> [String] {
|
|
159
|
-
guard let jsiRuntime
|
|
187
|
+
guard let jsiRuntime else {
|
|
160
188
|
FatalError.runtimeLost()
|
|
161
189
|
}
|
|
162
190
|
let propertyNames: facebook.jsi.Array = pointee.getPropertyNames(jsiRuntime)
|
|
@@ -209,32 +237,32 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
209
237
|
|
|
210
238
|
/// Returns a prototype of the object. Same as `Object.getPrototypeOf(object)` in JS.
|
|
211
239
|
public func getPrototype() -> JavaScriptValue {
|
|
212
|
-
guard let
|
|
240
|
+
guard let jsiRuntime else {
|
|
213
241
|
FatalError.runtimeLost()
|
|
214
242
|
}
|
|
215
|
-
return JavaScriptValue(
|
|
243
|
+
return JavaScriptValue(runtimeHandle, pointee.getPrototype(jsiRuntime))
|
|
216
244
|
}
|
|
217
245
|
|
|
218
246
|
/// Sets a prototype of the object. Same as `Object.setPrototypeOf(object, prototype)` in JS.
|
|
219
247
|
public func setPrototype(_ prototype: JavaScriptValue) {
|
|
220
|
-
guard let
|
|
248
|
+
guard let jsiRuntime else {
|
|
221
249
|
FatalError.runtimeLost()
|
|
222
250
|
}
|
|
223
|
-
pointee.setPrototype(
|
|
251
|
+
pointee.setPrototype(jsiRuntime, prototype.pointee)
|
|
224
252
|
}
|
|
225
253
|
|
|
226
254
|
// MARK: - Modifying object properties
|
|
227
255
|
|
|
228
256
|
public func setProperty(_ name: String, value: JavaScriptValue) {
|
|
229
|
-
guard let
|
|
257
|
+
guard let jsiRuntime else {
|
|
230
258
|
FatalError.runtimeLost()
|
|
231
259
|
}
|
|
232
260
|
// This specialization is to avoid copying the value; `asValue()` on `JavaScriptValue` needs to do a copy.
|
|
233
261
|
expo.setProperty(
|
|
234
|
-
|
|
262
|
+
jsiRuntime,
|
|
235
263
|
pointee,
|
|
236
|
-
name.toJSIPropNameID(in:
|
|
237
|
-
value.toJSIValue(in:
|
|
264
|
+
name.toJSIPropNameID(in: jsiRuntime),
|
|
265
|
+
value.toJSIValue(in: jsiRuntime)
|
|
238
266
|
)
|
|
239
267
|
}
|
|
240
268
|
|
|
@@ -247,22 +275,22 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
247
275
|
}
|
|
248
276
|
|
|
249
277
|
internal func setProperty<T: JavaScriptRepresentable>(_ name: String, value: consuming T) where T: JSIRepresentable {
|
|
250
|
-
guard let
|
|
278
|
+
guard let jsiRuntime else {
|
|
251
279
|
FatalError.runtimeLost()
|
|
252
280
|
}
|
|
253
|
-
let jsiValue = value.toJSIValue(in:
|
|
254
|
-
expo.setProperty(
|
|
281
|
+
let jsiValue = value.toJSIValue(in: jsiRuntime)
|
|
282
|
+
expo.setProperty(jsiRuntime, pointee, name.toJSIPropNameID(in: jsiRuntime), jsiValue)
|
|
255
283
|
}
|
|
256
284
|
|
|
257
285
|
public func setProperty(_ name: String, _ object: consuming JavaScriptObject) {
|
|
258
|
-
guard let
|
|
286
|
+
guard let jsiRuntime else {
|
|
259
287
|
FatalError.runtimeLost()
|
|
260
288
|
}
|
|
261
289
|
expo.setProperty(
|
|
262
|
-
|
|
290
|
+
jsiRuntime,
|
|
263
291
|
pointee,
|
|
264
|
-
name.toJSIPropNameID(in:
|
|
265
|
-
facebook.jsi.Value(
|
|
292
|
+
name.toJSIPropNameID(in: jsiRuntime),
|
|
293
|
+
facebook.jsi.Value(jsiRuntime, object.pointee)
|
|
266
294
|
)
|
|
267
295
|
}
|
|
268
296
|
|
|
@@ -314,10 +342,10 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
314
342
|
#if !os(macOS)
|
|
315
343
|
// TODO: remove when bumping to react-native-macos 0.86
|
|
316
344
|
public func deleteProperty(_ name: String) {
|
|
317
|
-
guard let
|
|
345
|
+
guard let jsiRuntime else {
|
|
318
346
|
FatalError.runtimeLost()
|
|
319
347
|
}
|
|
320
|
-
pointee.deleteProperty(
|
|
348
|
+
pointee.deleteProperty(jsiRuntime, name.toJSIPropNameID(in: jsiRuntime))
|
|
321
349
|
}
|
|
322
350
|
#endif
|
|
323
351
|
|
|
@@ -381,18 +409,18 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
381
409
|
// MARK: - Conversions
|
|
382
410
|
|
|
383
411
|
public func asValue() -> JavaScriptValue {
|
|
384
|
-
guard let
|
|
412
|
+
guard let jsiRuntime else {
|
|
385
413
|
FatalError.runtimeLost()
|
|
386
414
|
}
|
|
387
|
-
return JavaScriptValue(
|
|
415
|
+
return JavaScriptValue(runtimeHandle, facebook.jsi.Value(jsiRuntime, pointee))
|
|
388
416
|
}
|
|
389
417
|
|
|
390
418
|
/// Returns the object as a `facebook.jsi.Value` instance.
|
|
391
419
|
internal func asJSIValue() -> facebook.jsi.Value {
|
|
392
|
-
guard let
|
|
420
|
+
guard let jsiRuntime else {
|
|
393
421
|
FatalError.runtimeLost()
|
|
394
422
|
}
|
|
395
|
-
return facebook.jsi.Value(
|
|
423
|
+
return facebook.jsi.Value(jsiRuntime, pointee)
|
|
396
424
|
}
|
|
397
425
|
|
|
398
426
|
/// Provides scoped access to a raw pointer to the underlying `facebook.jsi.Object`.
|
|
@@ -428,10 +456,10 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
428
456
|
/// wrapper: C++-produced states carry a null context and are unrecoverable from
|
|
429
457
|
/// the Swift side by design.
|
|
430
458
|
public func hasNativeState() -> Bool {
|
|
431
|
-
guard let
|
|
459
|
+
guard let jsiRuntime else {
|
|
432
460
|
FatalError.runtimeLost()
|
|
433
461
|
}
|
|
434
|
-
return expo.hasNativeState(
|
|
462
|
+
return expo.hasNativeState(jsiRuntime, pointee)
|
|
435
463
|
}
|
|
436
464
|
|
|
437
465
|
/// Returns the Swift `JavaScriptNativeState` wrapper attached to this object via
|
|
@@ -439,10 +467,10 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
439
467
|
/// produced C++-side without a Swift back-pointer, or its concrete type doesn't
|
|
440
468
|
/// match `T`.
|
|
441
469
|
public func getNativeState<T: JavaScriptNativeState>(as: T.Type = JavaScriptNativeState.self) -> T? {
|
|
442
|
-
guard let
|
|
470
|
+
guard let jsiRuntime else {
|
|
443
471
|
FatalError.runtimeLost()
|
|
444
472
|
}
|
|
445
|
-
guard let cxxNativeState = expo.getExpoNativeState(
|
|
473
|
+
guard let cxxNativeState = expo.getExpoNativeState(jsiRuntime, pointee) else {
|
|
446
474
|
return nil
|
|
447
475
|
}
|
|
448
476
|
return T.from(cxx: cxxNativeState)
|
|
@@ -452,27 +480,27 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
|
|
|
452
480
|
/// Creates a new shared_ptr to the object managed by state, which will live until the value at this property becomes unreachable.
|
|
453
481
|
/// - TODO: throw a type error if this object is a proxy or host object.
|
|
454
482
|
public func setNativeState<T: JavaScriptNativeState>(_ nativeState: T) {
|
|
455
|
-
guard let
|
|
483
|
+
guard let jsiRuntime else {
|
|
456
484
|
FatalError.runtimeLost()
|
|
457
485
|
}
|
|
458
|
-
expo.setNativeState(
|
|
486
|
+
expo.setNativeState(jsiRuntime, self.pointee, nativeState.acquireShared())
|
|
459
487
|
}
|
|
460
488
|
|
|
461
489
|
/// Unsets the native state of this object.
|
|
462
490
|
public func unsetNativeState() {
|
|
463
|
-
guard let
|
|
491
|
+
guard let jsiRuntime else {
|
|
464
492
|
FatalError.runtimeLost()
|
|
465
493
|
}
|
|
466
|
-
expo.unsetNativeState(
|
|
494
|
+
expo.unsetNativeState(jsiRuntime, pointee)
|
|
467
495
|
}
|
|
468
496
|
|
|
469
497
|
// MARK: - Memory pressure
|
|
470
498
|
|
|
471
499
|
public func setExternalMemoryPressure(_ size: Int) {
|
|
472
|
-
guard let
|
|
500
|
+
guard let jsiRuntime else {
|
|
473
501
|
FatalError.runtimeLost()
|
|
474
502
|
}
|
|
475
|
-
pointee.setExternalMemoryPressure(
|
|
503
|
+
pointee.setExternalMemoryPressure(jsiRuntime, size)
|
|
476
504
|
}
|
|
477
505
|
|
|
478
506
|
// MARK: - Equality
|
|
@@ -7,21 +7,38 @@ internal import jsi
|
|
|
7
7
|
/// this one is a reference type so can be safely captured in closures, passed to other isolation context,
|
|
8
8
|
/// and stored in containers that don't support non-copyable types etc.
|
|
9
9
|
public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
10
|
-
|
|
10
|
+
/// Handle to the runtime the value belongs to. `nil` only for runtime-free values (undefined, null,
|
|
11
|
+
/// booleans and numbers).
|
|
12
|
+
internal let runtimeHandle: JavaScriptRuntimeHandle?
|
|
11
13
|
internal let pointee: facebook.jsi.Value
|
|
12
14
|
|
|
13
|
-
///
|
|
14
|
-
///
|
|
15
|
-
internal
|
|
16
|
-
|
|
15
|
+
/// The runtime the value belongs to, or `nil` if it has been deallocated or the value is runtime-free.
|
|
16
|
+
/// Prefer ``jsiRuntime`` on hot paths: it costs no reference counting.
|
|
17
|
+
internal var runtime: JavaScriptRuntime? {
|
|
18
|
+
return runtimeHandle?.runtime
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// The engine runtime the value belongs to, or `nil` if the runtime has been deallocated or the value
|
|
22
|
+
/// is runtime-free.
|
|
23
|
+
internal var jsiRuntime: facebook.jsi.IRuntime? {
|
|
24
|
+
return runtimeHandle?.pointee
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Takes ownership of the given JSI value, which belongs to the runtime behind `runtimeHandle`.
|
|
28
|
+
internal init(_ runtimeHandle: JavaScriptRuntimeHandle, _ pointee: consuming facebook.jsi.Value) {
|
|
29
|
+
self.runtimeHandle = runtimeHandle
|
|
30
|
+
self.pointee = pointee
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Takes ownership of the given runtime-free JSI value (undefined, null, a boolean or a number).
|
|
34
|
+
internal init(_ pointee: consuming facebook.jsi.Value) {
|
|
35
|
+
self.runtimeHandle = nil
|
|
17
36
|
self.pointee = pointee
|
|
18
37
|
}
|
|
19
38
|
|
|
20
|
-
/// Takes ownership of the given JSI value.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
internal init(_ runtime: JavaScriptRuntime, _ pointee: consuming facebook.jsi.Value) {
|
|
24
|
-
self.runtime = runtime
|
|
39
|
+
/// Takes ownership of the given JSI value.
|
|
40
|
+
internal init(_ runtime: borrowing JavaScriptRuntime, _ pointee: consuming facebook.jsi.Value) {
|
|
41
|
+
self.runtimeHandle = runtime.handle
|
|
25
42
|
self.pointee = pointee
|
|
26
43
|
}
|
|
27
44
|
|
|
@@ -29,51 +46,51 @@ public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
|
29
46
|
/// The `copying:` label keeps this initializer from being picked for temporaries: with a plain
|
|
30
47
|
/// second argument, a non-optional runtime used to win overload resolution over the consuming
|
|
31
48
|
/// initializer above, cloning every freshly returned value instead of taking it over.
|
|
32
|
-
internal init(_ runtime: JavaScriptRuntime, copying pointee: borrowing facebook.jsi.Value) {
|
|
33
|
-
self.
|
|
49
|
+
internal init(_ runtime: borrowing JavaScriptRuntime, copying pointee: borrowing facebook.jsi.Value) {
|
|
50
|
+
self.runtimeHandle = runtime.handle
|
|
34
51
|
self.pointee = facebook.jsi.Value(runtime.pointee, pointee)
|
|
35
52
|
}
|
|
36
53
|
|
|
37
54
|
/// Creates a boolean JS value.
|
|
38
|
-
public init(_ runtime: JavaScriptRuntime, _ bool: Bool) {
|
|
39
|
-
self.
|
|
55
|
+
public init(_ runtime: borrowing JavaScriptRuntime, _ bool: Bool) {
|
|
56
|
+
self.runtimeHandle = runtime.handle
|
|
40
57
|
self.pointee = facebook.jsi.Value(bool)
|
|
41
58
|
}
|
|
42
59
|
|
|
43
60
|
/// Creates a string JS value.
|
|
44
|
-
public init(_ runtime: JavaScriptRuntime, _ string: String) {
|
|
45
|
-
self.
|
|
61
|
+
public init(_ runtime: borrowing JavaScriptRuntime, _ string: String) {
|
|
62
|
+
self.runtimeHandle = runtime.handle
|
|
46
63
|
self.pointee = string.toJSIValue(in: runtime.pointee)
|
|
47
64
|
}
|
|
48
65
|
|
|
49
66
|
/// Creates a BigInt JS value from an Int64.
|
|
50
|
-
public init(_ runtime: JavaScriptRuntime, bigInt: Int64) {
|
|
51
|
-
self.
|
|
67
|
+
public init(_ runtime: borrowing JavaScriptRuntime, bigInt: Int64) {
|
|
68
|
+
self.runtimeHandle = runtime.handle
|
|
52
69
|
self.pointee = facebook.jsi.Value(runtime.pointee, facebook.jsi.BigInt.fromInt64(runtime.pointee, bigInt))
|
|
53
70
|
}
|
|
54
71
|
|
|
55
72
|
/// Creates a BigInt JS value from a UInt64.
|
|
56
|
-
public init(_ runtime: JavaScriptRuntime, bigInt: UInt64) {
|
|
57
|
-
self.
|
|
73
|
+
public init(_ runtime: borrowing JavaScriptRuntime, bigInt: UInt64) {
|
|
74
|
+
self.runtimeHandle = runtime.handle
|
|
58
75
|
self.pointee = facebook.jsi.Value(runtime.pointee, facebook.jsi.BigInt.fromUint64(runtime.pointee, bigInt))
|
|
59
76
|
}
|
|
60
77
|
|
|
61
78
|
/// Creates a JS value from a JS representable.
|
|
62
|
-
public init(_ runtime: JavaScriptRuntime, _ value: JavaScriptRepresentable) {
|
|
63
|
-
self.
|
|
79
|
+
public init(_ runtime: borrowing JavaScriptRuntime, _ value: JavaScriptRepresentable) {
|
|
80
|
+
self.runtimeHandle = runtime.handle
|
|
64
81
|
self.pointee = value.toJavaScriptValue(in: runtime).toJSIValue(in: runtime.pointee)
|
|
65
82
|
}
|
|
66
83
|
|
|
67
84
|
/// Creates a JS value from a JSI representable.
|
|
68
|
-
internal init(_ runtime: JavaScriptRuntime, _ value: JSIRepresentable) {
|
|
69
|
-
self.
|
|
85
|
+
internal init(_ runtime: borrowing JavaScriptRuntime, _ value: JSIRepresentable) {
|
|
86
|
+
self.runtimeHandle = runtime.handle
|
|
70
87
|
self.pointee = value.toJSIValue(in: runtime.pointee)
|
|
71
88
|
}
|
|
72
89
|
|
|
73
90
|
/// Copies the value.
|
|
74
91
|
public func copy() -> JavaScriptValue {
|
|
75
|
-
if let
|
|
76
|
-
return JavaScriptValue(
|
|
92
|
+
if let runtimeHandle, let jsiRuntime = runtimeHandle.pointee {
|
|
93
|
+
return JavaScriptValue(runtimeHandle, facebook.jsi.Value(jsiRuntime, pointee))
|
|
77
94
|
}
|
|
78
95
|
// Some simple value kinds do not require the runtime.
|
|
79
96
|
switch kind {
|
|
@@ -82,9 +99,9 @@ public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
|
82
99
|
case .null:
|
|
83
100
|
return .null
|
|
84
101
|
case .bool:
|
|
85
|
-
return .init(
|
|
102
|
+
return .init(facebook.jsi.Value(getBool()))
|
|
86
103
|
case .number:
|
|
87
|
-
return .init(
|
|
104
|
+
return .init(facebook.jsi.Value(getDouble()))
|
|
88
105
|
default:
|
|
89
106
|
FatalError.runtimeLost()
|
|
90
107
|
}
|
|
@@ -137,21 +154,21 @@ public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
|
137
154
|
}
|
|
138
155
|
|
|
139
156
|
public func isArray() -> Bool {
|
|
140
|
-
guard let jsiRuntime
|
|
157
|
+
guard let jsiRuntime else {
|
|
141
158
|
FatalError.runtimeLost()
|
|
142
159
|
}
|
|
143
160
|
return pointee.isObject() && pointee.getObject(jsiRuntime).isArray(jsiRuntime)
|
|
144
161
|
}
|
|
145
162
|
|
|
146
163
|
public func isFunction() -> Bool {
|
|
147
|
-
guard let jsiRuntime
|
|
164
|
+
guard let jsiRuntime else {
|
|
148
165
|
FatalError.runtimeLost()
|
|
149
166
|
}
|
|
150
167
|
return pointee.isObject() && pointee.getObject(jsiRuntime).isFunction(jsiRuntime)
|
|
151
168
|
}
|
|
152
169
|
|
|
153
170
|
public func isTypedArray() -> Bool {
|
|
154
|
-
guard let jsiRuntime
|
|
171
|
+
guard let jsiRuntime else {
|
|
155
172
|
FatalError.runtimeLost()
|
|
156
173
|
}
|
|
157
174
|
return pointee.isObject() && expo.isTypedArray(jsiRuntime, pointee.getObject(jsiRuntime))
|
|
@@ -159,7 +176,7 @@ public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
|
159
176
|
|
|
160
177
|
/// Checks whether the value is an `ArrayBuffer`.
|
|
161
178
|
public func isArrayBuffer() -> Bool {
|
|
162
|
-
guard let jsiRuntime
|
|
179
|
+
guard let jsiRuntime else {
|
|
163
180
|
FatalError.runtimeLost()
|
|
164
181
|
}
|
|
165
182
|
return pointee.isObject() && pointee.getObject(jsiRuntime).isArrayBuffer(jsiRuntime)
|
|
@@ -258,7 +275,7 @@ public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
|
258
275
|
|
|
259
276
|
/// Returns the value as a string, or asserts if not a string.
|
|
260
277
|
public func getString() -> String {
|
|
261
|
-
guard let jsiRuntime
|
|
278
|
+
guard let jsiRuntime else {
|
|
262
279
|
FatalError.runtimeLost()
|
|
263
280
|
}
|
|
264
281
|
assert(isString(), "Value is not a string")
|
|
@@ -276,20 +293,20 @@ public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
|
276
293
|
|
|
277
294
|
/// Returns the value as an object, or asserts if not an object.
|
|
278
295
|
public func getObject() -> JavaScriptObject {
|
|
279
|
-
guard let
|
|
296
|
+
guard let runtimeHandle, let jsiRuntime = runtimeHandle.pointee else {
|
|
280
297
|
FatalError.runtimeLost()
|
|
281
298
|
}
|
|
282
299
|
assert(isObject(), "Value is not an object")
|
|
283
|
-
return JavaScriptObject(
|
|
300
|
+
return JavaScriptObject(runtimeHandle, pointee.getObject(jsiRuntime))
|
|
284
301
|
}
|
|
285
302
|
|
|
286
303
|
/// Returns the value as an array, or asserts if not an array.
|
|
287
304
|
public func getArray() -> JavaScriptArray {
|
|
288
|
-
guard let
|
|
305
|
+
guard let runtimeHandle, let jsiRuntime = runtimeHandle.pointee else {
|
|
289
306
|
FatalError.runtimeLost()
|
|
290
307
|
}
|
|
291
308
|
assert(isArray(), "Value is not an array")
|
|
292
|
-
return JavaScriptArray(
|
|
309
|
+
return JavaScriptArray(runtimeHandle, pointee.getObject(jsiRuntime).getArray(jsiRuntime))
|
|
293
310
|
}
|
|
294
311
|
|
|
295
312
|
/// Returns the value as a function, or asserts if not a function.
|
|
@@ -426,7 +443,7 @@ public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
|
426
443
|
|
|
427
444
|
/// Returns a string representing the value. Same as calling `toString()` in JS.
|
|
428
445
|
public func toString() -> String {
|
|
429
|
-
guard let jsiRuntime
|
|
446
|
+
guard let jsiRuntime else {
|
|
430
447
|
FatalError.runtimeLost()
|
|
431
448
|
}
|
|
432
449
|
return String(jsiString: pointee.toString(jsiRuntime), in: jsiRuntime)
|
|
@@ -536,8 +553,8 @@ public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
|
536
553
|
}
|
|
537
554
|
|
|
538
555
|
internal func asJSIValue() -> facebook.jsi.Value {
|
|
539
|
-
if let
|
|
540
|
-
return facebook.jsi.Value(
|
|
556
|
+
if let jsiRuntime {
|
|
557
|
+
return facebook.jsi.Value(jsiRuntime, pointee)
|
|
541
558
|
}
|
|
542
559
|
// Some simple value kinds do not require the runtime.
|
|
543
560
|
switch kind {
|
|
@@ -597,7 +614,7 @@ public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
|
597
614
|
|
|
598
615
|
/// Tests whether two values are strictly equal, according to https://262.ecma-international.org/11.0/#sec-strict-equality-comparison
|
|
599
616
|
public func isEqual(to another: JavaScriptValue) -> Bool {
|
|
600
|
-
if let jsiRuntime =
|
|
617
|
+
if let jsiRuntime = jsiRuntime ?? another.jsiRuntime {
|
|
601
618
|
return facebook.jsi.Value.strictEquals(jsiRuntime, pointee, another.pointee)
|
|
602
619
|
}
|
|
603
620
|
// Some types don't have to be tied to any runtime. Since `strictEquals` needs a runtime, we need to handle this case ourselves.
|
|
@@ -659,20 +676,20 @@ public final class JavaScriptValue: JavaScriptType, Equatable, Escapable {
|
|
|
659
676
|
/// This is a lightweight way to create a boolean true value that can be used in contexts
|
|
660
677
|
/// where a runtime is not available or needed.
|
|
661
678
|
public static func `true`() -> JavaScriptValue {
|
|
662
|
-
return JavaScriptValue(
|
|
679
|
+
return JavaScriptValue(facebook.jsi.Value(true))
|
|
663
680
|
}
|
|
664
681
|
|
|
665
682
|
/// This is a lightweight way to create a boolean false value that can be used in contexts
|
|
666
683
|
/// where a runtime is not available or needed.
|
|
667
684
|
public static func `false`() -> JavaScriptValue {
|
|
668
|
-
return JavaScriptValue(
|
|
685
|
+
return JavaScriptValue(facebook.jsi.Value(false))
|
|
669
686
|
}
|
|
670
687
|
|
|
671
688
|
/// This is a lightweight way to create a numeric value that can be used in contexts
|
|
672
689
|
/// where a runtime is not available or needed. JavaScript numbers are represented
|
|
673
690
|
/// as double-precision floating-point values following the IEEE 754 standard.
|
|
674
691
|
public static func number(_ number: Double) -> JavaScriptValue {
|
|
675
|
-
return JavaScriptValue(
|
|
692
|
+
return JavaScriptValue(facebook.jsi.Value(number))
|
|
676
693
|
}
|
|
677
694
|
|
|
678
695
|
public static func representing(value: JavaScriptRepresentable, in runtime: JavaScriptRuntime) -> JavaScriptValue {
|
|
@@ -715,9 +732,18 @@ extension JavaScriptValue: JSIRepresentable {
|
|
|
715
732
|
extension JavaScriptValue {
|
|
716
733
|
public struct TypeError: Error, CustomStringConvertible {
|
|
717
734
|
let type: any ~Copyable.Type
|
|
735
|
+
/// A specific explanation that replaces the generic message, when the failure needs one.
|
|
736
|
+
var reason: String? = nil
|
|
737
|
+
/// Where inside the converted value the failure happened, such as `.user.tags[1]`. Empty when it's
|
|
738
|
+
/// the value itself.
|
|
739
|
+
var path: String = ""
|
|
718
740
|
|
|
719
741
|
public var description: String {
|
|
720
|
-
|
|
742
|
+
let message = "TypeError: \(reason ?? "Value cannot be represented as \(type)")"
|
|
743
|
+
guard !path.isEmpty else {
|
|
744
|
+
return message
|
|
745
|
+
}
|
|
746
|
+
return "\(message) (path: \(path.hasPrefix(".") ? path.dropFirst() : Substring(path)))"
|
|
721
747
|
}
|
|
722
748
|
}
|
|
723
749
|
}
|
|
@@ -725,6 +751,6 @@ extension JavaScriptValue {
|
|
|
725
751
|
// Shared instances behind `JavaScriptValue.undefined` and `JavaScriptValue.null`; see the comment on
|
|
726
752
|
// those accessors for why these are globals.
|
|
727
753
|
@usableFromInline
|
|
728
|
-
internal let sharedUndefinedValue = JavaScriptValue(
|
|
754
|
+
internal let sharedUndefinedValue = JavaScriptValue(facebook.jsi.Value.undefined())
|
|
729
755
|
@usableFromInline
|
|
730
|
-
internal let sharedNullValue = JavaScriptValue(
|
|
756
|
+
internal let sharedNullValue = JavaScriptValue(facebook.jsi.Value.null())
|
|
@@ -25,6 +25,16 @@ extension String {
|
|
|
25
25
|
self = result
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/// Builds a Swift `String` from a `jsi::PropNameID` the same way as `init(jsiString:in:)`, reading
|
|
29
|
+
/// the engine's internal representation without materializing a `std::string` first.
|
|
30
|
+
internal init(jsiPropNameID: borrowing facebook.jsi.PropNameID, in runtime: facebook.jsi.IRuntime) {
|
|
31
|
+
var result = ""
|
|
32
|
+
withUnsafeMutablePointer(to: &result) { resultPtr in
|
|
33
|
+
expo.getPropNameIdData(runtime, jsiPropNameID, UnsafeMutableRawPointer(resultPtr), appendEngineStringChunk)
|
|
34
|
+
}
|
|
35
|
+
self = result
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
/// Builds a `jsi::PropNameID` from the string's UTF-8 bytes. This is how every String-keyed
|
|
29
39
|
/// property API turns its name into a key: JSI's `const char*` overloads treat the bytes as ASCII
|
|
30
40
|
/// and would mangle any non-ASCII name.
|
|
@@ -26,7 +26,7 @@ public:
|
|
|
26
26
|
jsi::Value result;
|
|
27
27
|
// If the Swift getter stored a pending error, rethrow its JSError directly
|
|
28
28
|
// to preserve all properties (message, code, stack, etc.).
|
|
29
|
-
if (_callbacks.get(name
|
|
29
|
+
if (_callbacks.get(name, result)) {
|
|
30
30
|
if (auto *error = CppError::getCurrent()) {
|
|
31
31
|
throw error->release();
|
|
32
32
|
}
|
|
@@ -39,7 +39,7 @@ public:
|
|
|
39
39
|
// `jsi::JSError` directly and the `CppError` check below is never reached.
|
|
40
40
|
// For writable host objects, a throwing Swift setter routes its error through
|
|
41
41
|
// `CppError`'s thread-local slot, which we drain and rethrow here.
|
|
42
|
-
if (_callbacks.set(runtime, name
|
|
42
|
+
if (_callbacks.set(runtime, name, value)) {
|
|
43
43
|
if (auto *error = CppError::getCurrent()) {
|
|
44
44
|
throw error->release();
|
|
45
45
|
}
|