Skip to content

Commit ad8e34a

Browse files
BridgeJS: Move all @_extern(wasm) declarations to top-level
When generating `@_extern(wasm)` function declarations in the BridgeJS plugin, we now need to ensure that they are placed at the top level of the module, rather than nested within other declarations because of the restrictions of the main branch Swift compiler.
1 parent d7610c8 commit ad8e34a

File tree

2 files changed

+84
-54
lines changed

2 files changed

+84
-54
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 79 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,12 +1497,12 @@ public class ExportSwift {
14971497
}
14981498

14991499
for signature in closureSignatures.sorted(by: { $0.mangleName < $1.mangleName }) {
1500-
decls.append(try closureCodegen.renderClosureHelper(signature: signature))
1500+
decls.append(contentsOf: try closureCodegen.renderClosureHelper(signature: signature))
15011501
decls.append(try closureCodegen.renderClosureInvokeHandler(signature: signature))
15021502
}
15031503

15041504
for proto in exportedProtocols {
1505-
decls.append(try renderProtocolWrapper(protocol: proto))
1505+
decls.append(contentsOf: try renderProtocolWrapper(protocol: proto))
15061506
}
15071507

15081508
for enumDef in exportedEnums {
@@ -1547,6 +1547,7 @@ public class ExportSwift {
15471547
var parameters: [Parameter] = []
15481548
var abiParameterSignatures: [(name: String, type: WasmCoreType)] = []
15491549
var abiReturnType: WasmCoreType?
1550+
var externDecls: [DeclSyntax] = []
15501551
let effects: Effects
15511552

15521553
init(effects: Effects) {
@@ -1839,7 +1840,7 @@ public class ExportSwift {
18391840
return lines.isEmpty ? "" : lines.joined(separator: "\n") + "\n"
18401841
}
18411842

1842-
func renderClosureHelper(signature: ClosureSignature) throws -> DeclSyntax {
1843+
func renderClosureHelper(signature: ClosureSignature) throws -> [DeclSyntax] {
18431844
let mangledName = signature.mangleName
18441845
let helperName = "_BJS_Closure_\(mangledName)"
18451846
let boxClassName = "_BJS_ClosureBox_\(mangledName)"
@@ -1899,25 +1900,31 @@ public class ExportSwift {
18991900
invokeReturnType = "Void"
19001901
}
19011902

1903+
let externName = "invoke_js_callback_\(signature.moduleName)_\(mangledName)"
1904+
19021905
let returnLifting: String
19031906
if signature.returnType == .void {
1904-
returnLifting = "_invoke(\(invokeCallArgs.joined(separator: ", ")))"
1907+
returnLifting = "\(externName)(\(invokeCallArgs.joined(separator: ", ")))"
19051908
} else if case .optional = signature.returnType {
19061909
returnLifting = """
1907-
_invoke(\(invokeCallArgs.joined(separator: ", ")))
1910+
\(externName)(\(invokeCallArgs.joined(separator: ", ")))
19081911
return \(signature.returnType.swiftType).bridgeJSLiftReturnFromSideChannel()
19091912
"""
19101913
} else {
19111914
returnLifting = """
1912-
let resultId = _invoke(\(invokeCallArgs.joined(separator: ", ")))
1915+
let resultId = \(externName)(\(invokeCallArgs.joined(separator: ", ")))
19131916
return \(signature.returnType.swiftType).bridgeJSLiftReturn(resultId)
19141917
"""
19151918
}
19161919

1917-
let externName = "invoke_js_callback_\(signature.moduleName)_\(mangledName)"
19181920
let optionalLoweringCode = try generateOptionalParameterLowering(signature: signature)
19191921

1920-
return """
1922+
let externDecl: DeclSyntax = """
1923+
@_extern(wasm, module: "bjs", name: "\(raw: externName)")
1924+
fileprivate func \(raw: externName)(\(raw: invokeSignature)) -> \(raw: invokeReturnType)
1925+
"""
1926+
1927+
let boxDecl: DeclSyntax = """
19211928
private final class \(raw: boxClassName): _BridgedSwiftClosureBox {
19221929
let closure: \(raw: closureType)
19231930
init(_ closure: @escaping \(raw: closureType)) {
@@ -1935,16 +1942,15 @@ public class ExportSwift {
19351942
let callback = JSObject.bridgeJSLiftParameter(callbackId)
19361943
return { [callback] \(raw: signature.parameters.indices.map { "param\($0)" }.joined(separator: ", ")) in
19371944
#if arch(wasm32)
1938-
@_extern(wasm, module: "bjs", name: "\(raw: externName)")
1939-
func _invoke(\(raw: invokeSignature)) -> \(raw: invokeReturnType)
19401945
\(raw: optionalLoweringCode)\(raw: returnLifting)
19411946
#else
19421947
fatalError("Only available on WebAssembly")
1943-
#endif
1948+
#endif
19441949
}
19451950
}
19461951
}
19471952
"""
1953+
return [externDecl, boxDecl]
19481954
}
19491955

19501956
func renderClosureInvokeHandler(signature: ClosureSignature) throws -> DeclSyntax {
@@ -2051,28 +2057,28 @@ public class ExportSwift {
20512057
func renderAssociatedValueEnumHelpers(_ enumDef: ExportedEnum) -> DeclSyntax {
20522058
let typeName = enumDef.swiftCallName
20532059
return """
2054-
extension \(raw: typeName): _BridgedSwiftAssociatedValueEnum {
2060+
extension \(raw: typeName): _BridgedSwiftAssociatedValueEnum {
20552061
private static func _bridgeJSLiftFromCaseId(_ caseId: Int32) -> \(raw: typeName) {
20562062
switch caseId {
20572063
\(raw: generateStackLiftSwitchCases(enumDef: enumDef).joined(separator: "\n"))
20582064
default: fatalError("Unknown \(raw: typeName) case ID: \\(caseId)")
20592065
}
20602066
}
2061-
2067+
20622068
// MARK: Protocol Export
2063-
2069+
20642070
@_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 {
20652071
switch self {
20662072
\(raw: generateLowerParameterSwitchCases(enumDef: enumDef).joined(separator: "\n"))
20672073
}
20682074
}
2069-
2075+
20702076
@_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ caseId: Int32) -> \(raw: typeName) {
20712077
return _bridgeJSLiftFromCaseId(caseId)
20722078
}
2073-
2079+
20742080
// MARK: ExportSwift
2075-
2081+
20762082
@_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> \(raw: typeName) {
20772083
return _bridgeJSLiftFromCaseId(caseId)
20782084
}
@@ -2460,7 +2466,7 @@ public class ExportSwift {
24602466
}
24612467

24622468
// Generate ConvertibleToJSValue extension
2463-
decls.append(renderConvertibleToJSValueExtension(klass: klass))
2469+
decls.append(contentsOf: renderConvertibleToJSValueExtension(klass: klass))
24642470

24652471
return decls
24662472
}
@@ -2501,36 +2507,40 @@ public class ExportSwift {
25012507
/// }
25022508
/// }
25032509
/// ```
2504-
func renderConvertibleToJSValueExtension(klass: ExportedClass) -> DeclSyntax {
2510+
func renderConvertibleToJSValueExtension(klass: ExportedClass) -> [DeclSyntax] {
25052511
let wrapFunctionName = "_bjs_\(klass.name)_wrap"
25062512
let externFunctionName = "bjs_\(klass.name)_wrap"
25072513

25082514
// If the class has an explicit access control, we need to add it to the extension declaration.
25092515
let accessControl = klass.explicitAccessControl.map { "\($0) " } ?? ""
2510-
return """
2516+
let extensionDecl: DeclSyntax = """
25112517
extension \(raw: klass.swiftCallName): ConvertibleToJSValue, _BridgedSwiftHeapObject {
25122518
\(raw: accessControl)var jsValue: JSValue {
2513-
#if arch(wasm32)
2514-
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: externFunctionName)")
2515-
func \(raw: wrapFunctionName)(_: UnsafeMutableRawPointer) -> Int32
2516-
#else
2517-
func \(raw: wrapFunctionName)(_: UnsafeMutableRawPointer) -> Int32 {
2518-
fatalError("Only available on WebAssembly")
2519-
}
2520-
#endif
25212519
return .object(JSObject(id: UInt32(bitPattern: \(raw: wrapFunctionName)(Unmanaged.passRetained(self).toOpaque()))))
25222520
}
25232521
}
25242522
"""
2523+
let externDecl: DeclSyntax = """
2524+
#if arch(wasm32)
2525+
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: externFunctionName)")
2526+
fileprivate func \(raw: wrapFunctionName)(_: UnsafeMutableRawPointer) -> Int32
2527+
#else
2528+
fileprivate func \(raw: wrapFunctionName)(_: UnsafeMutableRawPointer) -> Int32 {
2529+
fatalError("Only available on WebAssembly")
2530+
}
2531+
#endif
2532+
"""
2533+
return [extensionDecl, externDecl]
25252534
}
25262535

25272536
/// Creates a struct that wraps a JSObject and implements protocol methods
25282537
/// by calling `@_extern(wasm)` functions that forward to JavaScript via JSObject ID
2529-
func renderProtocolWrapper(protocol proto: ExportedProtocol) throws -> DeclSyntax {
2538+
func renderProtocolWrapper(protocol proto: ExportedProtocol) throws -> [DeclSyntax] {
25302539
let wrapperName = "Any\(proto.name)"
25312540
let protocolName = proto.name
25322541

25332542
var methodDecls: [DeclSyntax] = []
2543+
var externDecls: [DeclSyntax] = []
25342544

25352545
for method in proto.methods {
25362546
var swiftParams: [String] = []
@@ -2617,10 +2627,16 @@ public class ExportSwift {
26172627
"""
26182628
}
26192629
}
2630+
2631+
externDecls.append(
2632+
"""
2633+
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: method.abiName)")
2634+
fileprivate func _extern_\(raw: method.name)(\(raw: externParams.joined(separator: ", ")))\(raw: externReturnType)
2635+
"""
2636+
)
2637+
26202638
let methodImplementation: DeclSyntax = """
26212639
func \(raw: method.name)(\(raw: swiftParams.joined(separator: ", ")))\(raw: returnTypeStr) {
2622-
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: method.abiName)")
2623-
func _extern_\(raw: method.name)(\(raw: externParams.joined(separator: ", ")))\(raw: externReturnType)
26242640
\(raw: callCode)
26252641
}
26262642
"""
@@ -2631,34 +2647,36 @@ public class ExportSwift {
26312647
var propertyDecls: [DeclSyntax] = []
26322648

26332649
for property in proto.properties {
2634-
let propertyImpl = try renderProtocolProperty(
2650+
let (propertyImpl, propertyExternDecls) = try renderProtocolProperty(
26352651
property: property,
26362652
protocolName: protocolName,
26372653
moduleName: moduleName
26382654
)
26392655
propertyDecls.append(propertyImpl)
2656+
externDecls.append(contentsOf: propertyExternDecls)
26402657
}
26412658

26422659
let allDecls = (methodDecls + propertyDecls).map { $0.description }.joined(separator: "\n\n")
26432660

2644-
return """
2661+
let structDecl: DeclSyntax = """
26452662
struct \(raw: wrapperName): \(raw: protocolName), _BridgedSwiftProtocolWrapper {
26462663
let jsObject: JSObject
2647-
2664+
26482665
\(raw: allDecls)
2649-
2666+
26502667
static func bridgeJSLiftParameter(_ value: Int32) -> Self {
26512668
return \(raw: wrapperName)(jsObject: JSObject(id: UInt32(bitPattern: value)))
26522669
}
26532670
}
26542671
"""
2672+
return [structDecl] + externDecls
26552673
}
26562674

26572675
private func renderProtocolProperty(
26582676
property: ExportedProtocolProperty,
26592677
protocolName: String,
26602678
moduleName: String
2661-
) throws -> DeclSyntax {
2679+
) throws -> (propertyDecl: DeclSyntax, externDecls: [DeclSyntax]) {
26622680
let getterAbiName = ABINameGenerator.generateABIName(
26632681
baseName: property.name,
26642682
operation: "get",
@@ -2679,33 +2697,39 @@ public class ExportSwift {
26792697
// Optional case/raw enums use side-channel reading (Void return)
26802698
getterReturnType = ""
26812699
getterBody = """
2682-
_extern_get(this: Int32(bitPattern: jsObject.id))
2700+
\(getterAbiName)(this: Int32(bitPattern: jsObject.id))
26832701
return \(property.type.swiftType).bridgeJSLiftReturnFromSideChannel()
26842702
"""
26852703
} else if let abiType = liftingInfo.valueToLift {
26862704
getterReturnType = " -> \(abiType.swiftType)"
26872705
getterBody = """
2688-
let ret = _extern_get(this: Int32(bitPattern: jsObject.id))
2706+
let ret = \(getterAbiName)(this: Int32(bitPattern: jsObject.id))
26892707
return \(property.type.swiftType).bridgeJSLiftReturn(ret)
26902708
"""
26912709
} else {
26922710
getterReturnType = ""
26932711
getterBody = """
2694-
_extern_get(this: Int32(bitPattern: jsObject.id))
2712+
\(getterAbiName)(this: Int32(bitPattern: jsObject.id))
26952713
return \(property.type.swiftType).bridgeJSLiftReturn()
26962714
"""
26972715
}
26982716

2717+
let getterExternDecl: DeclSyntax = """
2718+
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: getterAbiName)")
2719+
fileprivate func \(raw: getterAbiName)(this: Int32)\(raw: getterReturnType)
2720+
"""
2721+
26992722
if property.isReadonly {
2700-
return """
2723+
return (
2724+
"""
27012725
var \(raw: property.name): \(raw: property.type.swiftType) {
27022726
get {
2703-
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: getterAbiName)")
2704-
func _extern_get(this: Int32)\(raw: getterReturnType)
27052727
\(raw: getterBody)
27062728
}
27072729
}
2708-
"""
2730+
""",
2731+
[getterExternDecl]
2732+
)
27092733
} else {
27102734
let loweringInfo = try property.type.loweringParameterInfo(context: .exportSwift)
27112735

@@ -2720,28 +2744,32 @@ public class ExportSwift {
27202744
let wrappedParam = loweringInfo.loweredParameters[1].name
27212745
setterBody = """
27222746
let (\(isSomeParam), \(wrappedParam)) = newValue.bridgeJSLowerParameterWithPresence()
2723-
_extern_set(this: Int32(bitPattern: jsObject.id), \(isSomeParam): \(isSomeParam), \(wrappedParam): \(wrappedParam))
2747+
\(setterAbiName)(this: Int32(bitPattern: jsObject.id), \(isSomeParam): \(isSomeParam), \(wrappedParam): \(wrappedParam))
27242748
"""
27252749
} else {
27262750
let paramName = loweringInfo.loweredParameters[0].name
27272751
setterBody =
2728-
"_extern_set(this: Int32(bitPattern: jsObject.id), \(paramName): newValue.bridgeJSLowerParameter())"
2752+
"\(setterAbiName)(this: Int32(bitPattern: jsObject.id), \(paramName): newValue.bridgeJSLowerParameter())"
27292753
}
27302754

2731-
return """
2755+
let setterExternDecl: DeclSyntax = """
2756+
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: setterAbiName)")
2757+
fileprivate func \(raw: setterAbiName)(\(raw: setterParams))
2758+
"""
2759+
2760+
return (
2761+
"""
27322762
var \(raw: property.name): \(raw: property.type.swiftType) {
27332763
get {
2734-
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: getterAbiName)")
2735-
func _extern_get(this: Int32)\(raw: getterReturnType)
27362764
\(raw: getterBody)
27372765
}
27382766
set {
2739-
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: setterAbiName)")
2740-
func _extern_set(\(raw: setterParams))
27412767
\(raw: setterBody)
27422768
}
27432769
}
2744-
"""
2770+
""",
2771+
[getterExternDecl, setterExternDecl]
2772+
)
27452773
}
27462774
}
27472775
}

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ public struct ImportTS {
175175
)
176176
),
177177
body: CodeBlockSyntax {
178-
self.renderImportDecl()
179178
body
180179
}
181180
)
@@ -200,7 +199,6 @@ public struct ImportTS {
200199
effectSpecifiers: ImportTS.buildFunctionEffect(throws: true, async: false)
201200
),
202201
bodyBuilder: {
203-
self.renderImportDecl()
204202
body
205203
}
206204
)
@@ -228,6 +226,7 @@ public struct ImportTS {
228226
}
229227
builder.call(returnType: function.returnType)
230228
try builder.liftReturnValue(returnType: function.returnType)
229+
topLevelDecls.append(builder.renderImportDecl())
231230
return [
232231
builder.renderThunkDecl(
233232
name: function.name,
@@ -249,6 +248,7 @@ public struct ImportTS {
249248
}
250249
builder.call(returnType: method.returnType)
251250
try builder.liftReturnValue(returnType: method.returnType)
251+
topLevelDecls.append(builder.renderImportDecl())
252252
return [
253253
builder.renderThunkDecl(
254254
name: method.name,
@@ -266,6 +266,7 @@ public struct ImportTS {
266266
}
267267
builder.call(returnType: .jsObject(name))
268268
builder.assignThis(returnType: .jsObject(name))
269+
topLevelDecls.append(builder.renderImportDecl())
269270
return [
270271
builder.renderConstructorDecl(parameters: constructor.parameters)
271272
]
@@ -279,11 +280,11 @@ public struct ImportTS {
279280
try builder.lowerParameter(param: Parameter(label: nil, name: "self", type: .jsObject(name)))
280281
builder.call(returnType: property.type)
281282
try builder.liftReturnValue(returnType: property.type)
283+
topLevelDecls.append(builder.renderImportDecl())
282284
return AccessorDeclSyntax(
283285
accessorSpecifier: .keyword(.get),
284286
effectSpecifiers: Self.buildAccessorEffect(throws: true, async: false),
285287
body: CodeBlockSyntax {
286-
builder.renderImportDecl()
287288
builder.body
288289
}
289290
)
@@ -298,6 +299,7 @@ public struct ImportTS {
298299
try builder.lowerParameter(param: Parameter(label: nil, name: "self", type: .jsObject(name)))
299300
try builder.lowerParameter(param: newValue)
300301
builder.call(returnType: .void)
302+
topLevelDecls.append(builder.renderImportDecl())
301303
return builder.renderThunkDecl(
302304
name: "set\(property.name.capitalizedFirstLetter)",
303305
parameters: [newValue],

0 commit comments

Comments
 (0)