Skip to content

Commit 6d6007d

Browse files
Added runtime coverage for arrays inside @JSClass-style structs and fixed the cleanup scoping bug:
- New struct `ArrayMembers` with array properties and methods, plus helper exports `arrayMembersSum`/`arrayMembersFirst` and `roundTripArrayMembers` (`Tests/BridgeJSRuntimeTests/StructAPIs.swift`). - JS prelude exercises array fields/methods via those exports (`Tests/prelude.mjs`). - Fixed optional-field lowering to guard cleanup code and hoist cleanup arrays so optional arrays don’t throw ReferenceError (`Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift`). Tests: `SWIFT_SDK_ID=DEVELOPMENT-SNAPSHOT-2025-11-03-a-wasm32-unknown-wasip1 make unittest` now passes (20 suites / 96 tests).
1 parent 8a51909 commit 6d6007d

File tree

5 files changed

+387
-4
lines changed

5 files changed

+387
-4
lines changed

Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3060,7 +3060,7 @@ struct IntrinsicJSFragment: Sendable {
30603060
printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);")
30613061
cleanup.write("if (\(enumCleanupVar)) { \(enumCleanupVar)(); }")
30623062
default:
3063-
// For other types (nested structs, etc.), original logic applies
3063+
// For other types (including arrays), generate lowering under the isSome guard
30643064
let wrappedFragment = structFieldLowerFragment(
30653065
field: ExportedProperty(
30663066
name: field.name,
@@ -3070,9 +3070,36 @@ struct IntrinsicJSFragment: Sendable {
30703070
),
30713071
allStructs: allStructs
30723072
)
3073+
let guardedPrinter = CodeFragmentPrinter()
3074+
let guardedCleanup = CodeFragmentPrinter()
3075+
_ = wrappedFragment.printCode([value], scope, guardedPrinter, guardedCleanup)
3076+
var loweredLines = guardedPrinter.lines
3077+
var hoistedCleanupVar: String?
3078+
if let first = loweredLines.first {
3079+
let trimmed = first.trimmingCharacters(in: .whitespaces)
3080+
if trimmed.hasPrefix("const "),
3081+
let namePart = trimmed.split(separator: " ").dropFirst().first,
3082+
trimmed.contains("= []") {
3083+
hoistedCleanupVar = String(namePart)
3084+
loweredLines[0] = "\(hoistedCleanupVar!) = [];"
3085+
}
3086+
}
3087+
if let hoistedName = hoistedCleanupVar {
3088+
printer.write("let \(hoistedName);")
3089+
}
30733090
printer.write("if (\(isSomeVar)) {")
30743091
printer.indent {
3075-
_ = wrappedFragment.printCode([value], scope, printer, cleanup)
3092+
for line in loweredLines {
3093+
printer.write(line)
3094+
}
3095+
// Cleanup is conditional on isSome as well
3096+
if !guardedCleanup.lines.isEmpty {
3097+
cleanup.write("if (\(isSomeVar)) {")
3098+
cleanup.indent {
3099+
cleanup.write(contentsOf: guardedCleanup)
3100+
}
3101+
cleanup.write("}")
3102+
}
30763103
}
30773104
printer.write("}")
30783105
printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);")

Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3219,6 +3219,84 @@ fileprivate func _bjs_struct_lift_FooContainer() -> Int32 {
32193219
}
32203220
#endif
32213221

3222+
extension ArrayMembers: _BridgedSwiftStruct {
3223+
@_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter() -> ArrayMembers {
3224+
let optStrings = {
3225+
let __isSome = _swift_js_pop_i32()
3226+
if __isSome == 0 {
3227+
return Optional<[String]>.none
3228+
} else {
3229+
return [String].bridgeJSLiftParameter()
3230+
}
3231+
}()
3232+
let ints = [Int].bridgeJSLiftParameter()
3233+
return ArrayMembers(ints: ints, optStrings: optStrings)
3234+
}
3235+
3236+
@_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() {
3237+
self.ints.bridgeJSLowerReturn()
3238+
let __bjs_isSome_optStrings = self.optStrings != nil
3239+
if let __bjs_unwrapped_optStrings = self.optStrings {
3240+
__bjs_unwrapped_optStrings.bridgeJSLowerReturn()
3241+
}
3242+
_swift_js_push_i32(__bjs_isSome_optStrings ? 1 : 0)
3243+
}
3244+
3245+
init(unsafelyCopying jsObject: JSObject) {
3246+
let __bjs_cleanupId = _bjs_struct_lower_ArrayMembers(jsObject.bridgeJSLowerParameter())
3247+
defer {
3248+
_swift_js_struct_cleanup(__bjs_cleanupId)
3249+
}
3250+
self = Self.bridgeJSLiftParameter()
3251+
}
3252+
3253+
func toJSObject() -> JSObject {
3254+
let __bjs_self = self
3255+
__bjs_self.bridgeJSLowerReturn()
3256+
return JSObject(id: UInt32(bitPattern: _bjs_struct_lift_ArrayMembers()))
3257+
}
3258+
}
3259+
3260+
#if arch(wasm32)
3261+
@_extern(wasm, module: "bjs", name: "swift_js_struct_lower_ArrayMembers")
3262+
fileprivate func _bjs_struct_lower_ArrayMembers(_ objectId: Int32) -> Int32
3263+
#else
3264+
fileprivate func _bjs_struct_lower_ArrayMembers(_ objectId: Int32) -> Int32 {
3265+
fatalError("Only available on WebAssembly")
3266+
}
3267+
#endif
3268+
3269+
#if arch(wasm32)
3270+
@_extern(wasm, module: "bjs", name: "swift_js_struct_lift_ArrayMembers")
3271+
fileprivate func _bjs_struct_lift_ArrayMembers() -> Int32
3272+
#else
3273+
fileprivate func _bjs_struct_lift_ArrayMembers() -> Int32 {
3274+
fatalError("Only available on WebAssembly")
3275+
}
3276+
#endif
3277+
3278+
@_expose(wasm, "bjs_ArrayMembers_sumValues")
3279+
@_cdecl("bjs_ArrayMembers_sumValues")
3280+
public func _bjs_ArrayMembers_sumValues() -> Int32 {
3281+
#if arch(wasm32)
3282+
let ret = ArrayMembers.bridgeJSLiftParameter().sumValues(_: [Int].bridgeJSLiftParameter())
3283+
return ret.bridgeJSLowerReturn()
3284+
#else
3285+
fatalError("Only available on WebAssembly")
3286+
#endif
3287+
}
3288+
3289+
@_expose(wasm, "bjs_ArrayMembers_firstString")
3290+
@_cdecl("bjs_ArrayMembers_firstString")
3291+
public func _bjs_ArrayMembers_firstString() -> Void {
3292+
#if arch(wasm32)
3293+
let ret = ArrayMembers.bridgeJSLiftParameter().firstString(_: [String].bridgeJSLiftParameter())
3294+
return ret.bridgeJSLowerReturn()
3295+
#else
3296+
fatalError("Only available on WebAssembly")
3297+
#endif
3298+
}
3299+
32223300
@_expose(wasm, "bjs_roundTripVoid")
32233301
@_cdecl("bjs_roundTripVoid")
32243302
public func _bjs_roundTripVoid() -> Void {
@@ -5432,6 +5510,43 @@ public func _bjs_roundTripFooContainer() -> Void {
54325510
#endif
54335511
}
54345512

5513+
@_expose(wasm, "bjs_roundTripArrayMembers")
5514+
@_cdecl("bjs_roundTripArrayMembers")
5515+
public func _bjs_roundTripArrayMembers() -> Void {
5516+
#if arch(wasm32)
5517+
let ret = roundTripArrayMembers(_: ArrayMembers.bridgeJSLiftParameter())
5518+
return ret.bridgeJSLowerReturn()
5519+
#else
5520+
fatalError("Only available on WebAssembly")
5521+
#endif
5522+
}
5523+
5524+
@_expose(wasm, "bjs_arrayMembersSum")
5525+
@_cdecl("bjs_arrayMembersSum")
5526+
public func _bjs_arrayMembersSum() -> Int32 {
5527+
#if arch(wasm32)
5528+
let _tmp_values = [Int].bridgeJSLiftParameter()
5529+
let _tmp_value = ArrayMembers.bridgeJSLiftParameter()
5530+
let ret = arrayMembersSum(_: _tmp_value, _: _tmp_values)
5531+
return ret.bridgeJSLowerReturn()
5532+
#else
5533+
fatalError("Only available on WebAssembly")
5534+
#endif
5535+
}
5536+
5537+
@_expose(wasm, "bjs_arrayMembersFirst")
5538+
@_cdecl("bjs_arrayMembersFirst")
5539+
public func _bjs_arrayMembersFirst() -> Void {
5540+
#if arch(wasm32)
5541+
let _tmp_values = [String].bridgeJSLiftParameter()
5542+
let _tmp_value = ArrayMembers.bridgeJSLiftParameter()
5543+
let ret = arrayMembersFirst(_: _tmp_value, _: _tmp_values)
5544+
return ret.bridgeJSLowerReturn()
5545+
#else
5546+
fatalError("Only available on WebAssembly")
5547+
#endif
5548+
}
5549+
54355550
@_expose(wasm, "bjs_Greeter_init")
54365551
@_cdecl("bjs_Greeter_init")
54375552
public func _bjs_Greeter_init(_ nameBytes: Int32, _ nameLength: Int32) -> UnsafeMutableRawPointer {
@@ -8315,7 +8430,7 @@ fileprivate func bjs_jsRoundTripIntArray() -> Void {
83158430

83168431
func _$jsRoundTripIntArray(_ items: [Int]) throws(JSException) -> [Int] {
83178432
let _ = items.bridgeJSLowerParameter()
8318-
let ret = bjs_jsRoundTripIntArray()
8433+
bjs_jsRoundTripIntArray()
83198434
if let error = _swift_js_take_exception() {
83208435
throw error
83218436
}
@@ -8333,7 +8448,7 @@ fileprivate func bjs_jsRoundTripStringArray() -> Void {
83338448

83348449
func _$jsRoundTripStringArray(_ items: [String]) throws(JSException) -> [String] {
83358450
let _ = items.bridgeJSLowerParameter()
8336-
let ret = bjs_jsRoundTripStringArray()
8451+
bjs_jsRoundTripStringArray()
83378452
if let error = _swift_js_take_exception() {
83388453
throw error
83398454
}

0 commit comments

Comments
 (0)