Bind C array in-parameters, finish cross-module shadow migration
This commit is contained in:
parent
745a4c3401
commit
a172d07ab1
47 changed files with 1301 additions and 2584 deletions
|
|
@ -40,8 +40,16 @@ public enum MarshalIn: Equatable, Sendable {
|
|||
case boolToGboolean
|
||||
/// Convert `String` to a C string (`withCString` / `utf8` pointer).
|
||||
case stringToC
|
||||
/// Convert `[String]` to a NULL-terminated C `char **`.
|
||||
/// Convert `[String]` to a NULL-terminated C `char **` (mutable outer + elements).
|
||||
case stringArrayToC
|
||||
/// Convert `[String]` to a NULL-terminated C `const char * const *` (const outer + elements).
|
||||
case stringConstArrayToC
|
||||
/// Convert `[String]` to a NULL-terminated C `const char **` (const elements, mutable outer pointer).
|
||||
case stringConstElementArrayToC
|
||||
/// Convert `[Wrapper]` to a C array of instance pointers (`T **`), NULL-terminated.
|
||||
case objectArrayToC
|
||||
/// Convert `[T]` of C-scalar values to a contiguous C buffer (`const T *`).
|
||||
case scalarArrayToC
|
||||
/// Access the underlying pointer of an object or interface wrapper.
|
||||
case objectPointer
|
||||
/// Access the underlying pointer of an interface-typed wrapper (a
|
||||
|
|
@ -833,4 +841,34 @@ extension Mapping {
|
|||
static let stringArrayMapping = Mapping(
|
||||
swiftType: "[String]", cSwiftType: "UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?",
|
||||
marshalIn: .stringArrayToC, marshalOut: .unsupported(reason: "C array return not yet bridged"))
|
||||
|
||||
/// Maps a `[String]` parameter to a NULL-terminated C `const char * const *` array.
|
||||
static let constStringArrayMapping = Mapping(
|
||||
swiftType: "[String]", cSwiftType: "UnsafePointer<UnsafePointer<CChar>?>?",
|
||||
marshalIn: .stringConstArrayToC, marshalOut: .unsupported(reason: "C array return not yet bridged"))
|
||||
|
||||
/// Maps a `[String]` parameter to a C `const char **` (const elements,
|
||||
/// mutable outer pointer), NULL-terminated.
|
||||
static let constElementStringArrayMapping = Mapping(
|
||||
swiftType: "[String]", cSwiftType: "UnsafeMutablePointer<UnsafePointer<CChar>?>?",
|
||||
marshalIn: .stringConstElementArrayToC,
|
||||
marshalOut: .unsupported(reason: "C array return not yet bridged"))
|
||||
|
||||
/// Maps a C array of object/interface pointers to `[Wrapper]`.
|
||||
/// - Parameter element: The already-mapped element type.
|
||||
static func objectArrayMapping(element: Mapping) -> Mapping {
|
||||
Mapping(swiftType: "[\(element.swiftType)]", cSwiftType: "UnsafeMutableRawPointer?",
|
||||
marshalIn: .objectArrayToC,
|
||||
marshalOut: .unsupported(reason: "C array return not yet bridged"),
|
||||
category: element.category)
|
||||
}
|
||||
|
||||
/// Maps a contiguous C buffer of scalars to `[T]`.
|
||||
/// - Parameter element: The already-mapped element type.
|
||||
static func scalarArrayMapping(element: Mapping) -> Mapping {
|
||||
Mapping(swiftType: "[\(element.swiftType)]",
|
||||
cSwiftType: "UnsafeMutablePointer<\(element.swiftType)>?",
|
||||
marshalIn: .scalarArrayToC,
|
||||
marshalOut: .unsupported(reason: "C array return not yet bridged"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -888,6 +888,10 @@ public struct ArrayInfo: Equatable, Sendable {
|
|||
public var isZeroTerminated: Bool
|
||||
/// The C type spelling of the array itself (e.g. `"char**"`), when present.
|
||||
public var cType: String
|
||||
/// The C type spelling of the array's *element* (e.g. `"AdwNavigationPage*"`,
|
||||
/// `"guint8"`), when the GIR `<array>`'s child `<type>` carries one. Empty
|
||||
/// when absent; callers then infer element depth from `cType` minus one.
|
||||
public var elementCType: String
|
||||
|
||||
/// Creates array length metadata.
|
||||
///
|
||||
|
|
@ -897,12 +901,15 @@ public struct ArrayInfo: Equatable, Sendable {
|
|||
/// - isZeroTerminated: Whether a zero/NULL terminator delimits the array.
|
||||
/// Defaults to `false`.
|
||||
/// - cType: The C type spelling of the array. Defaults to `""`.
|
||||
/// - elementCType: The C type spelling of the array's element. Defaults to `""`.
|
||||
public init(lengthParameterIndex: Int? = nil, fixedSize: Int? = nil,
|
||||
isZeroTerminated: Bool = false, cType: String = "") {
|
||||
isZeroTerminated: Bool = false, cType: String = "",
|
||||
elementCType: String = "") {
|
||||
self.lengthParameterIndex = lengthParameterIndex
|
||||
self.fixedSize = fixedSize
|
||||
self.isZeroTerminated = isZeroTerminated
|
||||
self.cType = cType
|
||||
self.elementCType = elementCType
|
||||
}
|
||||
|
||||
/// Whether the array's length can be determined at all.
|
||||
|
|
|
|||
|
|
@ -378,6 +378,58 @@ private func renderSupport(moduleName: String, dependencyModules: [String] = [],
|
|||
return try cStrings.withUnsafeMutableBufferPointer { try body($0.baseAddress) }
|
||||
}
|
||||
|
||||
/// Bridges a `[String]` to a NULL-terminated C `const char * const *`, freeing the copies after `body`.
|
||||
@inline(__always)
|
||||
func _withConstStringArray<R>(_ strings: [String], _ body: (UnsafePointer<UnsafePointer<CChar>?>?) throws -> R) rethrows -> R {
|
||||
var cStrings: [UnsafeMutablePointer<CChar>?] = strings.map { g_strdup($0) }
|
||||
cStrings.append(nil)
|
||||
defer { for p in cStrings { g_free(p) } }
|
||||
return try cStrings.withUnsafeMutableBufferPointer { buffer in
|
||||
try buffer.withMemoryRebound(to: (UnsafePointer<CChar>?).self) { rebound in
|
||||
try body(rebound.baseAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bridges a `[String]` to a C `const char **` (const elements, mutable outer
|
||||
/// pointer), NULL-terminated, freeing the copies after `body`.
|
||||
@inline(__always)
|
||||
func _withConstElementStringArray<R>(_ strings: [String], _ body: (UnsafeMutablePointer<UnsafePointer<CChar>?>?) throws -> R) rethrows -> R {
|
||||
var cStrings: [UnsafeMutablePointer<CChar>?] = strings.map { g_strdup($0) }
|
||||
cStrings.append(nil)
|
||||
defer { for p in cStrings { g_free(p) } }
|
||||
return try cStrings.withUnsafeMutableBufferPointer { buffer in
|
||||
try buffer.withMemoryRebound(to: (UnsafePointer<CChar>?).self) { rebound in
|
||||
try body(rebound.baseAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bridges wrapper instance pointers to a NULL-terminated C array of element
|
||||
/// pointers (`T **`), valid only for the duration of `body`. The element
|
||||
/// pointer type `E` is inferred from the C function being called, which is
|
||||
/// how both opaque (`OpaquePointer?`) and complete-struct
|
||||
/// (`UnsafeMutablePointer<GtkWidget>?`) element types are served by one helper.
|
||||
@inline(__always)
|
||||
func _withPointerArray<E, R>(_ pointers: [UnsafeMutableRawPointer], _ body: (UnsafeMutablePointer<E>?) throws -> R) rethrows -> R {
|
||||
var raw: [UnsafeMutableRawPointer?] = pointers
|
||||
raw.append(nil)
|
||||
return try raw.withUnsafeMutableBufferPointer { buffer in
|
||||
try buffer.withMemoryRebound(to: E.self) { rebound in
|
||||
try body(rebound.baseAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bridges a `[T]` of C-scalar values to a contiguous C buffer valid only for
|
||||
/// the duration of `body`. The buffer is a copy: only `const` C parameters
|
||||
/// may be bridged this way (the planner enforces that).
|
||||
@inline(__always)
|
||||
func _withScalarArray<T, R>(_ values: [T], _ body: (UnsafeMutablePointer<T>?) throws -> R) rethrows -> R {
|
||||
var copy = values
|
||||
return try copy.withUnsafeMutableBufferPointer { try body($0.baseAddress) }
|
||||
}
|
||||
|
||||
\(glibError)\(gtypeConstants)\(closureBoxSupport)\(signalSupport)\(primitiveShims)
|
||||
"""
|
||||
}
|
||||
|
|
@ -982,17 +1034,35 @@ private func swiftSignature(_ plan: CallablePlan) -> String {
|
|||
|
||||
|
||||
/// How a string/array parameter is wrapped for the C call.
|
||||
private enum CWrapKind { case plain, optional, array }
|
||||
private enum CWrapKind { case plain, optional, array, constArray, constElementStringArray, pointerArray, scalarArray }
|
||||
|
||||
/// Generates the closure opener for a string/array parameter: `withCString`,
|
||||
/// `_withOptionalCString`, or `_withStringArray`.
|
||||
/// `_withOptionalCString`, `_withStringArray`, `_withConstStringArray`,
|
||||
/// `_withConstElementStringArray`, `_withPointerArray`, or `_withScalarArray`.
|
||||
private func cWrapOpener(_ cName: String, _ swiftName: String, _ kind: CWrapKind) -> String {
|
||||
switch kind {
|
||||
case .plain: return "\(swiftName).withCString { \(cName) in"
|
||||
case .optional: return "_withOptionalCString(\(swiftName)) { \(cName) in"
|
||||
case .array: return "_withStringArray(\(swiftName)) { \(cName) in"
|
||||
case .constArray: return "_withConstStringArray(\(swiftName)) { \(cName) in"
|
||||
case .constElementStringArray: return "_withConstElementStringArray(\(swiftName)) { \(cName) in"
|
||||
case .pointerArray: return "_withPointerArray(\(swiftName).map { $0.pointer }) { \(cName) in"
|
||||
case .scalarArray: return "_withScalarArray(\(swiftName)) { \(cName) in"
|
||||
}
|
||||
}
|
||||
/// The C-buffer wrapping kind for an array `MarshalIn`, or `nil` when the
|
||||
/// parameter is not an array.
|
||||
private func arrayWrapKind(_ marshalIn: MarshalIn) -> CWrapKind? {
|
||||
switch marshalIn {
|
||||
case .stringArrayToC: return .array
|
||||
case .stringConstArrayToC: return .constArray
|
||||
case .stringConstElementArrayToC: return .constElementStringArray
|
||||
case .objectArrayToC: return .pointerArray
|
||||
case .scalarArrayToC: return .scalarArray
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the C call: the C-function-call string with each argument marshalled,
|
||||
/// plus the string parameters that must be wrapped in `withCString`. The
|
||||
/// instance parameter — if any — is passed as `self.pointer`.
|
||||
|
|
@ -1026,9 +1096,9 @@ private func cArguments(_ plan: CallablePlan, error: Bool = false) -> (cCall: St
|
|||
let kind: CWrapKind = param.mapping.swiftType.hasSuffix("?") ? .optional : .plain
|
||||
stringParams.append((cName: cName, swiftName: param.swiftName, kind: kind))
|
||||
cArgExprs.append(cName)
|
||||
} else if param.mapping.marshalIn == .stringArrayToC {
|
||||
} else if let kind = arrayWrapKind(param.mapping.marshalIn) {
|
||||
let cName = "cArray\(stringParams.count)"
|
||||
stringParams.append((cName: cName, swiftName: param.swiftName, kind: .array))
|
||||
stringParams.append((cName: cName, swiftName: param.swiftName, kind: kind))
|
||||
cArgExprs.append(cName)
|
||||
} else if let dataPtrName = closureDataMap[param.cArgIndex], !isCallbackBox(param) {
|
||||
cArgExprs.append(dataPtrName)
|
||||
|
|
@ -1560,7 +1630,7 @@ private func marshalCallArg(_ param: ParameterPlan) -> String {
|
|||
return "\(param.swiftName) ? 1 : 0"
|
||||
case .stringToC:
|
||||
return param.swiftName
|
||||
case .stringArrayToC:
|
||||
case .stringArrayToC, .stringConstArrayToC, .stringConstElementArrayToC, .objectArrayToC, .scalarArrayToC:
|
||||
return param.swiftName
|
||||
case .objectPointer, .interfacePointer:
|
||||
return pointerArg(param)
|
||||
|
|
|
|||
|
|
@ -605,6 +605,7 @@ private let knownMissingCFunctions: Set<String> = [
|
|||
// exported by the .so but invisible to the Clang importer.
|
||||
"g_settings_backend_changed", "g_settings_backend_changed_tree",
|
||||
"g_settings_backend_get_default", "g_settings_backend_path_changed",
|
||||
"g_settings_backend_keys_changed",
|
||||
"g_settings_backend_path_writable_changed", "g_settings_backend_writable_changed",
|
||||
"g_null_settings_backend_new", "g_memory_settings_backend_new", "g_keyfile_settings_backend_new",
|
||||
// Declared in <gio/gnetworking.h>, likewise excluded from <gio/gio.h>.
|
||||
|
|
@ -1285,6 +1286,8 @@ func planFunction(_ fn: GlobalFunction, context: MapContext) -> CallablePlanResu
|
|||
/// `TypeRegistry.overridesAncestorMethod`), requiring Swift's `override`
|
||||
/// keyword. `nil` for interface methods, which have no class ancestry.
|
||||
func planMethod(_ method: Method, declaringGIRName: String? = nil, context: MapContext) -> CallablePlanResult {
|
||||
// Same-module override detection: `overridesAncestorMethod` checks only
|
||||
// ancestors in the same Swift module, where `override` works on `public`.
|
||||
let isOverride = declaringGIRName.map { girName in
|
||||
context.registry.overridesAncestorMethod(
|
||||
named: method.name,
|
||||
|
|
@ -1292,6 +1295,31 @@ func planMethod(_ method: Method, declaringGIRName: String? = nil, context: MapC
|
|||
in: girName
|
||||
)
|
||||
} ?? false
|
||||
// Cross-module conflict detection: a method that shadows a non-`open`
|
||||
// ancestor method from a *different* module must be skipped — Swift
|
||||
// rejects cross-module overrides of `public` methods.
|
||||
//
|
||||
// Same-module collisions are legitimate overrides and are NOT handled
|
||||
// here: they keep their `override` keyword, and the ancestor-dedup
|
||||
// post-pass in `planModules` drops the ones that would still clash.
|
||||
// The two checks are disjoint by module, and this one runs first, so a
|
||||
// method never reaches both.
|
||||
if !isOverride, let girName = declaringGIRName {
|
||||
if context.registry.shadowsCrossModuleAncestorMethod(
|
||||
named: method.name,
|
||||
paramNames: method.parameters.filter { !$0.isInstanceParameter }.map(\.name),
|
||||
in: girName
|
||||
) {
|
||||
// Symbol and reason match the post-pass's ancestor-dedup skips so
|
||||
// that both mechanisms report one rule identically: the qualified
|
||||
// `Namespace.Class.method` form, keyed `.inheritedMember`.
|
||||
let shadowingName = swiftFunctionName(method.name)
|
||||
return .skip(SkipEntry(symbol: "\(girName).\(shadowingName)",
|
||||
cIdentifier: method.cIdentifier,
|
||||
reason: .inheritedMember,
|
||||
detail: "method '\(shadowingName)' shadows an identically-selectored ancestor method in a different module"))
|
||||
}
|
||||
}
|
||||
return planCallable(
|
||||
fullName: "\(context.currentNamespace).\(method.name)",
|
||||
swiftName: swiftFunctionName(method.name), cIdentifier: method.cIdentifier,
|
||||
|
|
@ -1474,16 +1502,23 @@ func planParameters(
|
|||
// These are plain gpointer params that would otherwise fail the pointer
|
||||
// check — the callback-box mechanism handles them.
|
||||
let closureTargets = Set(parameters.compactMap(\.closureIndex))
|
||||
// Pre-scan length parameters for C arrays of strings (e.g. argc for argv).
|
||||
// Map the length param's position to the array's Swift name.
|
||||
// Pre-scan length parameters for in-parameter C arrays. The length C arg is
|
||||
// synthesized from the Swift array's `count` and dropped from the signature.
|
||||
let hasInstance = parameters.contains(where: \.isInstanceParameter)
|
||||
var lengthElision: [Int: String] = [:]
|
||||
for p in parameters {
|
||||
if case .cArray(let el, let info) = p.type,
|
||||
el == .string || el == .filename,
|
||||
let li = info.lengthParameterIndex {
|
||||
lengthElision[li + (hasInstance ? 1 : 0)] = swiftParameterName(p.name)
|
||||
for p in parameters where p.direction == .in {
|
||||
guard case .cArray(_, let info) = p.type, let li = info.lengthParameterIndex else { continue }
|
||||
let cIndex = li + (hasInstance ? 1 : 0)
|
||||
guard cIndex < parameters.count, parameters[cIndex].direction == .in else { continue }
|
||||
// Two arrays sharing one length arg can only be bridged if the caller
|
||||
// passes equal counts; nothing enforces that, and a mismatch overruns the
|
||||
// shorter buffer. Skip rather than guess (e.g. g_spawn_async_with_pipes_and_fds).
|
||||
if let existing = lengthElision[cIndex] {
|
||||
return .skip(SkipEntry(symbol: "", cIdentifier: nil,
|
||||
reason: .arrayBridgingUnimplemented,
|
||||
detail: "parameters '\(existing)' and '\(p.name)' share length parameter index \(li)"))
|
||||
}
|
||||
lengthElision[cIndex] = swiftParameterName(p.name)
|
||||
}
|
||||
for (index, param) in parameters.enumerated() {
|
||||
// The instance parameter is always `self` — passed as `self.pointer`,
|
||||
|
|
@ -1569,16 +1604,29 @@ func planParameters(
|
|||
synthesizedLengthOf: arrName))
|
||||
continue
|
||||
}
|
||||
// Intercept C arrays of strings with a known length param → [String].
|
||||
if case .cArray(let el, let info) = param.type,
|
||||
el == .string || el == .filename, info.lengthParameterIndex != nil {
|
||||
plans.append(ParameterPlan(
|
||||
swiftName: swiftParameterName(param.name), cArgIndex: index,
|
||||
mapping: .stringArrayMapping))
|
||||
continue
|
||||
// Intercept in-parameter C arrays -> Swift [T]. Array returns, out-params,
|
||||
// and array-typed properties still go through `map`, which rejects them.
|
||||
if case .cArray(let element, let info) = param.type {
|
||||
switch Result(catching: { try mapArrayParameter(element: element, info: info,
|
||||
transfer: param.transferOwnership,
|
||||
context: context) }) {
|
||||
case .success(let arrayMapping):
|
||||
guard arrayMapping.isReadyForCallables else {
|
||||
return .skip(SkipEntry(symbol: "", cIdentifier: nil, reason: .unknownType,
|
||||
detail: "array parameter '\(param.name)' element type '\(arrayMapping.swiftType)' not yet generated"))
|
||||
}
|
||||
plans.append(ParameterPlan(
|
||||
swiftName: swiftParameterName(param.name), cArgIndex: index,
|
||||
mapping: arrayMapping))
|
||||
continue
|
||||
case .failure(let error as MapError):
|
||||
return .skip(SkipEntry(symbol: "", cIdentifier: nil, reason: error.reason,
|
||||
detail: "parameter '\(param.name)': \(error.detail)"))
|
||||
case .failure:
|
||||
return .skip(SkipEntry(symbol: "", cIdentifier: nil, reason: .unknownType,
|
||||
detail: "parameter '\(param.name)' array type not mappable"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Map the type
|
||||
let mappingResult = Result { try map(param.type, nullable: param.isNullable,
|
||||
transfer: param.transferOwnership, context: context) }
|
||||
|
|
|
|||
|
|
@ -145,6 +145,101 @@ public func map(
|
|||
return result.optionalised(nullable: nullable)
|
||||
}
|
||||
|
||||
/// Maps a `direction="in"` C array parameter to its Swift `[T]` mapping.
|
||||
///
|
||||
/// Separate from ``map(_:nullable:transfer:context:cType:)`` because only
|
||||
/// *input parameters* can be bridged: the renderer builds a temporary C buffer
|
||||
/// that lives for the duration of the call. Array returns, out-params, and
|
||||
/// array-typed properties still go through `map`, which rejects every
|
||||
/// `.cArray`.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - element: The array's element type from the GIR `<array>`'s child `<type>`.
|
||||
/// - info: The array's length/termination metadata and C type spellings.
|
||||
/// - transfer: Ownership transfer declared on the parameter.
|
||||
/// - context: The resolution context.
|
||||
/// - Returns: A complete `Mapping` whose `marshalIn` is one of the array cases.
|
||||
/// - Throws: `MapError` with `.arrayWithoutLength` or `.arrayBridgingUnimplemented`.
|
||||
public func mapArrayParameter(
|
||||
element: GIRType, info: ArrayInfo,
|
||||
transfer: TransferOwnership, context: MapContext
|
||||
) throws(MapError) -> Mapping {
|
||||
guard info.hasKnownLength else {
|
||||
throw MapError(reason: .arrayWithoutLength, detail: "C array has no length annotation")
|
||||
}
|
||||
let arrayCType = info.cType.replacingOccurrences(of: " ", with: "")
|
||||
guard !arrayCType.isEmpty else {
|
||||
throw MapError(reason: .arrayBridgingUnimplemented, detail: "array has no c:type")
|
||||
}
|
||||
let elementDepth = info.elementCType.isEmpty
|
||||
? cPointerDepth(arrayCType) - 1
|
||||
: cPointerDepth(info.elementCType)
|
||||
|
||||
switch element {
|
||||
case .string, .filename:
|
||||
guard elementDepth == 1 else {
|
||||
throw MapError(reason: .arrayBridgingUnimplemented,
|
||||
detail: "string array element depth \(elementDepth) is not a string vector")
|
||||
}
|
||||
guard transfer != .full else {
|
||||
throw MapError(reason: .arrayBridgingUnimplemented,
|
||||
detail: "string array with transfer-ownership=full")
|
||||
}
|
||||
if arrayCType.hasSuffix("const*") {
|
||||
return .constStringArrayMapping
|
||||
} else if arrayCType.hasPrefix("const") {
|
||||
return .constElementStringArrayMapping
|
||||
} else {
|
||||
return .stringArrayMapping
|
||||
}
|
||||
|
||||
case .int8, .int16, .int32, .uint8, .uint16, .uint32,
|
||||
.char, .uchar, .unichar, .float, .double:
|
||||
guard info.lengthParameterIndex != nil else {
|
||||
throw MapError(reason: .arrayBridgingUnimplemented,
|
||||
detail: "scalar array has no length parameter")
|
||||
}
|
||||
guard arrayCType.contains("const") else {
|
||||
throw MapError(reason: .arrayBridgingUnimplemented,
|
||||
detail: "scalar array C type '\(info.cType)' is not const (may be an output buffer)")
|
||||
}
|
||||
guard elementDepth == 0 else {
|
||||
throw MapError(reason: .arrayBridgingUnimplemented,
|
||||
detail: "scalar array element depth \(elementDepth) is not a contiguous buffer")
|
||||
}
|
||||
return .scalarArrayMapping(element: try map(element, nullable: false, transfer: .none, context: context))
|
||||
|
||||
case .typeRef:
|
||||
let elementMapping = try map(element, nullable: false, transfer: .none, context: context)
|
||||
guard elementMapping.category == .needsClass else {
|
||||
throw MapError(reason: .arrayBridgingUnimplemented,
|
||||
detail: "array element type '\(elementMapping.swiftType)' is not an object or interface")
|
||||
}
|
||||
guard elementDepth == 1 else {
|
||||
throw MapError(reason: .arrayBridgingUnimplemented,
|
||||
detail: "object array element depth \(elementDepth) is not a pointer vector")
|
||||
}
|
||||
guard transfer == .none else {
|
||||
throw MapError(reason: .arrayBridgingUnimplemented,
|
||||
detail: "object array with transfer-ownership=\(transfer.rawValue)")
|
||||
}
|
||||
return .objectArrayMapping(element: elementMapping)
|
||||
|
||||
default:
|
||||
throw MapError(reason: .arrayBridgingUnimplemented,
|
||||
detail: "array element type is not a string, C scalar, object, or interface")
|
||||
}
|
||||
}
|
||||
|
||||
/// Pointer indirection level of a C type spelling. `gpointer` /
|
||||
/// `gconstpointer` count as one level even though they carry no `*`.
|
||||
private func cPointerDepth(_ cType: String) -> Int {
|
||||
let stripped = cType.replacingOccurrences(of: "const", with: "")
|
||||
.replacingOccurrences(of: " ", with: "")
|
||||
let stars = stripped.filter { $0 == "*" }.count
|
||||
let base = stripped.replacingOccurrences(of: "*", with: "")
|
||||
return (base == "gpointer" || base == "gconstpointer") ? stars + 1 : stars
|
||||
}
|
||||
// MARK: - Internal dispatch
|
||||
|
||||
/// Top-level dispatch: one switch with thin per-category delegation so that
|
||||
|
|
|
|||
|
|
@ -495,6 +495,33 @@ public struct TypeRegistry: Sendable {
|
|||
return false
|
||||
}
|
||||
|
||||
/// Checks whether a method in the given class would shadow a non-`open`
|
||||
/// method on an ancestor class from a *different* Swift module. Since
|
||||
/// generated methods are `public` (not `open`), Swift rejects such
|
||||
/// shadowing as an implicit override attempt. Methods flagged here must be
|
||||
/// skipped — they cannot be emitted with `override` because the ancestor
|
||||
/// lives in another module.
|
||||
///
|
||||
/// See ``overridesAncestorMethod(named:paramNames:in:)`` for the
|
||||
/// same-module case where `override` is valid.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - methodName: The raw GIR method name (e.g. `"set_name"`).
|
||||
/// - paramNames: The non-instance parameters' raw GIR names, in order.
|
||||
/// - girName: The fully qualified GIR name of the declaring class.
|
||||
/// - Returns: `true` when an ancestor in a different Swift module declares
|
||||
/// a method of the same signature.
|
||||
public func shadowsCrossModuleAncestorMethod(named methodName: String, paramNames: [String], in girName: String) -> Bool {
|
||||
guard let ownModule = resolve(girName: girName)?.swiftModule else { return false }
|
||||
let signature = MethodSignature(name: methodName, paramNames: paramNames)
|
||||
for ancestor in ancestry(of: girName) where ancestor.swiftModule != ownModule {
|
||||
if instanceMethodSignatures[ancestor.girName]?.contains(signature) == true {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/// Whether a class descends from `GObject.InitiallyUnowned`.
|
||||
///
|
||||
/// This is the sole basis for deciding that a constructor's result must be
|
||||
|
|
|
|||
|
|
@ -618,6 +618,7 @@ final class GIRXMLDelegate: NSObject, XMLParserDelegate {
|
|||
builder.children.append(type)
|
||||
$0 = .type(builder)
|
||||
case .array(var builder):
|
||||
if builder.children.isEmpty { builder.info.elementCType = cType }
|
||||
builder.children.append(type)
|
||||
$0 = .array(builder)
|
||||
default:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue