Skip to content

Commit cb9c071

Browse files
committed
BridgeJS: Test generic exports and callback lifetimes
1 parent 56cbb3b commit cb9c071

40 files changed

Lines changed: 20741 additions & 7572 deletions

‎Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ import Testing
7979
}
8080

8181
@Test(arguments: [
82+
"GenericInitializers.swift",
8283
"Namespaces.swift",
8384
"StaticFunctions.swift",
8485
"StaticProperties.swift",
@@ -157,6 +158,105 @@ import Testing
157158
return try bridgeJSLink.link().0
158159
}
159160

161+
@Test
162+
func genericRuntimeIsGatedToGenericBuilds() throws {
163+
let genericJS = try linkedJS(forFixture: "GenericImports.swift")
164+
#expect(genericJS.contains("__bjs_codecByTypeId"))
165+
#expect(genericJS.contains("function __bjs_codecForTypeId(typeId) {"))
166+
#expect(genericJS.contains("bjs[\"bjs_TestModule_register_type_handles\"] = function(base, count) {"))
167+
#expect(genericJS.contains("instance.exports[\"bjs_TestModule_register_type_handles\"]();"))
168+
169+
let nonGenericJS = try linkedJS(forFixture: "SwiftStructImports.swift")
170+
#expect(!nonGenericJS.contains("__bjs_codecByTypeId"))
171+
#expect(!nonGenericJS.contains("__bjs_codecForTypeId"))
172+
#expect(nonGenericJS.contains("bjs[\"bjs_core_register_type_handles\"] = function() {};"))
173+
#expect(nonGenericJS.contains("bjs[\"bjs_TestModule_register_type_handles\"] = function() {};"))
174+
#expect(!nonGenericJS.contains("instance.exports[\"bjs_TestModule_register_type_handles\"]();"))
175+
}
176+
177+
@Test(arguments: [
178+
"@JS func identity<T: BridgedSwiftGenericBridgeable>(_ value: T) -> T { value }",
179+
"@JS class Box { @JS func identity<T: BridgedSwiftGenericBridgeable>(_ value: T) -> T { value } }",
180+
"@JS class Box { @JS init<T: BridgedSwiftGenericBridgeable>(_ value: T) {} }",
181+
])
182+
func genericExportsEnableCoreTypeRegistration(source: String) throws {
183+
let js = try linkSource(source).js
184+
#expect(js.contains("__bjs_typeIdByToken.set(tokens[i], typeIds[i]);"))
185+
#expect(js.contains("instance.exports[\"bjs_core_register_type_handles\"]();"))
186+
}
187+
188+
@Test
189+
func sameTypeNameAcrossModulesLinksWithHandleIdentity() throws {
190+
func makeSkeleton(moduleName: String, source: String) throws -> BridgeJSSkeleton {
191+
let swiftAPI = SwiftToSkeleton(
192+
progress: .silent,
193+
moduleName: moduleName,
194+
exposeToGlobal: false,
195+
externalModuleIndex: .empty
196+
)
197+
swiftAPI.addSourceFile(Parser.parse(source: source), inputFilePath: "\(moduleName).swift")
198+
return try swiftAPI.finalize()
199+
}
200+
let structSource = """
201+
@JS public struct Point {
202+
public var x: Int
203+
@JS public init(x: Int) { self.x = x }
204+
}
205+
"""
206+
let first = try makeSkeleton(
207+
moduleName: "FirstModule",
208+
source: structSource + """
209+
210+
@JSFunction func identity<T: BridgedSwiftGenericBridgeable>(_ value: T) throws(JSException) -> T
211+
"""
212+
)
213+
let second = try makeSkeleton(moduleName: "SecondModule", source: structSource)
214+
let bridgeJSLink = BridgeJSLink(skeletons: [first, second])
215+
let js = try bridgeJSLink.link().outputJs
216+
#expect(js.contains("bjs[\"bjs_FirstModule_register_type_handles\"] = function(base, count) {"))
217+
#expect(js.contains("bjs[\"bjs_SecondModule_register_type_handles\"] = function(base, count) {"))
218+
}
219+
220+
@Test(arguments: [false, true])
221+
func sameTypeNameAcrossModulesFailsWithGenericExports(distinctNamespaces: Bool) throws {
222+
func makeSkeleton(moduleName: String, source: String) throws -> BridgeJSSkeleton {
223+
let swiftAPI = SwiftToSkeleton(
224+
progress: .silent,
225+
moduleName: moduleName,
226+
exposeToGlobal: false,
227+
externalModuleIndex: .empty
228+
)
229+
swiftAPI.addSourceFile(Parser.parse(source: source), inputFilePath: "\(moduleName).swift")
230+
return try swiftAPI.finalize()
231+
}
232+
let structSource = """
233+
@JS public struct Point {
234+
public var x: Int
235+
@JS public init(x: Int) { self.x = x }
236+
}
237+
"""
238+
let first = try makeSkeleton(
239+
moduleName: "FirstModule",
240+
source: structSource + """
241+
242+
@JS public func identity<T: BridgedSwiftGenericBridgeable>(_ value: T) -> T { value }
243+
"""
244+
)
245+
let second = try makeSkeleton(
246+
moduleName: "SecondModule",
247+
source: distinctNamespaces
248+
? structSource.replacingOccurrences(
249+
of: "@JS public struct",
250+
with: "@JS(namespace: \"Other\") public struct"
251+
)
252+
: structSource
253+
)
254+
let bridgeJSLink = BridgeJSLink(skeletons: [first, second])
255+
#expect(throws: BridgeJSLinkError.self) {
256+
_ = try bridgeJSLink.link()
257+
}
258+
}
259+
160260
@Test
161261
func perClassIdentityModeFromAnnotation() throws {
162262
let url = Self.inputsDirectory.appendingPathComponent("IdentityModeClass.swift")

‎Plugins/BridgeJS/Tests/BridgeJSToolTests/CodegenTestSupport.swift‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,31 @@ func makeSkeleton(
2222
return try swiftAPI.finalize()
2323
}
2424

25+
func renderExportGlue(
26+
_ source: String,
27+
moduleName: String = "TestModule"
28+
) throws -> String {
29+
let skeleton = try makeSkeleton(source, moduleName: moduleName)
30+
let exported = try #require(skeleton.exported)
31+
let exportSwift = ExportSwift(
32+
progress: .silent,
33+
moduleName: skeleton.moduleName,
34+
skeleton: exported
35+
)
36+
return try #require(try exportSwift.finalize())
37+
}
38+
39+
func linkSource(_ source: String, moduleName: String = "TestModule") throws -> (js: String, dts: String) {
40+
let skeleton = try makeSkeleton(source, moduleName: moduleName)
41+
var bridgeJSLink = BridgeJSLink()
42+
let encoder = JSONEncoder()
43+
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
44+
let unifiedData = try encoder.encode(skeleton)
45+
try bridgeJSLink.addSkeletonFile(data: unifiedData)
46+
let result = try bridgeJSLink.link()
47+
return (result.outputJs, result.outputDts)
48+
}
49+
2550
func expectDiagnostic(
2651
source: String,
2752
moduleName: String = "App",

‎Plugins/BridgeJS/Tests/BridgeJSToolTests/DiagnosticsTests.swift‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,16 @@ import Testing
833833
)
834834
}
835835

836+
@Test func constraintOnlyJSProtocolConformer() throws {
837+
_ = try makeSkeleton(
838+
"""
839+
@JS protocol GraphNode { var id: String { get } }
840+
@JS struct Building: GraphNode { var id: String }
841+
@JS func store<T: BridgedSwiftGenericBridgeable & GraphNode>(_ node: T) -> T { node }
842+
"""
843+
)
844+
}
845+
836846
@Test func protocolRefinementIsNotAConcreteConformance() throws {
837847
_ = try makeSkeleton(
838848
"""

0 commit comments

Comments
 (0)