expo-modules-jsi 58.0.7 → 58.0.9
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 +22 -0
- package/apple/Benchmarks/ContainerCodingBenchmarks.swift +74 -0
- package/apple/Benchmarks/HostFunctionBenchmarks.swift +41 -0
- package/apple/Benchmarks/RuntimeCacheBenchmarks.swift +71 -0
- package/apple/Benchmarks/UnownedDecodeBenchmarks.swift +121 -0
- package/apple/Benchmarks/ValueAccessBenchmarks.swift +23 -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 +210 -21
- package/apple/Sources/ExpoModulesJSI/Coding/JavaScriptCodable+Set.swift +79 -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 +43 -31
- 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 +97 -7
- 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/Errors.swift +19 -0
- package/apple/Sources/ExpoModulesJSI/Utilities/String+JSI.swift +4 -3
- package/apple/Sources/ExpoModulesJSI/Utilities/UncheckedSendable.swift +13 -0
- package/apple/Sources/ExpoModulesJSI-Cxx/include/JSIUtils.h +68 -0
- package/apple/Sources/ExpoModulesJSI-Cxx/include/RuntimeScheduler.h +18 -5
- package/apple/Tests/JavaScriptCodableSetTests.swift +115 -0
- package/apple/Tests/JavaScriptDecodableKindsTests.swift +132 -0
- package/apple/Tests/JavaScriptRuntimeCacheTests.swift +117 -0
- package/apple/Tests/JavaScriptRuntimeTests.swift +86 -3
- 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
|
@@ -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
|
|
@@ -407,13 +482,134 @@ extension UInt64: JavaScriptCodable {
|
|
|
407
482
|
}
|
|
408
483
|
}
|
|
409
484
|
|
|
485
|
+
// MARK: - Integer conversion entry points
|
|
486
|
+
|
|
487
|
+
// The inlinable integer witnesses call these non-generic overloads rather than the generic
|
|
488
|
+
// implementations below. A client can devirtualize a witness and inline it only when its body calls
|
|
489
|
+
// non-generic functions; through a generic, non-inlinable call, it falls back to dynamic dispatch, and a
|
|
490
|
+
// direct call runs the generic implementation unspecialized. Each overload runs a copy of the generic
|
|
491
|
+
// implementation specialized inside the framework.
|
|
492
|
+
|
|
493
|
+
@usableFromInline
|
|
494
|
+
@JavaScriptActor
|
|
495
|
+
func decodeInteger(_ number: Double, as type: Int8.Type) throws -> Int8 {
|
|
496
|
+
return try genericDecodeInteger(number, as: Int8.self)
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
@usableFromInline
|
|
500
|
+
@JavaScriptActor
|
|
501
|
+
func decodeInteger(_ number: Double, as type: Int16.Type) throws -> Int16 {
|
|
502
|
+
return try genericDecodeInteger(number, as: Int16.self)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
@usableFromInline
|
|
506
|
+
@JavaScriptActor
|
|
507
|
+
func decodeInteger(_ number: Double, as type: Int32.Type) throws -> Int32 {
|
|
508
|
+
return try genericDecodeInteger(number, as: Int32.self)
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
@usableFromInline
|
|
512
|
+
@JavaScriptActor
|
|
513
|
+
func decodeInteger(_ number: Double, as type: UInt8.Type) throws -> UInt8 {
|
|
514
|
+
return try genericDecodeInteger(number, as: UInt8.self)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
@usableFromInline
|
|
518
|
+
@JavaScriptActor
|
|
519
|
+
func decodeInteger(_ number: Double, as type: UInt16.Type) throws -> UInt16 {
|
|
520
|
+
return try genericDecodeInteger(number, as: UInt16.self)
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
@usableFromInline
|
|
524
|
+
@JavaScriptActor
|
|
525
|
+
func decodeInteger(_ number: Double, as type: UInt32.Type) throws -> UInt32 {
|
|
526
|
+
return try genericDecodeInteger(number, as: UInt32.self)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
@usableFromInline
|
|
530
|
+
@JavaScriptActor
|
|
531
|
+
func decodeWideInteger(_ value: borrowing JavaScriptValue, as type: Int.Type) throws -> Int {
|
|
532
|
+
return try genericDecodeWideInteger(value, as: Int.self)
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
@usableFromInline
|
|
536
|
+
@JavaScriptActor
|
|
537
|
+
func decodeWideInteger(
|
|
538
|
+
_ value: borrowing JavaScriptUnownedValue,
|
|
539
|
+
as type: Int.Type,
|
|
540
|
+
runtime: borrowing JavaScriptRuntime
|
|
541
|
+
) throws -> Int {
|
|
542
|
+
return try genericDecodeWideInteger(value, as: Int.self, runtime: runtime)
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
@usableFromInline
|
|
546
|
+
@JavaScriptActor
|
|
547
|
+
func decodeWideInteger(_ value: borrowing JavaScriptValue, as type: Int64.Type) throws -> Int64 {
|
|
548
|
+
return try genericDecodeWideInteger(value, as: Int64.self)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
@usableFromInline
|
|
552
|
+
@JavaScriptActor
|
|
553
|
+
func decodeWideInteger(
|
|
554
|
+
_ value: borrowing JavaScriptUnownedValue,
|
|
555
|
+
as type: Int64.Type,
|
|
556
|
+
runtime: borrowing JavaScriptRuntime
|
|
557
|
+
) throws -> Int64 {
|
|
558
|
+
return try genericDecodeWideInteger(value, as: Int64.self, runtime: runtime)
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
@usableFromInline
|
|
562
|
+
@JavaScriptActor
|
|
563
|
+
func decodeWideInteger(_ value: borrowing JavaScriptValue, as type: UInt.Type) throws -> UInt {
|
|
564
|
+
return try genericDecodeWideInteger(value, as: UInt.self)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
@usableFromInline
|
|
568
|
+
@JavaScriptActor
|
|
569
|
+
func decodeWideInteger(
|
|
570
|
+
_ value: borrowing JavaScriptUnownedValue,
|
|
571
|
+
as type: UInt.Type,
|
|
572
|
+
runtime: borrowing JavaScriptRuntime
|
|
573
|
+
) throws -> UInt {
|
|
574
|
+
return try genericDecodeWideInteger(value, as: UInt.self, runtime: runtime)
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
@usableFromInline
|
|
578
|
+
@JavaScriptActor
|
|
579
|
+
func decodeWideInteger(_ value: borrowing JavaScriptValue, as type: UInt64.Type) throws -> UInt64 {
|
|
580
|
+
return try genericDecodeWideInteger(value, as: UInt64.self)
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
@usableFromInline
|
|
584
|
+
@JavaScriptActor
|
|
585
|
+
func decodeWideInteger(
|
|
586
|
+
_ value: borrowing JavaScriptUnownedValue,
|
|
587
|
+
as type: UInt64.Type,
|
|
588
|
+
runtime: borrowing JavaScriptRuntime
|
|
589
|
+
) throws -> UInt64 {
|
|
590
|
+
return try genericDecodeWideInteger(value, as: UInt64.self, runtime: runtime)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
@usableFromInline
|
|
594
|
+
@JavaScriptActor
|
|
595
|
+
func encodeSafeInteger(_ value: Int) throws -> JavaScriptValue {
|
|
596
|
+
return try genericEncodeSafeInteger(value)
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
@usableFromInline
|
|
600
|
+
@JavaScriptActor
|
|
601
|
+
func encodeSafeInteger(_ value: UInt) throws -> JavaScriptValue {
|
|
602
|
+
return try genericEncodeSafeInteger(value)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// MARK: - Generic integer conversions
|
|
606
|
+
|
|
410
607
|
/// Rounds a JavaScript number to the nearest integer and narrows it to `T`, throwing instead of
|
|
411
608
|
/// trapping when the value is non-finite or outside `T`'s representable range. `T(exactly:)` on the
|
|
412
609
|
/// already-rounded value is nil only when out of range, sidestepping the lossy `Double(T.max)`
|
|
413
610
|
/// boundary comparison for 64-bit widths.
|
|
414
|
-
@usableFromInline
|
|
415
611
|
@JavaScriptActor
|
|
416
|
-
func
|
|
612
|
+
func genericDecodeInteger<T: FixedWidthInteger>(_ number: Double, as type: T.Type) throws -> T {
|
|
417
613
|
guard number.isFinite, let result = T(exactly: number.rounded()) else {
|
|
418
614
|
throw IntegerOutOfRangeException(value: number, type: "\(T.self)")
|
|
419
615
|
}
|
|
@@ -424,47 +620,41 @@ func decodeInteger<T: FixedWidthInteger>(_ number: Double, as type: T.Type) thro
|
|
|
424
620
|
/// case is checked first since it's the common one: a single `isNumber()` tag check, then the
|
|
425
621
|
/// assert-only `getDouble()` reads the value without re-checking the tag. A `bigint` is read losslessly
|
|
426
622
|
/// through its 64-bit accessor; anything else throws the same `TypeError` the `number` read would.
|
|
427
|
-
@usableFromInline
|
|
428
623
|
@JavaScriptActor
|
|
429
|
-
func
|
|
624
|
+
func genericDecodeWideInteger<T: FixedWidthInteger>(_ value: borrowing JavaScriptValue, as type: T.Type) throws -> T {
|
|
430
625
|
guard value.isNumber() else {
|
|
431
626
|
guard value.isBigInt() else {
|
|
432
|
-
// Neither a number nor a bigint: `asDouble()` throws the canonical `TypeError`.
|
|
433
|
-
|
|
434
|
-
// referencing `TypeError`'s internal memberwise initializer.
|
|
435
|
-
return try decodeInteger(value.asDouble(), as: T.self)
|
|
627
|
+
// Neither a number nor a bigint: `asDouble()` throws the canonical `TypeError`.
|
|
628
|
+
return try genericDecodeInteger(value.asDouble(), as: T.self)
|
|
436
629
|
}
|
|
437
|
-
return try
|
|
630
|
+
return try genericDecodeBigInt(value.getBigInt(), as: T.self)
|
|
438
631
|
}
|
|
439
|
-
return try
|
|
632
|
+
return try genericDecodeInteger(value.getDouble(), as: T.self)
|
|
440
633
|
}
|
|
441
634
|
|
|
442
635
|
/// `JavaScriptUnownedValue` overload of `decodeWideInteger`. The common `number` path stays zero-copy
|
|
443
636
|
/// and pays a single tag check (see the owning overload); only a `bigint` materializes an owning value
|
|
444
637
|
/// (the borrowed value exposes no BigInt accessor), which is acceptable on this rare branch.
|
|
445
|
-
@usableFromInline
|
|
446
638
|
@JavaScriptActor
|
|
447
|
-
func
|
|
639
|
+
func genericDecodeWideInteger<T: FixedWidthInteger>(
|
|
448
640
|
_ value: borrowing JavaScriptUnownedValue,
|
|
449
641
|
as type: T.Type,
|
|
450
642
|
runtime: borrowing JavaScriptRuntime
|
|
451
643
|
) throws -> T {
|
|
452
644
|
guard value.isNumber() else {
|
|
453
645
|
guard value.isBigInt() else {
|
|
454
|
-
//
|
|
455
|
-
|
|
456
|
-
return try decodeInteger(value.asDouble(), as: T.self)
|
|
646
|
+
// Neither a number nor a bigint: `asDouble()` throws the canonical `TypeError`.
|
|
647
|
+
return try genericDecodeInteger(value.asDouble(), as: T.self)
|
|
457
648
|
}
|
|
458
|
-
return try
|
|
649
|
+
return try genericDecodeBigInt(value.copied(in: runtime).getBigInt(), as: T.self)
|
|
459
650
|
}
|
|
460
|
-
return try
|
|
651
|
+
return try genericDecodeInteger(value.getDouble(), as: T.self)
|
|
461
652
|
}
|
|
462
653
|
|
|
463
654
|
/// Narrows a `JavaScriptBigInt` to `T`, reading it through the signed or unsigned 64-bit accessor to
|
|
464
655
|
/// match `T`'s signedness and throwing rather than truncating when it falls outside `T`'s range.
|
|
465
|
-
@usableFromInline
|
|
466
656
|
@JavaScriptActor
|
|
467
|
-
func
|
|
657
|
+
func genericDecodeBigInt<T: FixedWidthInteger>(_ bigInt: borrowing JavaScriptBigInt, as type: T.Type) throws -> T {
|
|
468
658
|
let wide: T? =
|
|
469
659
|
if T.isSigned {
|
|
470
660
|
bigInt.isInt64() ? T(exactly: bigInt.getInt64()) : nil
|
|
@@ -480,9 +670,8 @@ func decodeBigInt<T: FixedWidthInteger>(_ bigInt: borrowing JavaScriptBigInt, as
|
|
|
480
670
|
/// Encodes a 64-bit-wide integer to a JS `number`, throwing when it falls outside JavaScript's
|
|
481
671
|
/// safe-integer range where a `Double` could no longer represent it exactly. Used by `Int`/`UInt`,
|
|
482
672
|
/// whose JS mapping stays a `number`; the explicitly-sized 64-bit types encode as a `bigint` instead.
|
|
483
|
-
@usableFromInline
|
|
484
673
|
@JavaScriptActor
|
|
485
|
-
func
|
|
674
|
+
func genericEncodeSafeInteger<T: FixedWidthInteger>(_ value: T) throws -> JavaScriptValue {
|
|
486
675
|
// `Number.MAX_SAFE_INTEGER` (2^53 - 1): the largest magnitude where every integer up to it, and the
|
|
487
676
|
// next one, is representable as a JS number. 2^53 itself is representable but unsafe (it collides with
|
|
488
677
|
// 2^53 + 1), so the bound is exclusive above this. Used by `Int`/`UInt`, both 64-bit, so 2^53 fits.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Copyright 2026-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
internal import ExpoModulesJSI_Cxx
|
|
4
|
+
internal import jsi
|
|
5
|
+
|
|
6
|
+
// `Set` encodes to a JS `Set` and decodes from a JS `Set` or, like `Array`, from an array or an arrayized
|
|
7
|
+
// scalar. JSI has no `Set` API, so the conversions go through the runtime's global `Set` and `Array`
|
|
8
|
+
// constructors. Equality differs between the two sides: a JS `Set` compares objects by reference while
|
|
9
|
+
// a Swift `Set` compares by `Hashable`, so two distinct JS objects that decode to equal values collapse
|
|
10
|
+
// into a single element.
|
|
11
|
+
|
|
12
|
+
extension Set: JavaScriptDecodable where Element: JavaScriptDecodable {
|
|
13
|
+
@JavaScriptActor
|
|
14
|
+
@inlinable
|
|
15
|
+
public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws
|
|
16
|
+
-> Set<Element>
|
|
17
|
+
{
|
|
18
|
+
// The cheap tag checks come first: an array takes the `Array` path directly, and a primitive
|
|
19
|
+
// can't be a JS `Set`, so both skip the JS call below. Any other value that is not a JS `Set` is
|
|
20
|
+
// arrayized, as in `Array`. Duplicates in an array collapse without an error.
|
|
21
|
+
guard !value.isArray(), value.isObject(), let entries = try runtime.setEntries(of: value) else {
|
|
22
|
+
return Set(try [Element].decode(value, in: runtime))
|
|
23
|
+
}
|
|
24
|
+
return Set(try [Element].decode(entries, in: runtime))
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
extension Set: JavaScriptEncodable where Element: JavaScriptEncodable {
|
|
29
|
+
@JavaScriptActor
|
|
30
|
+
@inlinable
|
|
31
|
+
public static func encode(_ value: Set<Element>, in runtime: borrowing JavaScriptRuntime) throws
|
|
32
|
+
-> JavaScriptValue
|
|
33
|
+
{
|
|
34
|
+
// Constructing from an array fills the set in a single call, rather than one `add` call per element.
|
|
35
|
+
let entries = try [Element].encode(Array(value), in: runtime)
|
|
36
|
+
return try runtime.setConstructor().callAsConstructor(entries)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
extension JavaScriptRuntime {
|
|
41
|
+
/// Copies the entries of a JS `Set` out into a JS array, or returns `nil` when the value is not a
|
|
42
|
+
/// JS `Set`. The instance check and the copy run in a single call to a cached JS function, which
|
|
43
|
+
/// saves the global lookups and the separate `instanceof` call of `value.is("Set")` followed by
|
|
44
|
+
/// `Array.from`. For a small `Set` those fixed costs are most of the decode time.
|
|
45
|
+
@usableFromInline
|
|
46
|
+
@JavaScriptActor
|
|
47
|
+
func setEntries(of value: borrowing JavaScriptValue) throws -> JavaScriptValue? {
|
|
48
|
+
let function = try cached(setEntriesFunctionKey) {
|
|
49
|
+
return try eval(
|
|
50
|
+
label: "expo-modules-jsi/set-entries.js",
|
|
51
|
+
"(function (value) { return value instanceof Set ? Array.from(value) : undefined; })"
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
let setEntries = function.getFunction()
|
|
55
|
+
// Pass the borrowed `jsi::Value` straight to the call, rather than copying it into an owned value
|
|
56
|
+
// and again into an arguments buffer.
|
|
57
|
+
let entries = try withUnsafePointer(to: value.pointee) { argument in
|
|
58
|
+
return try capturingCppErrors {
|
|
59
|
+
return JavaScriptValue(self, expo.callFunction(pointee, setEntries.pointee, argument, 1))
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return entries.isUndefined() ? nil : entries
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Returns the global `Set` constructor, looked up once and cached, so an encode doesn't pay for
|
|
66
|
+
/// the global property lookup.
|
|
67
|
+
@usableFromInline
|
|
68
|
+
@JavaScriptActor
|
|
69
|
+
func setConstructor() throws -> JavaScriptFunction {
|
|
70
|
+
let constructor = try cached(setConstructorKey) {
|
|
71
|
+
return try global().getPropertyAsFunction("Set").asValue()
|
|
72
|
+
}
|
|
73
|
+
return constructor.getFunction()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// Keys of the `Set` entries function and the `Set` constructor in each runtime's cache.
|
|
78
|
+
private let setEntriesFunctionKey = JavaScriptRuntime.Cache.Key<JavaScriptValue>()
|
|
79
|
+
private let setConstructorKey = JavaScriptRuntime.Cache.Key<JavaScriptValue>()
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Copyright 2026-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
internal import jsi
|
|
4
|
+
|
|
5
|
+
/// A set of JavaScript value kinds, as told apart by the value's type tag alone.
|
|
6
|
+
///
|
|
7
|
+
/// Unlike `JavaScriptValue.Kind`, a function is an `object` here: telling it apart needs a call into
|
|
8
|
+
/// the runtime, while every kind in this set is read from the tag without one. Used by
|
|
9
|
+
/// `JavaScriptDecodable.decodableKinds` to describe the kinds a type can decode from. Frozen, so a check
|
|
10
|
+
/// against it compiles to a mask test in the client.
|
|
11
|
+
@frozen
|
|
12
|
+
public struct JavaScriptValueKinds: OptionSet, Sendable {
|
|
13
|
+
public let rawValue: UInt16
|
|
14
|
+
|
|
15
|
+
// `UInt16` leaves room for more kinds without changing the frozen layout.
|
|
16
|
+
//
|
|
17
|
+
// The kinds are computed and inlinable rather than stored `static let`s, which a client would reach
|
|
18
|
+
// through a lazily initialized global, so a mask built from them folds to a constant.
|
|
19
|
+
|
|
20
|
+
@inlinable
|
|
21
|
+
public init(rawValue: UInt16) {
|
|
22
|
+
self.rawValue = rawValue
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@inlinable public static var undefined: JavaScriptValueKinds { JavaScriptValueKinds(rawValue: 1 << 0) }
|
|
26
|
+
@inlinable public static var null: JavaScriptValueKinds { JavaScriptValueKinds(rawValue: 1 << 1) }
|
|
27
|
+
@inlinable public static var bool: JavaScriptValueKinds { JavaScriptValueKinds(rawValue: 1 << 2) }
|
|
28
|
+
@inlinable public static var number: JavaScriptValueKinds { JavaScriptValueKinds(rawValue: 1 << 3) }
|
|
29
|
+
@inlinable public static var bigint: JavaScriptValueKinds { JavaScriptValueKinds(rawValue: 1 << 4) }
|
|
30
|
+
@inlinable public static var string: JavaScriptValueKinds { JavaScriptValueKinds(rawValue: 1 << 5) }
|
|
31
|
+
@inlinable public static var symbol: JavaScriptValueKinds { JavaScriptValueKinds(rawValue: 1 << 6) }
|
|
32
|
+
/// Any object, including arrays, typed arrays and functions.
|
|
33
|
+
@inlinable public static var object: JavaScriptValueKinds { JavaScriptValueKinds(rawValue: 1 << 7) }
|
|
34
|
+
|
|
35
|
+
/// Every kind, including any added later: the bits above the current kinds are set too, so a type that
|
|
36
|
+
/// accepts every kind keeps doing so.
|
|
37
|
+
@inlinable public static var all: JavaScriptValueKinds { JavaScriptValueKinds(rawValue: .max) }
|
|
38
|
+
|
|
39
|
+
/// The kind of `value`.
|
|
40
|
+
///
|
|
41
|
+
/// Not inlinable on purpose: the type checks read the `jsi::Value`, which clients can't see, so an
|
|
42
|
+
/// inlined initializer would make one call into this module per check. Here it is a single call.
|
|
43
|
+
public init(of value: borrowing JavaScriptValue) {
|
|
44
|
+
self.init(of: value.pointee)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// The kind of the borrowed `value`, read the same way as from an owning value.
|
|
48
|
+
public init(of value: borrowing JavaScriptUnownedValue) {
|
|
49
|
+
self.init(of: value.pointer.pointee)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Reads the kind from the `jsi::Value` both public initializers wrap. The checks go from the most to
|
|
53
|
+
/// the least common kind of an argument.
|
|
54
|
+
private init(of value: borrowing facebook.jsi.Value) {
|
|
55
|
+
if value.isNumber() {
|
|
56
|
+
self = .number
|
|
57
|
+
} else if value.isString() {
|
|
58
|
+
self = .string
|
|
59
|
+
} else if value.isObject() {
|
|
60
|
+
self = .object
|
|
61
|
+
} else if value.isBool() {
|
|
62
|
+
self = .bool
|
|
63
|
+
} else if value.isNull() {
|
|
64
|
+
self = .null
|
|
65
|
+
} else if value.isUndefined() {
|
|
66
|
+
self = .undefined
|
|
67
|
+
} else if value.isBigInt() {
|
|
68
|
+
self = .bigint
|
|
69
|
+
} else {
|
|
70
|
+
self = .symbol
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -93,14 +93,16 @@ extension String: JSIRepresentable {
|
|
|
93
93
|
// allocation, copy and free per string. `withUTF8` is mutating (it makes a bridged string
|
|
94
94
|
// contiguous first), hence the local copy; native strings are already contiguous and pay nothing.
|
|
95
95
|
// The value is moved out through a local because `withUTF8` needs a `Copyable` closure result.
|
|
96
|
+
// The C++ helper moves the engine's `jsi::String` into the value, so this costs one engine handle;
|
|
97
|
+
// `jsi::Value(runtime, string)` would clone it and then release the original.
|
|
96
98
|
var string = self
|
|
97
99
|
var value = facebook.jsi.Value.undefined()
|
|
98
100
|
string.withUTF8 { utf8 in
|
|
99
101
|
guard let base = utf8.baseAddress else {
|
|
100
|
-
value =
|
|
102
|
+
value = expo.createStringValueFromAscii(runtime, "", 0)
|
|
101
103
|
return
|
|
102
104
|
}
|
|
103
|
-
value =
|
|
105
|
+
value = expo.createStringValueFromUtf8(runtime, base, utf8.count)
|
|
104
106
|
}
|
|
105
107
|
return value
|
|
106
108
|
}
|