Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce ComposableClass.OuterObject #439

Merged
merged 6 commits into from
Dec 20, 2024
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
2 changes: 2 additions & 0 deletions Generator/Sources/ProjectionModel/SupportModules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ extension SupportModules.WinRT {
public static var composableClassBinding: SwiftType { moduleType.member("ComposableClassBinding") }

public static var composableClass: SwiftType { moduleType.member("ComposableClass") }
public static var composableClass_outerObject: SwiftType { composableClass.member("OuterObject") }
public static var composableClass_outerObject_shortName: String { "OuterObject" }
public static var composableClass_supportsOverrides: String { "supportsOverrides" }

public static func winRTImport(of type: SwiftType) -> SwiftType {
Expand Down
82 changes: 72 additions & 10 deletions Generator/Sources/SwiftWinRT/Writing/ABIBinding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,19 +275,81 @@ fileprivate func writeClassBindingType(
projection: projection,
to: writer)

let overridableInterfaces = try classDefinition.baseInterfaces.compactMap {
try $0.hasAttribute(OverridableAttribute.self) ? $0.interface : nil
if !classDefinition.isSealed {
try writeComposableClassOuterObject(classDefinition, projection: projection, to: writer)
}
if !overridableInterfaces.isEmpty {
try writer.writeEnum(visibility: .internal, name: "VirtualTables") { writer in
for interface in overridableInterfaces {
try writeVirtualTableProperty(
visibility: .internal,
name: Casing.pascalToCamel(interface.definition.nameWithoutGenericArity),
abiType: interface.asBoundType, swiftType: classDefinition.bindType(),
projection: projection, to: writer)
}
}

fileprivate func writeComposableClassOuterObject(
_ classDefinition: ClassDefinition,
projection: Projection,
to writer: SwiftTypeDefinitionWriter) throws {
let outerObjectClassName = SupportModules.WinRT.composableClass_outerObject_shortName

let baseOuterObjectClass: SwiftType
if let base = try classDefinition.base, try base.definition.base != nil {
baseOuterObjectClass = .named(try projection.toBindingTypeName(base.definition))
.member(outerObjectClassName)
} else {
baseOuterObjectClass = SupportModules.WinRT.composableClass_outerObject
}

let overridableInterfaces = try classDefinition.baseInterfaces.compactMap {
try $0.hasAttribute(OverridableAttribute.self) ? $0.interface : nil
}

// If nothing to override:
// public typealias OuterObject = SuperclassBinding.OuterObject
guard !overridableInterfaces.isEmpty else {
writer.writeTypeAlias(visibility: .public, name: outerObjectClassName, target: baseOuterObjectClass)
return
}

let classSwiftType: SwiftType = .named(try projection.toTypeName(classDefinition))

try writer.writeClass(
visibility: .open,
name: outerObjectClassName,
base: baseOuterObjectClass) { writer in

// public override func _queryInterface(_ id: COM.COMInterfaceID) throws -> COM.IUnknownReference {
try writer.writeFunc(
visibility: .public, override: true, name: "_queryInterface",
params: [ .init(label: "_", name: "id", type: SupportModules.COM.comInterfaceID) ], throws: true,
returnType: SupportModules.COM.iunknownReference) { writer in
for interface in overridableInterfaces {
// if id == uuidof(SWRT_IFoo.self) {
let abiSwiftType = try projection.toABIType(interface.asBoundType)
writer.writeBracedBlock("if id == uuidof(\(abiSwiftType).self)") { writer in
let propertyName = SecondaryInterfaces.getPropertyName(interface)

// _ifoo.initEmbedder(owner as! MyClass)
// return _ifoo.toCOM()
writer.writeStatement("\(propertyName).initEmbedder(owner as! \(classSwiftType))")
writer.writeReturnStatement(value: "\(propertyName).toCOM()")
}
}

writer.writeReturnStatement(value: "try super._queryInterface(id)")
}

for interface in overridableInterfaces {
// private var _ifoo: COM.COMEmbedding = .init(virtualTable: &OuterObject.istringable, embedder: nil)
let vtablePropertyName = Casing.pascalToCamel(interface.definition.nameWithoutGenericArity)
writer.writeStoredProperty(
visibility: .private, declarator: .var,
name: SecondaryInterfaces.getPropertyName(interface),
type: SupportModules.COM.comEmbedding,
initialValue: ".init(virtualTable: &\(outerObjectClassName).\(vtablePropertyName), embedder: nil)")
}

for interface in overridableInterfaces {
try writeVirtualTableProperty(
visibility: .private,
name: Casing.pascalToCamel(interface.definition.nameWithoutGenericArity),
abiType: interface.asBoundType, swiftType: classDefinition.bindType(),
projection: projection, to: writer)
}
}
}
Expand Down
57 changes: 8 additions & 49 deletions Generator/Sources/SwiftWinRT/Writing/ClassDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,6 @@ fileprivate func writeClassMembers(

// var _lazyFoo: COM.COMReference<SWRT_IFoo>.Optional = .none
try writeSecondaryInterfaces(classDefinition, interfaces: interfaces, projection: projection, to: writer)

if !classDefinition.isSealed { // Composable
let overridableInterfaces = interfaces.secondary.compactMap { $0.overridable ? $0.interface : nil }
if !overridableInterfaces.isEmpty {
writer.writeMarkComment("Override support")
try writeOverrideSupport(classDefinition, interfaces: overridableInterfaces, projection: projection, to: writer)
}
}
}

fileprivate func writeInterfaceImplementations(
Expand Down Expand Up @@ -251,43 +243,6 @@ fileprivate func writeSecondaryInterfaces(
}
}

fileprivate func writeOverrideSupport(
_ classDefinition: ClassDefinition, interfaces: [BoundInterface],
projection: Projection, to writer: SwiftTypeDefinitionWriter) throws {
let outerPropertySuffix = "outer"

for interface in interfaces {
// private var _ifoo_outer: COM.COMEmbedding = .init(
// virtualTable: &SWRT_IStringable.VirtualTables.IStringable, embedder: nil)
let bindingTypeName = try projection.toBindingTypeName(classDefinition)
let vtablePropertyName = Casing.pascalToCamel(interface.definition.nameWithoutGenericArity)
writer.writeStoredProperty(
visibility: .private, declarator: .var,
name: SecondaryInterfaces.getPropertyName(interface, suffix: outerPropertySuffix),
type: SupportModules.COM.comEmbedding,
initialValue: ".init(virtualTable: &\(bindingTypeName).VirtualTables.\(vtablePropertyName), embedder: nil)")
}

// public override func _queryOverridesInterface(_ id: COM.COMInterfaceID) throws -> COM.IUnknownReference.Optional {
try writer.writeFunc(
visibility: .public, override: true, name: "_queryOverridesInterface",
params: [ .init(label: "_", name: "id", type: SupportModules.COM.comInterfaceID) ], throws: true,
returnType: SupportModules.COM.iunknownReference_Optional) { writer in
for interface in interfaces {
// if id == uuidof(SWRT_IFoo.self) {
let abiSwiftType = try projection.toABIType(interface.asBoundType)
writer.writeBracedBlock("if id == uuidof(\(abiSwiftType).self)") { writer in
let outerPropertyName = SecondaryInterfaces.getPropertyName(interface, suffix: outerPropertySuffix)

// _ifoo_outer.initEmbedder(self)
// return .init(_ifoo_outer.toCOM())
writer.writeStatement("\(outerPropertyName).initEmbedder(self)")
writer.writeReturnStatement(value: ".init(\(outerPropertyName).toCOM())")
}
}
writer.writeReturnStatement(value: ".none")
}
}

fileprivate func writeMarkComment(forInterface interface: BoundInterface, to writer: SwiftTypeDefinitionWriter) throws {
let interfaceName = try WinRTTypeName.from(type: interface.asBoundType).description
Expand All @@ -304,6 +259,7 @@ fileprivate func writeComposableInitializers(
let propertyName = SecondaryInterfaces.getPropertyName(factoryInterface.bind())

let baseClassDefinition = try getRuntimeClassBase(classDefinition)
let outerObjectType: SwiftType = .named(try projection.toBindingTypeName(classDefinition)).member("OuterObject")

for method in factoryInterface.methods {
// Swift requires "override" on initializers iff the same initializer is defined in the direct base class
Expand All @@ -323,7 +279,7 @@ fileprivate func writeComposableInitializers(
params: params.dropLast(2).map { $0.toSwiftParam() }, // Drop inner and outer pointer params
throws: true) { writer in
let output = writer.output
try output.writeLineBlock(header: "try super.init {", footer: "}") {
try output.writeLineBlock(header: "try super.init(_outer: \(outerObjectType).self) {", footer: "}") {
let outerObjectParamName = params[params.count - 2].name
let innerObjectParamName = params[params.count - 1].name
output.writeFullLine("(\(outerObjectParamName), \(innerObjectParamName): inout IInspectablePointer?) in")
Expand Down Expand Up @@ -449,12 +405,15 @@ fileprivate func writeDelegatingWrappingInitializer(

fileprivate func writeDelegatingComposableInitializer(
defaultInterface: BoundInterface, projection: Projection, to writer: SwiftTypeDefinitionWriter) throws {
// public init<ABIStruct>(_factory: ComposableFactory<ABIStruct>) throws {
// public init<ABIStruct>(_outer: OuterObject.Type, _factory: ComposableFactory<ABIStruct>) throws {
writer.writeInit(visibility: .public,
override: true,
genericParams: [ "ABIStruct" ],
params: [ SwiftParam(name: "_factory", type: .named("ComposableFactory", genericArgs: [.named("ABIStruct") ])) ],
params: [
SwiftParam(name: "_outer", type: .named("OuterObject").member("Type")),
SwiftParam(name: "_factory", type: .named("ComposableFactory", genericArgs: [ .named("ABIStruct") ]))
],
throws: true) { writer in
writer.writeStatement("try super.init(_factory: _factory)")
writer.writeStatement("try super.init(_outer: _outer, _factory: _factory)")
}
}
14 changes: 4 additions & 10 deletions Generator/Sources/SwiftWinRT/Writing/SecondaryInterfaces.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,12 @@ internal enum SecondaryInterfaces {
})
}

internal static func getPropertyName(_ interface: BoundInterface, suffix: String? = nil) -> String {
getPropertyName(interfaceName: interface.definition.nameWithoutGenericArity, suffix: suffix)
internal static func getPropertyName(_ interface: BoundInterface) -> String {
getPropertyName(interfaceName: interface.definition.nameWithoutGenericArity)
}

internal static func getPropertyName(interfaceName: String, suffix: String? = nil) -> String {
var name = "_" + Casing.pascalToCamel(interfaceName)
if let suffix { name += "_" + suffix }
return name
}

internal static func getOverridableOuterName(_ interface: BoundInterface) -> String {
"_outer" + interface.definition.nameWithoutGenericArity
internal static func getPropertyName(interfaceName: String) -> String {
"_" + Casing.pascalToCamel(interfaceName)
}

fileprivate static func getStoredPropertyName(_ interfaceName: String) -> String {
Expand Down
8 changes: 4 additions & 4 deletions Support/Sources/COM/COMDelegatingTearOff.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import COM_ABI
public final class COMDelegatingTearOff: COMEmbedderEx {
private var comEmbedding: COMEmbedding

public init(virtualTable: UnsafeRawPointer, implementer: IUnknown) {
public init(virtualTable: UnsafeRawPointer, owner: IUnknown) {
comEmbedding = .init(virtualTable: virtualTable, embedder: nil)
super.init(implementer: implementer)
super.init(implementer: owner)
comEmbedding.initEmbedder(self)
}

public convenience init<Binding: COMTwoWayBinding>(binding: Binding.Type, implementer: Binding.SwiftObject) {
self.init(virtualTable: Binding.virtualTablePointer, implementer: implementer as! IUnknown)
public convenience init<Binding: COMTwoWayBinding>(binding: Binding.Type, owner: Binding.SwiftObject) {
self.init(virtualTable: Binding.virtualTablePointer, owner: owner as! IUnknown)
}

public func toCOM() -> IUnknownReference { comEmbedding.toCOM() }
Expand Down
2 changes: 1 addition & 1 deletion Support/Sources/COM/COMEmbedding+statics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ extension COMEmbedding {
let opaquePointer = UnsafeMutableRawPointer(bitPattern: embedderAndFlags & ~SWRT_COMEmbeddingFlags_Mask)
assert(opaquePointer != nil, "Bad COM object embedding. The embedder pointer is nil.")

let implementerObject: AnyObject?
let implementerObject: AnyObject
if (embedderAndFlags & SWRT_COMEmbeddingFlags_Extended) != 0 {
// COMEmbedding asserted that we can reinterpret cast to COMEmbedderEx.
implementerObject = Unmanaged<COMEmbedderEx>.fromOpaque(opaquePointer!).takeUnretainedValue().implementer
Expand Down
2 changes: 1 addition & 1 deletion Support/Sources/COM/COMExportBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ open class COMExportBase<PrimaryInterfaceBinding: COMTwoWayBinding>: IUnknownPro
return try FreeThreadedMarshal(self).toCOM().cast()
default:
if let interfaceBinding = Self.queriableInterfaces.first(where: { $0.interfaceID == id }) {
return COMDelegatingTearOff(virtualTable: interfaceBinding.virtualTablePointer, implementer: self).toCOM()
return COMDelegatingTearOff(virtualTable: interfaceBinding.virtualTablePointer, owner: self).toCOM()
}
throw COMError.noInterface
}
Expand Down
Loading
Loading