Skip to content

Commit

Permalink
Implemented composition!
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle committed Feb 25, 2024
1 parent 2cff728 commit ded5aa2
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 161 deletions.
5 changes: 5 additions & 0 deletions Generator/Sources/ProjectionModel/SupportModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import DotNetMetadata
public enum SupportModule {
public static var comModuleName: String { "COM" }

public static var implementABIMethodFunc: String { "\(comModuleName).implementABIMethod" }

public static var iunknownPointer: SwiftType { .chain(comModuleName, "IUnknownPointer") }
public static var comInterfaceID: SwiftType { .chain(comModuleName, "COMInterfaceID") }
public static var nullResult: SwiftType { .chain(comModuleName, "NullResult") }

Expand All @@ -15,6 +18,8 @@ public enum SupportModule {
public static var guidProjection: SwiftType { .chain(comModuleName, "GUIDProjection") }
public static var hresultProjection: SwiftType { .chain(comModuleName, "HResultProjection") }

public static var comExportedInterface: SwiftType { .chain(comModuleName, "COMExportedInterface") }

public static func comInterop(of type: SwiftType) -> SwiftType {
.chain([ .init(comModuleName), .init("COMInterop", genericArgs: [type]) ])
}
Expand Down
15 changes: 15 additions & 0 deletions Generator/Sources/ProjectionModel/SwiftProjection+abi.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import CodeWriters
import DotNetMetadata
import WindowsMetadata

extension SwiftProjection {
public func toABIType(_ type: BoundType) throws -> SwiftType {
precondition(!(type.definition is ClassDefinition)) // Classes have no ABI representation
return .chain(abiModuleName, try CAbi.mangleName(type: type))
}

public func toABIVirtualTableType(_ type: BoundType) throws -> SwiftType {
precondition(type.definition is InterfaceDefinition || type.definition is DelegateDefinition)
return .chain(abiModuleName, try CAbi.mangleName(type: type) + CAbi.virtualTableSuffix)
}
}
27 changes: 19 additions & 8 deletions Generator/Sources/SwiftWinRT/Writing/ABIProjectionType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ fileprivate func writeStructProjectionExtension(
_ structDefinition: StructDefinition,
projection: SwiftProjection,
to writer: SwiftSourceFileWriter) throws {
let abiType = SwiftType.chain(projection.abiModuleName, try CAbi.mangleName(type: structDefinition.bindType()))
let abiType = try projection.toABIType(structDefinition.bindType())

// TODO: Support strings and IReference<T> field types (non-inert)
// extension <struct>: ABIInertProjection
Expand Down Expand Up @@ -211,6 +211,21 @@ fileprivate func writeClassProjectionType(
},
projection: projection,
to: writer)

let overridableInterfaces = try classDefinition.baseInterfaces.compactMap {
try $0.hasAttribute(OverridableAttribute.self) ? $0.interface : nil
}
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.nameWithoutGenericSuffix),
abiType: interface.asBoundType, swiftType: classDefinition.bindType(),
projection: projection, to: writer)
}
}
}
}
}

Expand Down Expand Up @@ -266,9 +281,7 @@ fileprivate func writeInterfaceOrDelegateProjectionType(
writer.writeStatement("withUnsafePointer(to: &virtualTable) { $0 }")
}

try writer.writeStoredProperty(
visibility: .private, static: true, declarator: .var, name: "virtualTable",
initializer: { try writeVirtualTable(interfaceOrDelegate: type, projection: projection, to: $0) })
try writeVirtualTableProperty(name: "virtualTable", abiType: type, swiftType: type, projection: projection, to: writer)
}
}

Expand All @@ -279,14 +292,12 @@ internal func writeCOMProjectionConformance(
toCOMBody: (_ writer: inout SwiftStatementWriter, _ paramName: String) throws -> Void,
projection: SwiftProjection,
to writer: SwiftTypeDefinitionWriter) throws {
let abiName = try CAbi.mangleName(type: abiType)

writer.writeTypeAlias(visibility: .public, name: "SwiftObject",
target: try projection.toType(apiType.asNode).unwrapOptional())
writer.writeTypeAlias(visibility: .public, name: "COMInterface",
target: .chain(projection.abiModuleName, abiName))
target: try projection.toABIType(abiType))
writer.writeTypeAlias(visibility: .public, name: "COMVirtualTable",
target: .chain(projection.abiModuleName, abiName + CAbi.virtualTableSuffix))
target: try projection.toABIVirtualTableType(abiType))

// public static var id: COM.COMInterfaceID { COM.COMInterop<COMInterface>.iid }
writer.writeComputedProperty(visibility: .public, static: true, name: "id", type: SupportModule.comInterfaceID) { writer in
Expand Down
74 changes: 62 additions & 12 deletions Generator/Sources/SwiftWinRT/Writing/ClassDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ internal func writeClassDefinition(_ classDefinition: ClassDefinition, projectio
fileprivate struct ClassInterfaces {
var hasDefaultFactory = false
var factories: [InterfaceDefinition] = []
var `static`: [InterfaceDefinition] = []
var `default`: BoundInterface? = nil
var secondary: [Secondary] = []
var `static`: [InterfaceDefinition] = []

struct Secondary {
var interface: BoundInterface
Expand All @@ -73,8 +73,6 @@ fileprivate struct ClassInterfaces {
hasDefaultFactory = activatableAttributes.count > factories.count
}

`static` = try classDefinition.getAttributes(StaticAttribute.self).map { $0.interface }

for baseInterface in classDefinition.baseInterfaces {
if try baseInterface.hasAttribute(DefaultAttribute.self) {
`default` = try baseInterface.interface
Expand All @@ -85,6 +83,8 @@ fileprivate struct ClassInterfaces {
secondary.append(Secondary(interface: try baseInterface.interface, overridable: overridable, protected: protected))
}
}

`static` = try classDefinition.getAttributes(StaticAttribute.self).map { $0.interface }
}
}

Expand All @@ -101,6 +101,14 @@ fileprivate func writeClassMembers(
try writeClassInterfaceProperties(
classDefinition, interfaces: interfaces, composable: composable,
projection: projection, to: writer)

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

fileprivate func writeClassInterfaceImplementations(
Expand All @@ -124,8 +132,9 @@ fileprivate func writeClassInterfaceImplementations(

if let defaultInterface = interfaces.default, !defaultInterface.definition.methods.isEmpty {
try writeMarkComment(forInterface: defaultInterface, to: writer)
let thisPointer: ThisPointer = composable ? .init(name: "_interop")
: .init(name: SecondaryInterfaces.getPropertyName(defaultInterface), lazy: true)
let thisPointer: ThisPointer = composable
? .init(name: SecondaryInterfaces.getPropertyName(defaultInterface), lazy: true)
: .init(name: "_interop")
try writeInterfaceImplementation(
interfaceOrDelegate: defaultInterface.asBoundType,
thisPointer: thisPointer,
Expand Down Expand Up @@ -156,21 +165,21 @@ fileprivate func writeClassInterfaceProperties(
_ classDefinition: ClassDefinition, interfaces: ClassInterfaces, composable: Bool,
projection: SwiftProjection, to writer: SwiftTypeDefinitionWriter) throws {
// Instance properties, initializers and deinit
if let defaultInterface = interfaces.default {
try SecondaryInterfaces.writeDeclaration(defaultInterface, projection: projection, to: writer)
if composable, let defaultInterface = interfaces.default {
try SecondaryInterfaces.writeDeclaration(defaultInterface, composable: composable, projection: projection, to: writer)
}

for secondaryInterface in interfaces.secondary {
try SecondaryInterfaces.writeDeclaration(secondaryInterface.interface, projection: projection, to: writer)
try SecondaryInterfaces.writeDeclaration(secondaryInterface.interface, composable: composable, projection: projection, to: writer)
}

if composable, let defaultInterface = interfaces.default {
try writeSupportComposableInitializers(defaultInterface: defaultInterface, projection: projection, to: writer)
}

if interfaces.default != nil || !interfaces.secondary.isEmpty {
if (composable && interfaces.default != nil) || !interfaces.secondary.isEmpty {
writer.writeDeinit { writer in
if let defaultInterface = interfaces.default {
if composable, let defaultInterface = interfaces.default {
SecondaryInterfaces.writeCleanup(defaultInterface, to: writer)
}

Expand Down Expand Up @@ -206,6 +215,48 @@ fileprivate func writeClassInterfaceProperties(
}
}

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

for interface in interfaces {
// private var _istringable_outer: COM.COMExportedInterface = .uninitialized
writer.writeStoredProperty(
visibility: .private, declarator: .var,
name: SecondaryInterfaces.getPropertyName(interface, suffix: outerPropertySuffix),
type: SupportModule.comExportedInterface, initialValue: ".uninitialized")
}

// public override func _queryOverridesInterfacePointer(_ id: COM.COMInterfaceID) throws -> COM.IUnknownPointer? {
try writer.writeFunc(
visibility: .public, override: true, name: "_queryOverridesInterfacePointer",
params: [ .init(label: "_", name: "id", type: SupportModule.comInterfaceID) ], throws: true,
returnType: .optional(wrapped: SupportModule.iunknownPointer)) { writer in
for interface in interfaces {
// if id == COMInterop<SWRT_IFoo>.iid {
let comImport = try SupportModule.comInterop(of: projection.toABIType(interface.asBoundType))
try writer.writeBracedBlock("if id == \(comImport).iid") { writer in
// if !_ifooOverrides_outer.isInitialized {
// _ifooOverrides_outer = COMExportedInterface(
// swiftObject: self, virtualTable: &FooProjection.VirtualTables.ifooOverrides)
// }
let outerPropertyName = SecondaryInterfaces.getPropertyName(interface, suffix: outerPropertySuffix)
try writer.writeBracedBlock("if !\(outerPropertyName).isInitialized") { writer in
let projectionTypeName = try projection.toProjectionTypeName(classDefinition)
let vtablePropertyName = Casing.pascalToCamel(interface.definition.nameWithoutGenericSuffix)
writer.writeStatement("\(outerPropertyName) = \(SupportModule.comExportedInterface)(\n"
+ "swiftObject: self, virtualTable: &\(projectionTypeName).VirtualTables.\(vtablePropertyName))")
}

// return _iminimalUnsealedClassOverridesOuter.unknownPointer.addingRef()
writer.writeReturnStatement(value: "\(outerPropertyName).unknownPointer.addingRef()")
}
}
writer.writeReturnStatement(value: "nil")
}
}

fileprivate func writeMarkComment(forInterface interface: BoundInterface, to writer: SwiftTypeDefinitionWriter) throws {
let interfaceName = try WinRTTypeName.from(type: interface.asBoundType).description
writeMarkComment(forInterface: interfaceName, to: writer)
Expand Down Expand Up @@ -289,9 +340,8 @@ fileprivate func writeSupportComposableInitializers(
// public init(_transferringRef comPointer: UnsafeMutablePointer<CWinRTComponent.SWRT_IFoo>) {
// super.init(_transferringRef: IInspectablePointer.cast(comPointer))
// }
let defaultInterfaceABIName = try CAbi.mangleName(type: defaultInterface.asBoundType)
let param = SwiftParam(label: "_transferringRef", name: "comPointer",
type: .unsafeMutablePointer(to: .chain(projection.abiModuleName, defaultInterfaceABIName)))
type: .unsafeMutablePointer(to: try projection.toABIType(defaultInterface.asBoundType)))
writer.writeInit(visibility: .public, params: [param]) { writer in
writer.writeStatement("super.init(_transferringRef: IInspectablePointer.cast(comPointer))")
}
Expand Down
28 changes: 18 additions & 10 deletions Generator/Sources/SwiftWinRT/Writing/SecondaryInterfaces.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ import struct Foundation.UUID

internal enum SecondaryInterfaces {
internal static func writeDeclaration(
_ interface: BoundInterface, staticOf: ClassDefinition? = nil,
_ interface: BoundInterface, staticOf: ClassDefinition? = nil, composable: Bool = false,
projection: SwiftProjection, to writer: SwiftTypeDefinitionWriter) throws {
try writeDeclaration(
interfaceName: projection.toTypeName(interface.definition, namespaced: false),
abiStructType: .chain(projection.abiModuleName, CAbi.mangleName(type: interface.asBoundType)),
abiStructType: projection.toABIType(interface.asBoundType),
iid: WindowsMetadata.getInterfaceID(interface.asBoundType),
staticOf: staticOf, projection: projection, to: writer)
staticOf: staticOf, composable: composable,
projection: projection, to: writer)
}

internal static func writeDeclaration(
interfaceName: String, abiStructType: SwiftType, iid: UUID, staticOf: ClassDefinition? = nil,
interfaceName: String, abiStructType: SwiftType, iid: UUID, staticOf: ClassDefinition? = nil, composable: Bool = false,
projection: SwiftProjection, to writer: SwiftTypeDefinitionWriter) throws {

// private [static] var _istringable_storage: COM.COMInterop<SWRT_WindowsFoundation_IStringable>? = nil
Expand All @@ -41,18 +42,25 @@ internal enum SecondaryInterfaces {
+ "activatableId: \"\(activatableId)\", id: \(abiInteropType).iid)")
}
else {
writer.writeStatement("try _queryInterfacePointer(\(abiInteropType).iid).cast(to: \(abiStructType).self)")
let qiMethodName = composable ? "_queryInnerInterfacePointer" : "_queryInterfacePointer"
writer.writeStatement("try \(qiMethodName)(\(abiInteropType).iid).cast(to: \(abiStructType).self)")
}
}
})
}

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

internal static func getPropertyName(interfaceName: String) -> String {
"_" + Casing.pascalToCamel(interfaceName)
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 {
getPropertyName(interface) + "_outer"
}

internal static func writeCleanup(_ interface: BoundInterface, to writer: SwiftStatementWriter) {
Expand All @@ -65,6 +73,6 @@ internal enum SecondaryInterfaces {
}

fileprivate static func getStoredPropertyName(_ interfaceName: String) -> String {
"_\(Casing.pascalToCamel(interfaceName))_storage"
getPropertyName(interfaceName: interfaceName) + "_storage"
}
}
Loading

0 comments on commit ded5aa2

Please sign in to comment.