expo-modules-jsi 58.0.4 → 58.0.6

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +9 -5
  2. package/apple/Benchmarks/HostObjectBenchmarks.swift +57 -0
  3. package/apple/Sources/ExpoModulesJSI/Coding/JavaScriptCodable+Primitives.swift +4 -6
  4. package/apple/Sources/ExpoModulesJSI/Protocols/JSIRepresentable.swift +2 -2
  5. package/apple/Sources/ExpoModulesJSI/Protocols/JavaScriptRepresentable.swift +2 -3
  6. package/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntime.swift +24 -17
  7. package/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntimeHandle.swift +67 -0
  8. package/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptValuesBuffer.swift +3 -2
  9. package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptArray.swift +53 -35
  10. package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptFunction.swift +2 -2
  11. package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptObject.swift +79 -59
  12. package/apple/Sources/ExpoModulesJSI/Runtime/Values/JavaScriptValue.swift +63 -46
  13. package/apple/Sources/ExpoModulesJSI/Utilities/String+JSI.swift +10 -0
  14. package/apple/Sources/ExpoModulesJSI-Cxx/include/HostObject.h +2 -2
  15. package/apple/Sources/ExpoModulesJSI-Cxx/include/HostObjectCallbacks.h +17 -8
  16. package/apple/Sources/ExpoModulesJSI-Cxx/include/JSIUtils.h +20 -0
  17. package/apple/Tests/JavaScriptRuntimeTests.swift +40 -0
  18. package/apple/Tests/JavaScriptValueTests.swift +18 -0
  19. package/apple/Tests/JavaScriptValuesBufferTests.swift +14 -0
  20. package/package.json +1 -2
  21. package/apple/.generated/module.modulemap +0 -6
  22. package/apple/Products/ExpoModulesJSI.xcframework/Info.plist +0 -107
  23. package/apple/Products/ExpoModulesJSI.xcframework/ios-arm64/.build-hash +0 -0
  24. package/apple/Products/ExpoModulesJSI.xcframework/ios-arm64/ExpoModulesJSI.framework/ExpoModulesJSI +0 -0
  25. package/apple/Products/ExpoModulesJSI.xcframework/ios-arm64_x86_64-maccatalyst/.build-hash +0 -0
  26. package/apple/Products/ExpoModulesJSI.xcframework/ios-arm64_x86_64-maccatalyst/ExpoModulesJSI.framework/ExpoModulesJSI +0 -0
  27. package/apple/Products/ExpoModulesJSI.xcframework/ios-arm64_x86_64-simulator/.build-hash +0 -0
  28. package/apple/Products/ExpoModulesJSI.xcframework/ios-arm64_x86_64-simulator/ExpoModulesJSI.framework/ExpoModulesJSI +0 -0
  29. package/apple/Products/ExpoModulesJSI.xcframework/macos-arm64_x86_64/.build-hash +0 -0
  30. package/apple/Products/ExpoModulesJSI.xcframework/macos-arm64_x86_64/ExpoModulesJSI.framework/ExpoModulesJSI +0 -0
  31. package/apple/Products/ExpoModulesJSI.xcframework/tvos-arm64/.build-hash +0 -0
  32. package/apple/Products/ExpoModulesJSI.xcframework/tvos-arm64/ExpoModulesJSI.framework/ExpoModulesJSI +0 -0
  33. package/apple/Products/ExpoModulesJSI.xcframework/tvos-arm64_x86_64-simulator/.build-hash +0 -0
  34. package/apple/Products/ExpoModulesJSI.xcframework/tvos-arm64_x86_64-simulator/ExpoModulesJSI.framework/ExpoModulesJSI +0 -0
@@ -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
- internal weak let runtime: JavaScriptRuntime?
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>(_ runtime: JavaScriptRuntime, _ dictionary: [String: DictValue]) {
19
- self.runtime = runtime
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.runtime = runtime
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 runtime else {
52
+ guard let jsiRuntime else {
32
53
  FatalError.runtimeLost()
33
54
  }
34
- return pointee.instanceOf(runtime.pointee, constructor.pointee)
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,34 @@ 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 runtime else {
65
+ guard let jsiRuntime else {
45
66
  FatalError.runtimeLost()
46
67
  }
47
- return pointee.isArray(runtime.pointee)
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 runtime else {
73
+ guard let jsiRuntime else {
53
74
  FatalError.runtimeLost()
54
75
  }
55
- return pointee.isFunction(runtime.pointee)
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 runtime else {
82
+ guard let jsiRuntime else {
62
83
  FatalError.runtimeLost()
63
84
  }
64
- return expo.isHostObject(runtime.pointee, pointee)
85
+ return expo.isHostObject(jsiRuntime, pointee)
65
86
  }
66
87
 
67
88
  public func isArrayBuffer() -> Bool {
68
- guard let runtime else {
89
+ guard let jsiRuntime else {
69
90
  FatalError.runtimeLost()
70
91
  }
71
- return pointee.isArrayBuffer(runtime.pointee)
92
+ return pointee.isArrayBuffer(jsiRuntime)
72
93
  }
73
94
 
74
95
  /// Returns the object as an array buffer, or asserts if not an array buffer.
@@ -82,11 +103,11 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
82
103
 
83
104
  /// Returns the object as an array, or asserts if not an array.
84
105
  public func getArray() -> JavaScriptArray {
85
- guard let runtime else {
106
+ guard let jsiRuntime else {
86
107
  FatalError.runtimeLost()
87
108
  }
88
109
  assert(isArray(), "Object is not an array")
89
- return JavaScriptArray(runtime, pointee.getArray(runtime.pointee))
110
+ return JavaScriptArray(runtimeHandle, pointee.getArray(jsiRuntime))
90
111
  }
91
112
 
92
113
  /// Returns the object as a function, or asserts if not a function.
@@ -102,28 +123,28 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
102
123
 
103
124
  /// Checks whether the object has a property with the given name.
104
125
  public func hasProperty(_ name: String) -> Bool {
105
- guard let runtime else {
126
+ guard let jsiRuntime else {
106
127
  FatalError.runtimeLost()
107
128
  }
108
- return pointee.hasProperty(runtime.pointee, name.toJSIPropNameID(in: runtime.pointee))
129
+ return pointee.hasProperty(jsiRuntime, name.toJSIPropNameID(in: jsiRuntime))
109
130
  }
110
131
 
111
132
  /// Returns the property of the object with the given name,
112
133
  /// or `undefined` value if the name is not a property of the object.
113
134
  public func getProperty(_ name: String) -> JavaScriptValue {
114
- guard let runtime else {
135
+ guard let jsiRuntime else {
115
136
  FatalError.runtimeLost()
116
137
  }
117
- return JavaScriptValue(runtime, pointee.getProperty(runtime.pointee, name.toJSIPropNameID(in: runtime.pointee)))
138
+ return JavaScriptValue(runtimeHandle, pointee.getProperty(jsiRuntime, name.toJSIPropNameID(in: jsiRuntime)))
118
139
  }
119
140
 
120
141
  /// Returns the property of the object with the given prop name id,
121
142
  /// or `undefined` value if the name is not a property of the object.
122
143
  public func getProperty(_ propName: JavaScriptPropNameID) -> JavaScriptValue {
123
- guard let runtime else {
144
+ guard let jsiRuntime else {
124
145
  FatalError.runtimeLost()
125
146
  }
126
- return JavaScriptValue(runtime, pointee.getProperty(runtime.pointee, propName.pointee))
147
+ return JavaScriptValue(runtimeHandle, pointee.getProperty(jsiRuntime, propName.pointee))
127
148
  }
128
149
 
129
150
  /// Accesses nested properties in a single subscript operation by traversing the object chain.
@@ -138,16 +159,15 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
138
159
  /// - Note: Each intermediate value in the chain (except the last) must be an object.
139
160
  /// If any intermediate value is not an object, the behavior is undefined and may crash.
140
161
  public subscript(_ key: String, _ nestedKeys: String...) -> JavaScriptValue {
141
- guard let runtime else {
162
+ guard let jsiRuntime else {
142
163
  FatalError.runtimeLost()
143
164
  }
144
- let jsiRuntime = runtime.pointee
145
165
  var value = pointee.getProperty(jsiRuntime, key)
146
166
 
147
167
  for key in nestedKeys {
148
168
  value = value.getObject(jsiRuntime).getProperty(jsiRuntime, key)
149
169
  }
150
- return JavaScriptValue(runtime, value)
170
+ return JavaScriptValue(runtimeHandle, value)
151
171
  }
152
172
 
153
173
  /// Returns an array of the object's own enumerable property names.
@@ -156,7 +176,7 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
156
176
  ///
157
177
  /// - Returns: An array of property names as strings
158
178
  public func getPropertyNames() -> [String] {
159
- guard let jsiRuntime = runtime?.pointee else {
179
+ guard let jsiRuntime else {
160
180
  FatalError.runtimeLost()
161
181
  }
162
182
  let propertyNames: facebook.jsi.Array = pointee.getPropertyNames(jsiRuntime)
@@ -209,32 +229,32 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
209
229
 
210
230
  /// Returns a prototype of the object. Same as `Object.getPrototypeOf(object)` in JS.
211
231
  public func getPrototype() -> JavaScriptValue {
212
- guard let runtime else {
232
+ guard let jsiRuntime else {
213
233
  FatalError.runtimeLost()
214
234
  }
215
- return JavaScriptValue(runtime, pointee.getPrototype(runtime.pointee))
235
+ return JavaScriptValue(runtimeHandle, pointee.getPrototype(jsiRuntime))
216
236
  }
217
237
 
218
238
  /// Sets a prototype of the object. Same as `Object.setPrototypeOf(object, prototype)` in JS.
219
239
  public func setPrototype(_ prototype: JavaScriptValue) {
220
- guard let runtime else {
240
+ guard let jsiRuntime else {
221
241
  FatalError.runtimeLost()
222
242
  }
223
- pointee.setPrototype(runtime.pointee, prototype.pointee)
243
+ pointee.setPrototype(jsiRuntime, prototype.pointee)
224
244
  }
225
245
 
226
246
  // MARK: - Modifying object properties
227
247
 
228
248
  public func setProperty(_ name: String, value: JavaScriptValue) {
229
- guard let runtime else {
249
+ guard let jsiRuntime else {
230
250
  FatalError.runtimeLost()
231
251
  }
232
252
  // This specialization is to avoid copying the value; `asValue()` on `JavaScriptValue` needs to do a copy.
233
253
  expo.setProperty(
234
- runtime.pointee,
254
+ jsiRuntime,
235
255
  pointee,
236
- name.toJSIPropNameID(in: runtime.pointee),
237
- value.toJSIValue(in: runtime.pointee)
256
+ name.toJSIPropNameID(in: jsiRuntime),
257
+ value.toJSIValue(in: jsiRuntime)
238
258
  )
239
259
  }
240
260
 
@@ -247,22 +267,22 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
247
267
  }
248
268
 
249
269
  internal func setProperty<T: JavaScriptRepresentable>(_ name: String, value: consuming T) where T: JSIRepresentable {
250
- guard let runtime else {
270
+ guard let jsiRuntime else {
251
271
  FatalError.runtimeLost()
252
272
  }
253
- let jsiValue = value.toJSIValue(in: runtime.pointee)
254
- expo.setProperty(runtime.pointee, pointee, name.toJSIPropNameID(in: runtime.pointee), jsiValue)
273
+ let jsiValue = value.toJSIValue(in: jsiRuntime)
274
+ expo.setProperty(jsiRuntime, pointee, name.toJSIPropNameID(in: jsiRuntime), jsiValue)
255
275
  }
256
276
 
257
277
  public func setProperty(_ name: String, _ object: consuming JavaScriptObject) {
258
- guard let runtime else {
278
+ guard let jsiRuntime else {
259
279
  FatalError.runtimeLost()
260
280
  }
261
281
  expo.setProperty(
262
- runtime.pointee,
282
+ jsiRuntime,
263
283
  pointee,
264
- name.toJSIPropNameID(in: runtime.pointee),
265
- facebook.jsi.Value(runtime.pointee, object.pointee)
284
+ name.toJSIPropNameID(in: jsiRuntime),
285
+ facebook.jsi.Value(jsiRuntime, object.pointee)
266
286
  )
267
287
  }
268
288
 
@@ -314,10 +334,10 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
314
334
  #if !os(macOS)
315
335
  // TODO: remove when bumping to react-native-macos 0.86
316
336
  public func deleteProperty(_ name: String) {
317
- guard let runtime else {
337
+ guard let jsiRuntime else {
318
338
  FatalError.runtimeLost()
319
339
  }
320
- pointee.deleteProperty(runtime.pointee, name.toJSIPropNameID(in: runtime.pointee))
340
+ pointee.deleteProperty(jsiRuntime, name.toJSIPropNameID(in: jsiRuntime))
321
341
  }
322
342
  #endif
323
343
 
@@ -381,18 +401,18 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
381
401
  // MARK: - Conversions
382
402
 
383
403
  public func asValue() -> JavaScriptValue {
384
- guard let runtime else {
404
+ guard let jsiRuntime else {
385
405
  FatalError.runtimeLost()
386
406
  }
387
- return JavaScriptValue(runtime, facebook.jsi.Value(runtime.pointee, pointee))
407
+ return JavaScriptValue(runtimeHandle, facebook.jsi.Value(jsiRuntime, pointee))
388
408
  }
389
409
 
390
410
  /// Returns the object as a `facebook.jsi.Value` instance.
391
411
  internal func asJSIValue() -> facebook.jsi.Value {
392
- guard let runtime else {
412
+ guard let jsiRuntime else {
393
413
  FatalError.runtimeLost()
394
414
  }
395
- return facebook.jsi.Value(runtime.pointee, pointee)
415
+ return facebook.jsi.Value(jsiRuntime, pointee)
396
416
  }
397
417
 
398
418
  /// Provides scoped access to a raw pointer to the underlying `facebook.jsi.Object`.
@@ -428,10 +448,10 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
428
448
  /// wrapper: C++-produced states carry a null context and are unrecoverable from
429
449
  /// the Swift side by design.
430
450
  public func hasNativeState() -> Bool {
431
- guard let runtime else {
451
+ guard let jsiRuntime else {
432
452
  FatalError.runtimeLost()
433
453
  }
434
- return expo.hasNativeState(runtime.pointee, pointee)
454
+ return expo.hasNativeState(jsiRuntime, pointee)
435
455
  }
436
456
 
437
457
  /// Returns the Swift `JavaScriptNativeState` wrapper attached to this object via
@@ -439,10 +459,10 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
439
459
  /// produced C++-side without a Swift back-pointer, or its concrete type doesn't
440
460
  /// match `T`.
441
461
  public func getNativeState<T: JavaScriptNativeState>(as: T.Type = JavaScriptNativeState.self) -> T? {
442
- guard let runtime else {
462
+ guard let jsiRuntime else {
443
463
  FatalError.runtimeLost()
444
464
  }
445
- guard let cxxNativeState = expo.getExpoNativeState(runtime.pointee, pointee) else {
465
+ guard let cxxNativeState = expo.getExpoNativeState(jsiRuntime, pointee) else {
446
466
  return nil
447
467
  }
448
468
  return T.from(cxx: cxxNativeState)
@@ -452,27 +472,27 @@ public struct JavaScriptObject: JavaScriptType, Sendable, ~Copyable {
452
472
  /// Creates a new shared_ptr to the object managed by state, which will live until the value at this property becomes unreachable.
453
473
  /// - TODO: throw a type error if this object is a proxy or host object.
454
474
  public func setNativeState<T: JavaScriptNativeState>(_ nativeState: T) {
455
- guard let runtime else {
475
+ guard let jsiRuntime else {
456
476
  FatalError.runtimeLost()
457
477
  }
458
- expo.setNativeState(runtime.pointee, self.pointee, nativeState.acquireShared())
478
+ expo.setNativeState(jsiRuntime, self.pointee, nativeState.acquireShared())
459
479
  }
460
480
 
461
481
  /// Unsets the native state of this object.
462
482
  public func unsetNativeState() {
463
- guard let runtime else {
483
+ guard let jsiRuntime else {
464
484
  FatalError.runtimeLost()
465
485
  }
466
- expo.unsetNativeState(runtime.pointee, pointee)
486
+ expo.unsetNativeState(jsiRuntime, pointee)
467
487
  }
468
488
 
469
489
  // MARK: - Memory pressure
470
490
 
471
491
  public func setExternalMemoryPressure(_ size: Int) {
472
- guard let runtime else {
492
+ guard let jsiRuntime else {
473
493
  FatalError.runtimeLost()
474
494
  }
475
- pointee.setExternalMemoryPressure(runtime.pointee, size)
495
+ pointee.setExternalMemoryPressure(jsiRuntime, size)
476
496
  }
477
497
 
478
498
  // 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
- internal weak let runtime: JavaScriptRuntime?
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
- /// Takes ownership of the given JSI value. Only runtime-free values (undefined, null, booleans and
14
- /// numbers) pass `nil` here.
15
- internal init(_ runtime: JavaScriptRuntime?, _ pointee: consuming facebook.jsi.Value) {
16
- self.runtime = runtime
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
17
30
  self.pointee = pointee
18
31
  }
19
32
 
20
- /// Takes ownership of the given JSI value. Same as the optional-runtime overload, but the exact
21
- /// parameter type keeps the caller from promoting its runtime to an optional, which costs a
22
- /// retain/release pair around every call on the hot paths (property reads, function results).
23
- internal init(_ runtime: JavaScriptRuntime, _ pointee: consuming facebook.jsi.Value) {
24
- self.runtime = runtime
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
36
+ self.pointee = pointee
37
+ }
38
+
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.runtime = runtime
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.runtime = runtime
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.runtime = runtime
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.runtime = runtime
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.runtime = runtime
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.runtime = runtime
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.runtime = runtime
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 runtime {
76
- return JavaScriptValue(runtime, facebook.jsi.Value(runtime.pointee, pointee))
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(nil, facebook.jsi.Value(getBool()))
102
+ return .init(facebook.jsi.Value(getBool()))
86
103
  case .number:
87
- return .init(nil, facebook.jsi.Value(getDouble()))
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 = runtime?.pointee else {
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 = runtime?.pointee else {
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 = runtime?.pointee else {
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 = runtime?.pointee else {
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 = runtime?.pointee else {
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 runtime else {
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(runtime, pointee.getObject(runtime.pointee))
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 runtime else {
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(runtime, pointee.getObject(runtime.pointee).getArray(runtime.pointee))
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 = runtime?.pointee else {
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 runtime {
540
- return facebook.jsi.Value(runtime.pointee, pointee)
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 = runtime?.pointee ?? another.runtime?.pointee {
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(nil, facebook.jsi.Value(true))
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(nil, facebook.jsi.Value(false))
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(nil, facebook.jsi.Value(number))
692
+ return JavaScriptValue(facebook.jsi.Value(number))
676
693
  }
677
694
 
678
695
  public static func representing(value: JavaScriptRepresentable, in runtime: JavaScriptRuntime) -> JavaScriptValue {
@@ -725,6 +742,6 @@ extension JavaScriptValue {
725
742
  // Shared instances behind `JavaScriptValue.undefined` and `JavaScriptValue.null`; see the comment on
726
743
  // those accessors for why these are globals.
727
744
  @usableFromInline
728
- internal let sharedUndefinedValue = JavaScriptValue(nil, facebook.jsi.Value.undefined())
745
+ internal let sharedUndefinedValue = JavaScriptValue(facebook.jsi.Value.undefined())
729
746
  @usableFromInline
730
- internal let sharedNullValue = JavaScriptValue(nil, facebook.jsi.Value.null())
747
+ 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.utf8(runtime).c_str(), result)) {
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.utf8(runtime).c_str(), value)) {
42
+ if (_callbacks.set(runtime, name, value)) {
43
43
  if (auto *error = CppError::getCurrent()) {
44
44
  throw error->release();
45
45
  }