Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 11 additions & 26 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)"

Expand Down Expand Up @@ -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: ", ") + ")"
}
Expand Down Expand Up @@ -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)"

Expand All @@ -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(
Expand Down
158 changes: 77 additions & 81 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand All @@ -1007,8 +1004,7 @@ struct StackCodegen {

private func lowerUnwrappedOptionalStatements(
wrappedType: BridgeType,
unwrappedVar: String,
varPrefix: String
unwrappedVar: String
) -> [CodeBlockItemSyntax] {
switch wrappedType {
case .jsObject(_?):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
42 changes: 16 additions & 26 deletions Sources/JavaScriptKit/BridgeJSIntrinsics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
(
Expand All @@ -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)")
Expand All @@ -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)
Expand Down
11 changes: 1 addition & 10 deletions Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down