Return signal handler values across the C ABI
This commit is contained in:
parent
a172d07ab1
commit
858544cdec
1 changed files with 83 additions and 5 deletions
|
|
@ -867,6 +867,75 @@ private func renderClass(_ plan: ClassPlan) -> String {
|
||||||
|
|
||||||
// ── Signal rendering ──
|
// ── Signal rendering ──
|
||||||
|
|
||||||
|
/// How a signal handler's Swift return value is handed back across the C ABI.
|
||||||
|
///
|
||||||
|
/// `nil` for a void signal. Switches on `marshalIn` - the mapping's
|
||||||
|
/// Swift-into-C direction, which is exactly the direction a signal return
|
||||||
|
/// travels - so the spellings match the parameter path in `renderCallable`.
|
||||||
|
private struct SignalReturnBridge {
|
||||||
|
/// The `@_cdecl` function's return type.
|
||||||
|
let cType: String
|
||||||
|
/// Returned when `data` is nil and the closure never runs.
|
||||||
|
let zero: String
|
||||||
|
/// Statements that evaluate `call` and `return` its C form. Emitted
|
||||||
|
/// inside `MainActor.assumeIsolated`.
|
||||||
|
let emit: (_ call: String) -> [String]
|
||||||
|
}
|
||||||
|
|
||||||
|
private func signalReturnBridge(_ m: Mapping) -> SignalReturnBridge? {
|
||||||
|
let isOptional = m.swiftType.hasSuffix("?")
|
||||||
|
switch m.marshalIn {
|
||||||
|
case .boolToGboolean:
|
||||||
|
return .init(cType: "Int32", zero: "0", emit: { ["return (\($0)) ? 1 : 0"] })
|
||||||
|
case .direct:
|
||||||
|
return .init(cType: m.cSwiftType, zero: "0", emit: { ["return \($0)"] })
|
||||||
|
case .numericCast:
|
||||||
|
return .init(cType: m.cSwiftType, zero: "0", emit: { ["return numericCast(\($0))"] })
|
||||||
|
case .enumRaw, .bitfieldRaw:
|
||||||
|
return .init(
|
||||||
|
cType: m.cSwiftType, zero: ".init(rawValue: 0)",
|
||||||
|
emit: { ["return \(m.cSwiftType)(rawValue: numericCast((\($0)).rawValue))"] })
|
||||||
|
case .stringToC:
|
||||||
|
// The signal's return is an owned `char*`; duplicate so the Swift
|
||||||
|
// String's buffer is not handed to C.
|
||||||
|
return .init(cType: "UnsafeMutablePointer<CChar>?", zero: "nil",
|
||||||
|
emit: { ["return g_strdup(\($0))"] })
|
||||||
|
case .objectPointer, .interfacePointer:
|
||||||
|
// transfer=full (`.objectWrap`/`.interfaceWrap(adopt:)`) means C takes
|
||||||
|
// ownership of a reference, so add one; transfer=none
|
||||||
|
// (`.objectRetain`) hands over a borrowed pointer unchanged.
|
||||||
|
let transfersFull: Bool
|
||||||
|
switch m.marshalOut {
|
||||||
|
case .objectRetain: transfersFull = false
|
||||||
|
default: transfersFull = true
|
||||||
|
}
|
||||||
|
let refLine = transfersFull ? ["_ = g_object_ref(r.pointer)"] : []
|
||||||
|
// _instancePointer overloads return OpaquePointer or
|
||||||
|
// UnsafeMutablePointer<T>; neither matches UnsafeMutableRawPointer.
|
||||||
|
if m.cSwiftType.replacingOccurrences(of: "?", with: "") == "UnsafeMutableRawPointer" {
|
||||||
|
if isOptional {
|
||||||
|
return .init(cType: m.cSwiftType, zero: "nil", emit: { call in
|
||||||
|
["guard let r = \(call) else { return nil }"] + refLine
|
||||||
|
+ ["return r.pointer"]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return .init(cType: m.cSwiftType, zero: "nil", emit: { call in
|
||||||
|
["let r = \(call)"] + refLine + ["return r.pointer"]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if isOptional {
|
||||||
|
return .init(cType: m.cSwiftType, zero: "nil", emit: { call in
|
||||||
|
["guard let r = \(call) else { return nil }"] + refLine
|
||||||
|
+ ["return _instancePointer(r.pointer)"]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return .init(cType: m.cSwiftType, zero: "nil", emit: { call in
|
||||||
|
["let r = \(call)"] + refLine + ["return _instancePointer(r.pointer)"]
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Renders a `@_cdecl nonisolated` trampoline for a GObject signal.
|
/// Renders a `@_cdecl nonisolated` trampoline for a GObject signal.
|
||||||
///
|
///
|
||||||
/// The `@_cdecl` ABI is required because C calls this function via a raw
|
/// The `@_cdecl` ABI is required because C calls this function via a raw
|
||||||
|
|
@ -914,13 +983,21 @@ private func renderSignalTrampoline(_ plan: SignalPlan) -> [String] {
|
||||||
}
|
}
|
||||||
let wrapperRefs = (0..<allParams.count).map { "w\($0)" }.joined(separator: ", ")
|
let wrapperRefs = (0..<allParams.count).map { "w\($0)" }.joined(separator: ", ")
|
||||||
let shadowBody = shadowLines.map { " \($0)" }.joined(separator: "\n")
|
let shadowBody = shadowLines.map { " \($0)" }.joined(separator: "\n")
|
||||||
let wrapperBody = (wrapperLines + ["box.closure(\(wrapperRefs))"]).map { " \($0)" }.joined(separator: "\n")
|
let bridge = plan.returnMapping.flatMap(signalReturnBridge)
|
||||||
|
let call = "box.closure(\(wrapperRefs))"
|
||||||
|
let tail = bridge.map { $0.emit(call) } ?? [call]
|
||||||
|
let wrapperBody = (wrapperLines + tail).map { " \($0)" }.joined(separator: "\n")
|
||||||
|
|
||||||
lines.append("@_cdecl(\"\(plan.trampolineCName)\")")
|
lines.append("@_cdecl(\"\(plan.trampolineCName)\")")
|
||||||
lines.append("nonisolated func \(plan.trampolineCName)(\(cDecl)) {")
|
lines.append("nonisolated func \(plan.trampolineCName)(\(cDecl))\(bridge.map { " -> \($0.cType)" } ?? "") {")
|
||||||
lines.append(" guard let data else { return }")
|
lines.append(" guard let data else { return \(bridge?.zero ?? "") }")
|
||||||
lines.append(" let box = Unmanaged<_ClosureBox<\(closureType)>>.fromOpaque(data).takeUnretainedValue()")
|
lines.append(" let box = Unmanaged<_ClosureBox<\(closureType)>>.fromOpaque(data).takeUnretainedValue()")
|
||||||
lines.append(shadowBody)
|
lines.append(shadowBody)
|
||||||
lines.append(" MainActor.assumeIsolated {")
|
if let bridge {
|
||||||
|
lines.append(" return MainActor.assumeIsolated { () -> \(bridge.cType) in")
|
||||||
|
} else {
|
||||||
|
lines.append(" MainActor.assumeIsolated {")
|
||||||
|
}
|
||||||
lines.append(wrapperBody)
|
lines.append(wrapperBody)
|
||||||
lines.append(" }")
|
lines.append(" }")
|
||||||
lines.append("}")
|
lines.append("}")
|
||||||
|
|
@ -987,6 +1064,7 @@ private func renderSignalConnect(_ plan: SignalPlan, className: String) -> [Stri
|
||||||
let closureParams = closureParamTypes.isEmpty ? "" : closureParamTypes.joined(separator: ", ")
|
let closureParams = closureParamTypes.isEmpty ? "" : closureParamTypes.joined(separator: ", ")
|
||||||
let closureType = "(\(closureParams)) -> \(closureRet)"
|
let closureType = "(\(closureParams)) -> \(closureRet)"
|
||||||
|
|
||||||
|
let cRet = plan.returnMapping.flatMap(signalReturnBridge)?.cType ?? "Void"
|
||||||
// Build @convention(c) type for the trampoline's unsafeBitCast
|
// Build @convention(c) type for the trampoline's unsafeBitCast
|
||||||
let cTypes = ["UnsafeMutableRawPointer"] + realParams.map { p in
|
let cTypes = ["UnsafeMutableRawPointer"] + realParams.map { p in
|
||||||
p.mapping.cSwiftType.replacingOccurrences(of: "?", with: "")
|
p.mapping.cSwiftType.replacingOccurrences(of: "?", with: "")
|
||||||
|
|
@ -1014,7 +1092,7 @@ private func renderSignalConnect(_ plan: SignalPlan, className: String) -> [Stri
|
||||||
lines.append(" let ptr = self.pointer")
|
lines.append(" let ptr = self.pointer")
|
||||||
lines.append(" \(detailBody)")
|
lines.append(" \(detailBody)")
|
||||||
lines.append(" return signalName.withCString { cName in")
|
lines.append(" return signalName.withCString { cName in")
|
||||||
lines.append(" let id = _sgtkSignalConnectData(ptr, cName, unsafeBitCast(\(plan.trampolineCName) as (@convention(c) (\(cTypeStr)) -> Void), to: UnsafeRawPointer.self), dataPtr, unsafeBitCast(destroyFn as (@convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Void), to: UnsafeRawPointer.self), 0)")
|
lines.append(" let id = _sgtkSignalConnectData(ptr, cName, unsafeBitCast(\(plan.trampolineCName) as (@convention(c) (\(cTypeStr)) -> \(cRet)), to: UnsafeRawPointer.self), dataPtr, unsafeBitCast(destroyFn as (@convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Void), to: UnsafeRawPointer.self), 0)")
|
||||||
lines.append(" return SignalHandle(id: id, instance: ptr)")
|
lines.append(" return SignalHandle(id: id, instance: ptr)")
|
||||||
lines.append(" }")
|
lines.append(" }")
|
||||||
lines.append(" }")
|
lines.append(" }")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue