◐ Shell
reader mode source ↗
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
File filter
Conversations
Jump to
Diff view
Apply and reload
Show whitespace
Diff view
Apply and reload
106 changes: 96 additions & 10 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public struct ClosureCodegen {
let closureParams = signature.parameters.map { "\(sendingPrefix)\($0.closureSwiftType)" }.joined(
separator: ", "
)
let swiftEffects = (signature.isAsync ? " async" : "") + (signature.isThrows ? " throws" : "")
let swiftReturnType = signature.returnType.closureSwiftType
return "(\(closureParams))\(swiftEffects) -> \(swiftReturnType)"
}
Expand Down Expand Up @@ -73,7 +73,17 @@ public struct ClosureCodegen {
helperEnumDeclPrinter.indent {
helperEnumDeclPrinter.write("let callback = JSObject.bridgeJSLiftParameter(callbackId)")
let parameters: String
if signature.parameters.isEmpty {
parameters = ""
} else if signature.parameters.count == 1 {
parameters = " param0"
Expand Down Expand Up @@ -146,22 +156,25 @@ public struct ClosureCodegen {
liftedParams.append("\(paramType.swiftType).bridgeJSLiftParameter(\(argNames.joined(separator: ", ")))")
}

let closureCallExpr = ExprSyntax("closure(\(raw: liftedParams.joined(separator: ", ")))")

let abiReturnWasmType = try signature.returnType.loweringReturnInfo().returnType

// Build signature using SwiftSignatureBuilder
let funcSignature = SwiftSignatureBuilder.buildABIFunctionSignature(
abiParameters: abiParams,
returnType: abiReturnWasmType
)

// Build function declaration using helper
let funcDecl = SwiftCodePattern.buildExposedFunctionDecl(
abiName: abiName,
signature: funcSignature
) { printer in
printer.write("let closure = Unmanaged<\(boxType)>.fromOpaque(boxPtr).takeUnretainedValue().closure")
if signature.returnType == .void {
printer.write(closureCallExpr.description)
} else {
Expand Up @@ -189,6 +202,79 @@ public struct ClosureCodegen {
}
}

return DeclSyntax(funcDecl)
}

Expand Down
9 changes: 4 additions & 5 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift
Original file line number Diff line number Diff line change
@@ -272,9 +272,7 @@ public struct ImportTS {
}
}

// Add exception check for ImportTS context (skipped for async, where
// errors are funneled through the JS-side reject path)
if !effects.isAsync && context == .importTS {
body.write("if let error = _swift_js_take_exception() { throw error }")
}
}
Expand Down Expand Up @@ -323,18 +321,19 @@ public struct ImportTS {
let innerBody = body
body = CodeFragmentPrinter()

let rejectFactory = "makeRejectClosure: { JSTypedClosure<(sending JSValue) -> Void>($0) }"
if returnType == .void {
let resolveFactory = "makeResolveClosure: { JSTypedClosure<() -> Void>($0) }"
body.write(
"try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in"
)
} else {
let resolveSwiftType = returnType.closureSwiftType
let resolveFactory =
"makeResolveClosure: { JSTypedClosure<(sending \(resolveSwiftType)) -> Void>($0) }"
body.write(
"let resolved = try await _bjs_awaitPromise(\(resolveFactory), \(rejectFactory)) { resolveRef, rejectRef in"
)
}
body.indent {
Expand Down
20 changes: 15 additions & 5 deletions Plugins/BridgeJS/Sources/BridgeJSCore/Misc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,21 @@ import SwiftSyntax
import class Foundation.ProcessInfo

public struct DiagnosticError: Error {
public let node: Syntax
public let message: String
public let hint: String?

public init(node: some SyntaxProtocol, message: String, hint: String? = nil) {
self.node = Syntax(node)
self.message = message
self.hint = hint
}

/// Formats the diagnostic error as a string.
Expand All @@ -166,12 +173,14 @@ public struct DiagnosticError: Error {

let lineNumberWidth = max(3, String(lines.count).count)

let header: String = {
guard colorize else {
return "\(displayFileName):\(startLocation.line):\(startLocation.column): error: \(message)"
}
return
"\(displayFileName):\(startLocation.line):\(startLocation.column): \(ANSI.boldRed)error: \(ANSI.boldDefault)\(message)\(ANSI.reset)"
}()

let highlightStartColumn = min(max(1, startLocation.column), mainLine.utf8.count + 1)
Expand Down Expand Up @@ -227,8 +236,8 @@ public struct DiagnosticError: Error {
let pointerSpacing = max(0, highlightStartColumn - 1)
let pointerMessage: String = {
let pointer = String(repeating: " ", count: pointerSpacing) + "`- "
guard colorize else { return pointer + "error: \(message)" }
return pointer + "\(ANSI.boldRed)error: \(ANSI.boldDefault)\(message)\(ANSI.reset)"
}()
descriptionParts.append(
Self.formatSourceLine(
Expand Down Expand Up @@ -304,6 +313,7 @@ public struct BridgeJSCoreDiagnosticError: Swift.Error, CustomStringConvertible
private enum ANSI {
static let reset = "\u{001B}[0;0m"
static let boldRed = "\u{001B}[1;31m"
static let boldDefault = "\u{001B}[1;39m"
static let cyan = "\u{001B}[0;36m"
static let underline = "\u{001B}[4;39m"
Expand Down
65 changes: 45 additions & 20 deletions Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public final class SwiftToSkeleton {
private var sourceFiles: [(sourceFile: SourceFileSyntax, inputFilePath: String)] = []
private var usedExternalModules = Set<String>()

public init(
progress: ProgressReporting,
moduleName: String,
Expand Up @@ -87,10 +90,15 @@ public final class SwiftToSkeleton {
)
importCollector.walk(sourceFile)

let importErrorsFatal = importCollector.errors.filter { !$0.message.contains("Unsupported type '") }
if !exportCollector.errors.isEmpty || !importErrorsFatal.isEmpty {
perSourceErrors.append(
(inputFilePath: inputFilePath, errors: exportCollector.errors + importErrorsFatal)
)
}

Expand Down Expand Up @@ -191,7 +199,40 @@ public final class SwiftToSkeleton {
}

let isAsync = functionType.effectSpecifiers?.asyncSpecifier != nil
let isThrows = functionType.effectSpecifiers?.throwsClause != nil

return .closure(
ClosureSignature(
Expand Down Expand Up @@ -1028,22 +1069,6 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
guard let type = resolvedType else {
continue // Skip unsupported types
}
if case .closure(let signature, _) = type {
if signature.isAsync {
diagnose(
node: param.type,
message: "Async is not supported for Swift closures yet."
)
continue
}
if signature.isThrows {
diagnose(
node: param.type,
message: "Throws is not supported for Swift closures yet."
)
continue
}
}
if case .nullable(let wrappedType, _) = type, wrappedType.isOptional {
diagnoseNestedOptional(node: param.type, type: param.type.trimmedDescription)
continue
Expand Down
6 changes: 4 additions & 2 deletions Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ public struct BridgeJSLink {
) throws -> [String] {
let printer = CodeFragmentPrinter()
let builder = ExportedThunkBuilder(
effects: Effects(isAsync: false, isThrows: true),
hasDirectAccessToSwiftClass: false,
intrinsicRegistry: intrinsicRegistry
)
Expand Down Expand Up @@ -3743,7 +3743,9 @@ extension BridgeType {
let paramTypes = signature.parameters.enumerated().map { index, param in
"arg\(index): \(param.tsType)"
}.joined(separator: ", ")
return "(\(paramTypes)) => \(signature.returnType.tsType)"
case .array(let elementType):
let inner = elementType.tsType
if inner.contains("|") || inner.contains("=>") {
Expand Down
Loading
Loading
Toggle all file notes Toggle all file annotations