diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index eefc63a7e..cbec4ba31 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -11,17 +11,17 @@ import BridgeJSUtilities public struct ClosureCodegen { public init() {} + private func swiftClosureType(for signature: ClosureSignature) -> String { + let closureParams = signature.parameters.map { "\($0.swiftType)" }.joined(separator: ", ") + let swiftEffects = (signature.isAsync ? " async" : "") + (signature.isThrows ? " throws" : "") + let swiftReturnType = signature.returnType.swiftType + return "(\(closureParams))\(swiftEffects) -> \(swiftReturnType)" + } + func renderClosureHelpers(_ signature: ClosureSignature) throws -> [DeclSyntax] { let mangledName = signature.mangleName let helperName = "_BJS_Closure_\(mangledName)" - - let closureParams = signature.parameters.enumerated().map { _, type in - "\(type.swiftType)" - }.joined(separator: ", ") - - let swiftEffects = (signature.isAsync ? " async" : "") + (signature.isThrows ? " throws" : "") - let swiftReturnType = signature.returnType.swiftType - let swiftClosureType = "(\(closureParams))\(swiftEffects) -> \(swiftReturnType)" + let swiftClosureType = swiftClosureType(for: signature) let externName = "invoke_js_callback_\(signature.moduleName)_\(mangledName)" @@ -72,7 +72,7 @@ public struct ClosureCodegen { } else { parameters = " (" - + signature.parameters.enumerated().map { index, param in + + signature.parameters.enumerated().map { index, _ in "param\(index)" }.joined(separator: ", ") + ")" } @@ -113,12 +113,7 @@ public struct ClosureCodegen { } func renderClosureInvokeHandler(_ signature: ClosureSignature) throws -> DeclSyntax { - let closureParams = signature.parameters.enumerated().map { _, type in - "\(type.swiftType)" - }.joined(separator: ", ") - let swiftEffects = (signature.isAsync ? " async" : "") + (signature.isThrows ? " throws" : "") - let swiftReturnType = signature.returnType.swiftType - let swiftClosureType = "(\(closureParams))\(swiftEffects) -> \(swiftReturnType)" + let swiftClosureType = swiftClosureType(for: signature) let boxType = "_BridgeJSTypedClosureBox<\(swiftClosureType)>" let abiName = "invoke_swift_closure_\(signature.moduleName)_\(signature.mangleName)" @@ -144,17 +139,7 @@ public struct ClosureCodegen { let closureCallExpr = ExprSyntax("closure(\(raw: liftedParams.joined(separator: ", ")))") - // Determine return type - let abiReturnWasmType: WasmCoreType? - if signature.returnType == .void { - abiReturnWasmType = nil - } else if let wasmType = try signature.returnType.loweringReturnInfo().returnType { - abiReturnWasmType = wasmType - } else { - abiReturnWasmType = nil - } - - let throwReturn = abiReturnWasmType?.swiftReturnPlaceholderStmt ?? "return" + let abiReturnWasmType = try signature.returnType.loweringReturnInfo().returnType // Build signature using SwiftSignatureBuilder let funcSignature = SwiftSignatureBuilder.buildABIFunctionSignature( diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index e324c866f..bb42e9d1e 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -482,12 +482,7 @@ public class ExportSwift { } if function.effects.isStatic, let staticContext = function.staticContext { - let callName: String - switch staticContext { - case .className(let baseName), .enumName(let baseName), .structName(let baseName), - .namespaceEnum(let baseName): - callName = "\(baseName).\(function.name)" - } + let callName = "\(staticContextBaseName(staticContext)).\(function.name)" builder.call(name: callName, returnType: function.returnType) } else { builder.call(name: function.name, returnType: function.returnType) @@ -497,17 +492,61 @@ public class ExportSwift { return builder.render(abiName: function.abiName) } + private func staticContextBaseName(_ staticContext: StaticContext) -> String { + switch staticContext { + case .className(let baseName), .enumName(let baseName), .structName(let baseName), + .namespaceEnum(let baseName): + return baseName + } + } + + private func renderSingleExportedConstructor( + constructor: ExportedConstructor, + callName: String, + returnType: BridgeType + ) throws -> DeclSyntax { + let builder = ExportedThunkBuilder(effects: constructor.effects) + for param in constructor.parameters { + try builder.liftParameter(param: param) + } + builder.call(name: callName, returnType: returnType) + try builder.lowerReturnValue(returnType: returnType) + return builder.render(abiName: constructor.abiName) + } + + private func renderSingleExportedMethod( + method: ExportedFunction, + ownerTypeName: String, + instanceSelfType: BridgeType + ) throws -> DeclSyntax { + let builder = ExportedThunkBuilder(effects: method.effects) + if !method.effects.isStatic { + try builder.liftParameter(param: Parameter(label: nil, name: "_self", type: instanceSelfType)) + } + for param in method.parameters { + try builder.liftParameter(param: param) + } + + if method.effects.isStatic { + builder.call(name: "\(ownerTypeName).\(method.name)", returnType: method.returnType) + } else { + builder.callMethod(methodName: method.name, returnType: method.returnType) + } + try builder.lowerReturnValue(returnType: method.returnType) + return builder.render(abiName: method.abiName) + } + func renderSingleExportedStruct(struct structDef: ExportedStruct) throws -> [DeclSyntax] { var decls: [DeclSyntax] = [] if let constructor = structDef.constructor { - let builder = ExportedThunkBuilder(effects: constructor.effects) - for param in constructor.parameters { - try builder.liftParameter(param: param) - } - builder.call(name: structDef.swiftCallName, returnType: .swiftStruct(structDef.swiftCallName)) - try builder.lowerReturnValue(returnType: .swiftStruct(structDef.swiftCallName)) - decls.append(builder.render(abiName: constructor.abiName)) + decls.append( + try renderSingleExportedConstructor( + constructor: constructor, + callName: structDef.swiftCallName, + returnType: .swiftStruct(structDef.swiftCallName) + ) + ) } for property in structDef.properties where property.isStatic { @@ -520,28 +559,13 @@ public class ExportSwift { } for method in structDef.methods { - let builder = ExportedThunkBuilder(effects: method.effects) - - if method.effects.isStatic { - for param in method.parameters { - try builder.liftParameter(param: param) - } - builder.call(name: "\(structDef.swiftCallName).\(method.name)", returnType: method.returnType) - } else { - try builder.liftParameter( - param: Parameter(label: nil, name: "_self", type: .swiftStruct(structDef.swiftCallName)) - ) - for param in method.parameters { - try builder.liftParameter(param: param) - } - builder.callMethod( - methodName: method.name, - returnType: method.returnType + decls.append( + try renderSingleExportedMethod( + method: method, + ownerTypeName: structDef.swiftCallName, + instanceSelfType: .swiftStruct(structDef.swiftCallName) ) - } - - try builder.lowerReturnValue(returnType: method.returnType) - decls.append(builder.render(abiName: method.abiName)) + ) } return decls @@ -598,55 +622,29 @@ public class ExportSwift { var decls: [DeclSyntax] = [] if let constructor = klass.constructor { - let builder = ExportedThunkBuilder(effects: constructor.effects) - for param in constructor.parameters { - try builder.liftParameter(param: param) - } - builder.call(name: klass.swiftCallName, returnType: BridgeType.swiftHeapObject(klass.name)) - try builder.lowerReturnValue(returnType: BridgeType.swiftHeapObject(klass.name)) - decls.append(builder.render(abiName: constructor.abiName)) + decls.append( + try renderSingleExportedConstructor( + constructor: constructor, + callName: klass.swiftCallName, + returnType: .swiftHeapObject(klass.name) + ) + ) } for method in klass.methods { - let builder = ExportedThunkBuilder(effects: method.effects) - - if method.effects.isStatic { - for param in method.parameters { - try builder.liftParameter(param: param) - } - builder.call(name: "\(klass.swiftCallName).\(method.name)", returnType: method.returnType) - } else { - try builder.liftParameter( - param: Parameter(label: nil, name: "_self", type: BridgeType.swiftHeapObject(klass.swiftCallName)) - ) - for param in method.parameters { - try builder.liftParameter(param: param) - } - builder.callMethod( - methodName: method.name, - returnType: method.returnType + decls.append( + try renderSingleExportedMethod( + method: method, + ownerTypeName: klass.swiftCallName, + instanceSelfType: .swiftHeapObject(klass.swiftCallName) ) - } - try builder.lowerReturnValue(returnType: method.returnType) - decls.append(builder.render(abiName: method.abiName)) + ) } // Generate property getters and setters for property in klass.properties { - if property.isStatic { - decls.append( - contentsOf: try renderSingleExportedProperty( - property: property, - context: .classStatic(klass: klass) - ) - ) - } else { - decls.append( - contentsOf: try renderSingleExportedProperty( - property: property, - context: .classInstance(klass: klass) - ) - ) - } + let context: PropertyRenderingContext = + property.isStatic ? .classStatic(klass: klass) : .classInstance(klass: klass) + decls.append(contentsOf: try renderSingleExportedProperty(property: property, context: context)) } do { @@ -760,7 +758,7 @@ struct StackCodegen { func liftArrayExpression(elementType: BridgeType) -> ExprSyntax { switch elementType { case .jsObject(let className?) where className != "JSObject": - return liftArrayExpressionInline(elementType: elementType) + return "[JSObject].bridgeJSLiftParameter().map { \(raw: className)(unsafelyWrapping: $0) }" case .nullable, .closure: return liftArrayExpressionInline(elementType: elementType) case .void, .namespaceEnum: @@ -992,8 +990,7 @@ struct StackCodegen { let innerStatements = lowerUnwrappedOptionalStatements( wrappedType: wrappedType, - unwrappedVar: "__bjs_unwrapped_\(varPrefix)", - varPrefix: varPrefix + unwrappedVar: "__bjs_unwrapped_\(varPrefix)" ) for stmt in innerStatements { statements.append(stmt.description) @@ -1007,8 +1004,7 @@ struct StackCodegen { private func lowerUnwrappedOptionalStatements( wrappedType: BridgeType, - unwrappedVar: String, - varPrefix: String + unwrappedVar: String ) -> [CodeBlockItemSyntax] { switch wrappedType { case .jsObject(_?): diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.swift index cd0f56694..5708e7de9 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.swift @@ -76,16 +76,7 @@ public func _bjs_makeFoo() -> Int32 { @_cdecl("bjs_processFooArray") public func _bjs_processFooArray() -> Void { #if arch(wasm32) - let ret = processFooArray(_: { - let __count = Int(_swift_js_pop_i32()) - var __result: [Foo] = [] - __result.reserveCapacity(__count) - for _ in 0..<__count { - __result.append(Foo(unsafelyWrapping: JSObject.bridgeJSLiftParameter())) - } - __result.reverse() - return __result - }()) + let ret = processFooArray(_: [JSObject].bridgeJSLiftParameter().map { Foo(unsafelyWrapping: $0) }) ret.map { $0.jsObject }.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly") diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index 90493f773..ff219531d 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -386,6 +386,19 @@ extension JSObject: _BridgedSwiftStackType { extension JSValue: _BridgedSwiftStackType { public typealias StackLiftResult = JSValue + @_transparent + private static func bridgeJSRetainPayloadIfNeeded(kind: Int32, payload1: Int32) -> Int32 { + guard let kindEnum = JavaScriptValueKind(rawValue: UInt32(kind)) else { + return payload1 + } + switch kindEnum { + case .string, .object, .symbol, .bigInt: + return _swift_js_retain(payload1) + default: + return payload1 + } + } + @_spi(BridgeJS) public consuming func bridgeJSLowerParameter() -> (kind: Int32, payload1: Int32, payload2: Double) { return withRawJSValue { raw in ( @@ -409,17 +422,7 @@ extension JSValue: _BridgedSwiftStackType { _ payload1: Int32, _ payload2: Double ) -> JSValue { - let retainedPayload1: Int32 - if let kindEnum = JavaScriptValueKind(rawValue: UInt32(kind)) { - switch kindEnum { - case .string, .object, .symbol, .bigInt: - retainedPayload1 = _swift_js_retain(payload1) - default: - retainedPayload1 = payload1 - } - } else { - retainedPayload1 = payload1 - } + let retainedPayload1 = bridgeJSRetainPayloadIfNeeded(kind: kind, payload1: payload1) guard let kindEnum = JavaScriptValueKind(rawValue: UInt32(kind)) else { fatalError("Invalid JSValue kind: \(kind)") @@ -440,25 +443,12 @@ extension JSValue: _BridgedSwiftStackType { } @_spi(BridgeJS) public static func bridgeJSLiftReturn() -> JSValue { - let payload2 = _swift_js_pop_f64() - let payload1 = _swift_js_pop_i32() - let kind = _swift_js_pop_i32() - return bridgeJSLiftParameter(kind, payload1, payload2) + bridgeJSLiftParameter() } @_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void { let lowered = bridgeJSLowerParameter() - let retainedPayload1: Int32 - if let kind = JavaScriptValueKind(rawValue: UInt32(lowered.kind)) { - switch kind { - case .string, .object, .symbol, .bigInt: - retainedPayload1 = _swift_js_retain(lowered.payload1) - default: - retainedPayload1 = lowered.payload1 - } - } else { - retainedPayload1 = lowered.payload1 - } + let retainedPayload1 = Self.bridgeJSRetainPayloadIfNeeded(kind: lowered.kind, payload1: lowered.payload1) _swift_js_push_i32(lowered.kind) _swift_js_push_i32(retainedPayload1) _swift_js_push_f64(lowered.payload2) diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index e01c41511..a606ad741 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -5555,16 +5555,7 @@ public func _bjs_roundTripOptionalJSObjectArray() -> Void { @_cdecl("bjs_roundTripFooArray") public func _bjs_roundTripFooArray() -> Void { #if arch(wasm32) - let ret = roundTripFooArray(_: { - let __count = Int(_swift_js_pop_i32()) - var __result: [Foo] = [] - __result.reserveCapacity(__count) - for _ in 0..<__count { - __result.append(Foo(unsafelyWrapping: JSObject.bridgeJSLiftParameter())) - } - __result.reverse() - return __result - }()) + let ret = roundTripFooArray(_: [JSObject].bridgeJSLiftParameter().map { Foo(unsafelyWrapping: $0) }) ret.map { $0.jsObject }.bridgeJSLowerReturn() #else fatalError("Only available on WebAssembly")