Skip to content

Commit bf69cdb

Browse files
authored
Merge pull request #665 from swiftwasm/fix-swift-class-array
BridgeJS: Fix `Array<@jsclass struct>` support on imported interfaces
2 parents 866ba61 + f75e365 commit bf69cdb

File tree

13 files changed

+1000
-719
lines changed

13 files changed

+1000
-719
lines changed

Sources/JavaScriptKit/BridgeJSIntrinsics.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -510,12 +510,13 @@ extension _JSBridgedClass {
510510
@_spi(BridgeJS) public static func bridgeJSLiftParameter(_ id: Int32) -> Self {
511511
Self(unsafelyWrapping: JSObject.bridgeJSLiftParameter(id))
512512
}
513-
@_spi(BridgeJS) public static func bridgeJSStackPop() -> Self {
514-
bridgeJSLiftParameter(_swift_js_pop_i32())
515-
}
516513
@_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Int32 { jsObject.bridgeJSLowerReturn() }
517-
@_spi(BridgeJS) public consuming func bridgeJSStackPush() {
518-
_swift_js_push_i32(bridgeJSLowerReturn())
514+
515+
public static func bridgeJSStackPop() -> Self {
516+
Self(unsafelyWrapping: JSObject.bridgeJSStackPop())
517+
}
518+
public consuming func bridgeJSStackPush() {
519+
jsObject.bridgeJSStackPush()
519520
}
520521
}
521522

Sources/JavaScriptKit/JSBridgedType.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ extension JSBridgedType {
1616
/// A protocol that Swift classes that are exposed to JavaScript via `@JS class` conform to.
1717
///
1818
/// The conformance is automatically synthesized by `@JSClass` for BridgeJS-generated declarations.
19-
public protocol _JSBridgedClass {
19+
public protocol _JSBridgedClass: Equatable, _BridgedSwiftStackType {
2020
/// The JavaScript object wrapped by this instance.
2121
/// You may assume that `jsObject instanceof Self.constructor == true`
2222
var jsObject: JSObject { get }
@@ -26,6 +26,12 @@ public protocol _JSBridgedClass {
2626
init(unsafelyWrapping jsObject: JSObject)
2727
}
2828

29+
extension _JSBridgedClass {
30+
public static func == (lhs: Self, rhs: Self) -> Bool {
31+
lhs.jsObject == rhs.jsObject
32+
}
33+
}
34+
2935
/// Conform to this protocol when your Swift class wraps a JavaScript class.
3036
public protocol JSBridgedClass: JSBridgedType, _JSBridgedClass {
3137
/// The constructor function for the JavaScript class
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import XCTest
2+
import JavaScriptKit
3+
4+
@JSClass struct ArrayElementObject {
5+
@JSGetter var id: String
6+
7+
@JSFunction init(id: String) throws(JSException)
8+
}
9+
10+
@JSClass struct ArraySupportImports {
11+
@JSFunction static func jsIntArrayLength(_ items: [Int]) throws(JSException) -> Int
12+
13+
@JSFunction static func jsRoundTripIntArray(_ items: [Int]) throws(JSException) -> [Int]
14+
@JSFunction static func jsRoundTripNumberArray(_ values: [Double]) throws(JSException) -> [Double]
15+
@JSFunction static func jsRoundTripStringArray(_ values: [String]) throws(JSException) -> [String]
16+
@JSFunction static func jsRoundTripBoolArray(_ values: [Bool]) throws(JSException) -> [Bool]
17+
18+
@JSFunction static func jsRoundTripJSValueArray(_ v: [JSValue]) throws(JSException) -> [JSValue]
19+
@JSFunction static func jsRoundTripOptionalJSValueArray(
20+
_ v: Optional<[JSValue]>
21+
) throws(JSException) -> Optional<[JSValue]>
22+
23+
@JSFunction static func jsRoundTripJSObjectArray(_ values: [JSObject]) throws(JSException) -> [JSObject]
24+
25+
@JSFunction static func jsRoundTripJSClassArray(
26+
_ values: [ArrayElementObject]
27+
) throws(JSException) -> [ArrayElementObject]
28+
29+
@JSFunction static func jsSumNumberArray(_ values: [Double]) throws(JSException) -> Double
30+
@JSFunction static func jsCreateNumberArray() throws(JSException) -> [Double]
31+
}
32+
33+
final class ArraySupportTests: XCTestCase {
34+
35+
func testRoundTripIntArray() throws {
36+
let values = [1, 2, 3, 4, 5]
37+
let result = try ArraySupportImports.jsRoundTripIntArray(values)
38+
XCTAssertEqual(result, values)
39+
XCTAssertEqual(try ArraySupportImports.jsIntArrayLength(values), values.count)
40+
XCTAssertEqual(try ArraySupportImports.jsRoundTripIntArray([]), [])
41+
}
42+
43+
func testRoundTripNumberArray() throws {
44+
let input: [Double] = [1.0, 2.5, 3.0, -4.5]
45+
let result = try ArraySupportImports.jsRoundTripNumberArray(input)
46+
XCTAssertEqual(result, input)
47+
XCTAssertEqual(try ArraySupportImports.jsRoundTripNumberArray([]), [])
48+
XCTAssertEqual(try ArraySupportImports.jsRoundTripNumberArray([42.0]), [42.0])
49+
}
50+
51+
func testRoundTripStringArray() throws {
52+
let input = ["Hello", "World", "🎉"]
53+
let result = try ArraySupportImports.jsRoundTripStringArray(input)
54+
XCTAssertEqual(result, input)
55+
XCTAssertEqual(try ArraySupportImports.jsRoundTripStringArray([]), [])
56+
XCTAssertEqual(try ArraySupportImports.jsRoundTripStringArray(["", "a", ""]), ["", "a", ""])
57+
}
58+
59+
func testRoundTripBoolArray() throws {
60+
let input = [true, false, true, false]
61+
let result = try ArraySupportImports.jsRoundTripBoolArray(input)
62+
XCTAssertEqual(result, input)
63+
XCTAssertEqual(try ArraySupportImports.jsRoundTripBoolArray([]), [])
64+
}
65+
66+
func testSumNumberArray() throws {
67+
XCTAssertEqual(try ArraySupportImports.jsSumNumberArray([1.0, 2.0, 3.0, 4.0]), 10.0)
68+
XCTAssertEqual(try ArraySupportImports.jsSumNumberArray([]), 0.0)
69+
XCTAssertEqual(try ArraySupportImports.jsSumNumberArray([42.0]), 42.0)
70+
}
71+
72+
func testCreateNumberArray() throws {
73+
let result = try ArraySupportImports.jsCreateNumberArray()
74+
XCTAssertEqual(result, [1.0, 2.0, 3.0, 4.0, 5.0])
75+
}
76+
77+
func testRoundTripJSValueArray() throws {
78+
let object = JSObject.global
79+
let symbol = JSSymbol("array")
80+
let bigInt = JSBigInt(_slowBridge: Int64(42))
81+
let values: [JSValue] = [
82+
.boolean(false),
83+
.number(123.5),
84+
.string(JSString("hello")),
85+
.object(object),
86+
.null,
87+
.undefined,
88+
.symbol(symbol),
89+
.bigInt(bigInt),
90+
]
91+
let roundTripped = try ArraySupportImports.jsRoundTripJSValueArray(values)
92+
XCTAssertEqual(roundTripped, values)
93+
XCTAssertEqual(try ArraySupportImports.jsRoundTripJSValueArray([]), [])
94+
}
95+
96+
func testRoundTripOptionalJSValueArray() throws {
97+
XCTAssertNil(try ArraySupportImports.jsRoundTripOptionalJSValueArray(nil))
98+
let values: [JSValue] = [.number(1), .undefined, .null]
99+
let result = try ArraySupportImports.jsRoundTripOptionalJSValueArray(values)
100+
XCTAssertEqual(result, values)
101+
}
102+
103+
func testRoundTripJSObjectArray() throws {
104+
let values: [JSObject] = [.global, JSObject(), ["a": 1, "b": 2]]
105+
let result = try ArraySupportImports.jsRoundTripJSObjectArray(values)
106+
XCTAssertEqual(result, values)
107+
}
108+
109+
func testRoundTripJSClassArray() throws {
110+
let values = try [ArrayElementObject(id: "1"), ArrayElementObject(id: "2"), ArrayElementObject(id: "3")]
111+
let result = try ArraySupportImports.jsRoundTripJSClassArray(values)
112+
XCTAssertEqual(result, values)
113+
XCTAssertEqual(try result[0].id, "1")
114+
XCTAssertEqual(try result[1].id, "2")
115+
XCTAssertEqual(try result[2].id, "3")
116+
117+
XCTAssertEqual(try ArraySupportImports.jsRoundTripJSClassArray([]), [])
118+
}
119+
}

Tests/BridgeJSRuntimeTests/Generated/BridgeJS.Macros.swift

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616

1717
@JSFunction func jsRoundTripJSValue(_ v: JSValue) throws(JSException) -> JSValue
1818

19-
@JSFunction func jsRoundTripJSValueArray(_ v: [JSValue]) throws(JSException) -> [JSValue]
20-
21-
@JSFunction func jsRoundTripOptionalJSValueArray(_ v: Optional<[JSValue]>) throws(JSException) -> Optional<[JSValue]>
22-
2319
@JSFunction func jsThrowOrVoid(_ shouldThrow: Bool) throws(JSException) -> Void
2420

2521
@JSFunction func jsThrowOrNumber(_ shouldThrow: Bool) throws(JSException) -> Double
@@ -63,16 +59,6 @@ extension FeatureFlag: _BridgedSwiftEnumNoPayload, _BridgedSwiftRawValueEnum {}
6359
@JSFunction(jsName: "with-dashes") static func with_dashes() throws(JSException) -> StaticBox
6460
}
6561

66-
@JSFunction func jsRoundTripNumberArray(_ values: [Double]) throws(JSException) -> [Double]
67-
68-
@JSFunction func jsRoundTripStringArray(_ values: [String]) throws(JSException) -> [String]
69-
70-
@JSFunction func jsRoundTripBoolArray(_ values: [Bool]) throws(JSException) -> [Bool]
71-
72-
@JSFunction func jsSumNumberArray(_ values: [Double]) throws(JSException) -> Double
73-
74-
@JSFunction func jsCreateNumberArray() throws(JSException) -> [Double]
75-
7662
@JSFunction(from: .global) func parseInt(_ string: String) throws(JSException) -> Double
7763

7864
@JSClass(from: .global) struct Animal {

0 commit comments

Comments
 (0)