Skip to content

Commit

Permalink
Reintroduce the COMEmbedding struct and COM_PrivateABI module (#419)
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle authored Dec 6, 2024
1 parent 0d59973 commit 490dec8
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 34 deletions.
2 changes: 1 addition & 1 deletion Generator/Sources/ProjectionModel/SupportModules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ extension SupportModules.COM {
public static var comBinding: SwiftType { .chain(moduleName, "COMBinding") }
public static var comTwoWayBinding: SwiftType { .chain(moduleName, "COMTwoWayBinding") }

public static var comEmbedding: SwiftType { .chain(moduleName, "SWRT_COMEmbedding") }
public static var comEmbedding: SwiftType { .chain(moduleName, "COMEmbedding") }

public static var comReference: SwiftType { .chain(moduleName, "COMReference") }
public static func comReference(to type: SwiftType) -> SwiftType {
Expand Down
10 changes: 5 additions & 5 deletions Generator/Sources/SwiftWinRT/Writing/ClassDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -244,15 +244,15 @@ fileprivate func writeOverrideSupport(
let outerPropertySuffix = "outer"

for interface in interfaces {
// private var _ifoo_outer: COM.SWRT_COMEmbedding = .init(
// virtualTable: &SWRT_IStringable.VirtualTables.IStringable, swiftEmbedder: nil)
// 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), swiftEmbedder: nil)")
initialValue: ".init(virtualTable: &\(bindingTypeName).VirtualTables.\(vtablePropertyName), embedder: nil)")
}

// public override func _queryOverridesInterface(_ id: COM.COMInterfaceID) throws -> COM.IUnknownReference.Optional {
Expand All @@ -266,9 +266,9 @@ fileprivate func writeOverrideSupport(
try writer.writeBracedBlock("if id == uuidof(\(abiSwiftType).self)") { writer in
let outerPropertyName = SecondaryInterfaces.getPropertyName(interface, suffix: outerPropertySuffix)

// _ifoo_outer.initSwiftEmbedder(self)
// _ifoo_outer.initEmbedder(self)
// return .init(_ifoo_outer.toCOM())
writer.writeStatement("\(outerPropertyName).initSwiftEmbedder(self)")
writer.writeStatement("\(outerPropertyName).initEmbedder(self)")
writer.writeReturnStatement(value: ".init(\(outerPropertyName).toCOM())")
}
}
Expand Down
6 changes: 3 additions & 3 deletions Support/Sources/COM/COMDelegatingExport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import COM_ABI

/// Exposes a secondary COM interface whose implementation is delegated to a primary Swift exported object.
public final class COMDelegatingExport: COMEmbedderWithDelegatedImplementation {
private var comEmbedding: SWRT_COMEmbedding
private var comEmbedding: COMEmbedding
public let delegatedImplementation: AnyObject

public init(virtualTable: UnsafeRawPointer, implementer: IUnknown) {
comEmbedding = .init(virtualTable: virtualTable, swiftEmbedder: nil)
comEmbedding = .init(virtualTable: virtualTable, embedder: nil)
delegatedImplementation = implementer
comEmbedding.initSwiftEmbedder(self)
comEmbedding.initEmbedder(self)
}

public convenience init<Binding: COMTwoWayBinding>(binding: Binding.Type, implementer: Binding.SwiftObject) {
Expand Down
42 changes: 28 additions & 14 deletions Support/Sources/COM/COMEmbedding.swift
Original file line number Diff line number Diff line change
@@ -1,43 +1,57 @@
import COM_ABI
@_exported import struct COM_ABI.SWRT_COMEmbedding
import COM_PrivateABI

/// Protocol for Swift objects which embed COM interfaces.
public protocol COMEmbedderWithDelegatedImplementation: AnyObject {
/// Gets the Swift object implementating the COM interface (often the same as self)
var delegatedImplementation: AnyObject { get }
}

/// SWRT_COMEmbedding should be stored as a property of a Swift object
/// to embed a COM object representation which shares its reference count.
/// Use as a stored property in a Swift object to embed a COM object
/// representation which shares its reference count.
/// In most cases, this is done via the `COMImplements<InterfaceBinding>` struct.
extension SWRT_COMEmbedding {
public init(virtualTable: UnsafeRawPointer, swiftEmbedder: AnyObject) {
self.init(
virtualTable: virtualTable,
swiftEmbedder: Unmanaged<AnyObject>.passUnretained(swiftEmbedder).toOpaque())
public struct COMEmbedding: ~Copyable {
private var abi: SWRT_COMEmbedding

public static var null: COMEmbedding { .init() }

private init() {
self.abi = SWRT_COMEmbedding(virtualTable: nil, swiftEmbedder: nil)
}

/// Initializes an instance with a virtual table,
/// but delays setting the embedder since "self" wouldn't be available yet.
public init(virtualTable: UnsafeRawPointer, embedder: Never?) {
self.abi = SWRT_COMEmbedding(virtualTable: virtualTable, swiftEmbedder: nil)
}

public var hasSwiftEmbedder: Bool { swiftEmbedder != nil }
public var virtualTable: UnsafeRawPointer? {
get { abi.virtualTable }
}

public var embedder: AnyObject? {
get { abi.swiftEmbedder.map { Unmanaged<AnyObject>.fromOpaque($0).takeUnretainedValue() } }
}

public mutating func initSwiftEmbedder(_ value: AnyObject) {
if let currentValue = self.swiftEmbedder {
public mutating func initEmbedder(_ value: AnyObject) {
if let currentValue = abi.swiftEmbedder {
assert(Unmanaged<AnyObject>.fromOpaque(currentValue).takeUnretainedValue() === value,
"COM object already embedded in a different object.")
} else {
self.swiftEmbedder = Unmanaged<AnyObject>.passUnretained(value).toOpaque()
abi.swiftEmbedder = Unmanaged<AnyObject>.passUnretained(value).toOpaque()
}
}

public mutating func asUnknownPointer() -> IUnknownPointer {
withUnsafeMutablePointer(to: &self) {
withUnsafeMutablePointer(to: &abi) {
IUnknownPointer(OpaquePointer($0))
}
}

public mutating func toCOM() -> IUnknownReference { .init(addingRef: asUnknownPointer()) }
}

public enum COMEmbedding {
extension COMEmbedding {
fileprivate static func getUnmanagedEmbedderUnsafe<ABIStruct>(_ this: UnsafeMutablePointer<ABIStruct>) -> Unmanaged<AnyObject> {
this.withMemoryRebound(to: SWRT_COMEmbedding.self, capacity: 1) {
Unmanaged<AnyObject>.fromOpaque($0.pointee.swiftEmbedder)
Expand Down
9 changes: 3 additions & 6 deletions Support/Sources/COM/COMImplements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ import COM_ABI
/// Use as a stored property of a class to allow the object to be referenced
/// as a COM object exposing a given interface.
public struct COMImplements<InterfaceBinding: COMTwoWayBinding>: ~Copyable {
private var embedding: SWRT_COMEmbedding = .init()
private var embedding: COMEmbedding = .init(virtualTable: InterfaceBinding.virtualTablePointer, embedder: nil)

public init() {
embedding.virtualTable = InterfaceBinding.virtualTablePointer
// embedding.swiftEmbedder is lazy initialized
}
public init() {}

public mutating func toCOM(embedder: InterfaceBinding.SwiftObject) -> InterfaceBinding.ABIReference {
// The embedder should conform to IUnknownProtocol and hence be an AnyObject,
Expand All @@ -17,7 +14,7 @@ public struct COMImplements<InterfaceBinding: COMTwoWayBinding>: ~Copyable {
}

internal mutating func toCOM(embedder: AnyObject) -> InterfaceBinding.ABIReference {
embedding.initSwiftEmbedder(embedder)
embedding.initEmbedder(embedder)
return embedding.toCOM().cast()
}
}
5 changes: 5 additions & 0 deletions Support/Sources/COM_ABI/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ module COM_ABI {
header "SWRT/winapi/wtypes.h"
header "SWRT/COMEmbedding.h"
export *
}

module COM_PrivateABI {
header "SWRT/COMEmbedding.h"
export *
}
10 changes: 5 additions & 5 deletions Support/Sources/WindowsRuntime/ComposableClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ open class ComposableClass: IInspectableProtocol {

/// The outer object, which brokers QueryInterface calls between the inner object
/// and any Swift overrides. This is only initialized for derived Swift classes.
private var outer: SWRT_COMEmbedding
private var outer: COMEmbedding

/// Initializer for instances created in WinRT
public init(_wrapping inner: consuming IInspectableReference) {
innerWithRef = inner.detach()
// The pointer comes from WinRT so we don't have any overrides and there is no outer object.
// All methods will delegate to the inner object (in this case the full object).
outer = .init()
outer = .null
}

public typealias ComposableFactory<ABIStruct> = (
Expand All @@ -36,9 +36,9 @@ open class ComposableClass: IInspectableProtocol {
// Workaround Swift initialization rules:
// - Factory needs an initialized outer pointer pointing to self
// - self.inner needs to be initialized before being able to reference self
self.outer = .init(virtualTable: IInspectableBinding.virtualTablePointer, swiftEmbedder: nil)
self.outer = .init(virtualTable: IInspectableBinding.virtualTablePointer, embedder: nil)
self.innerWithRef = IInspectablePointer(OpaquePointer(bitPattern: 0xDEADBEEF)!) // We need to assign inner to something, it doesn't matter what.
self.outer.initSwiftEmbedder(self)
self.outer.initEmbedder(self)

// Like C++/WinRT, discard the returned composed object and only use the inner object
// The composed object is useful only when not providing an outer object.
Expand All @@ -49,7 +49,7 @@ open class ComposableClass: IInspectableProtocol {
}
else {
// We're not overriding any methods so we don't need to provide an outer object.
outer = .init()
outer = .null

// We don't care about the inner object since WinRT provides us with the composed object.
var inner: IInspectablePointer? = nil
Expand Down

0 comments on commit 490dec8

Please sign in to comment.