Skip to content

Commit

Permalink
Add a mechanism for pregenerated types in WindowsRuntime.
Browse files Browse the repository at this point in the history
Add basic support for sequences.
  • Loading branch information
tristanlabelle committed Mar 31, 2024
1 parent dd5548e commit a5af8f7
Show file tree
Hide file tree
Showing 31 changed files with 708 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ extension SwiftDeclarationWriter {
visibility: SwiftVisibility = .implicit,
static: Bool = false,
override: Bool = false,
mutating: Bool = false,
name: String,
typeParams: [String] = [],
params: [SwiftParam] = [],
Expand All @@ -106,6 +107,7 @@ extension SwiftDeclarationWriter {
visibility: visibility,
static: `static`,
override: `override`,
mutating: `mutating`,
name: name,
typeParams: typeParams,
params: params,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public struct SwiftProtocolBodyWriter: SwiftSyntaxWriter {
documentation: SwiftDocumentationComment? = nil,
isPropertySetter: Bool = false,
static: Bool = false,
mutating: Bool = false,
name: String,
typeParams: [String] = [],
params: [SwiftParam] = [],
Expand All @@ -81,6 +82,7 @@ public struct SwiftProtocolBodyWriter: SwiftSyntaxWriter {
writeFuncHeader(
visibility: .implicit,
static: `static`,
mutating: `mutating`,
name: name,
typeParams: typeParams,
params: params,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@ public struct SwiftSourceFileWriter: SwiftDeclarationWriter {
self.output = IndentedTextOutputStream(inner: output, indent: indent)
}

public func writeImport(module: String) {
public func writeImport(exported: Bool = false, module: String) {
output.beginLine(grouping: .withName("import"))
if exported { output.write("@_exported ") }
output.write("import ")
output.write(module, endLine: true)
}

public func writeImport(module: String, struct: String) {
public func writeImport(exported: Bool = false, kind: SwiftImportKind, module: String, symbolName: String) {
output.beginLine(grouping: .withName("import"))
output.write("import struct ")
if exported { output.write("@_exported ") }
output.write("import ")
output.write(String(describing: kind))
output.write(" ")
output.write(module)
output.write(".")
output.write(`struct`, endLine: true)
output.write(symbolName, endLine: true)
}

public func writeExtension(
Expand Down
30 changes: 30 additions & 0 deletions Generator/Sources/CodeWriters/Swift/SyntaxWriters/Statements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,34 @@ public struct SwiftStatementWriter: SwiftSyntaxWriter {
try body(SwiftStatementWriter(output: output))
}
}

public func writeIf(
conditions: [String],
then: (_ writer: SwiftStatementWriter) throws -> Void,
else: ((_ writer: SwiftStatementWriter) throws -> Void)? = nil) rethrows {
precondition(!conditions.isEmpty)
output.write("if ")
for (index, condition) in conditions.enumerated() {
if index > 0 { output.write(", ") }
output.write(condition)
}
output.write(" {", endLine: true)
try output.writeIndentedBlock { try then(SwiftStatementWriter(output: output)) }
output.endLine()
output.write("}")
if let `else` {
output.write(" else {", endLine: true)
try output.writeIndentedBlock { try `else`(SwiftStatementWriter(output: output)) }
output.endLine()
output.write("}")
}
output.endLine()
}

public func writeIf(
_ condition: String,
then: (_ writer: SwiftStatementWriter) throws -> Void,
else: ((_ writer: SwiftStatementWriter) throws -> Void)? = nil) rethrows {
try writeIf(conditions: [condition], then: then, else: `else`)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
public enum SwiftImportKind: Hashable {
case `typealias`
case `struct`
case `class`
case `enum`
case `protocol`
case `let`
case `var`
case `func`
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ extension SwiftSyntaxWriter {
visibility: SwiftVisibility = .implicit,
static: Bool = false,
override: Bool = false,
mutating: Bool = false,
name: String,
typeParams: [String] = [],
params: [SwiftParam] = [],
Expand All @@ -71,6 +72,7 @@ extension SwiftSyntaxWriter {
visibility.write(to: &output, trailingSpace: true)
if `static` { output.write("static ") }
if `override` { output.write("override ") }
if `mutating` { output.write("mutating ") }
output.write("func ")
SwiftIdentifier.write(name, to: &output)
writeTypeParams(typeParams)
Expand Down
44 changes: 37 additions & 7 deletions Generator/Sources/ProjectionModel/SupportModules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,44 @@ extension SupportModules.WinRT {
public static var winRTClassLoader: SwiftType { .chain(moduleName, "WinRTClassLoader") }
}

public enum BuiltInTypeKind {
/// Indicates that the support module has special support for this type,
/// and no definition or projection should be generated.
case special
/// Indicates that the support module exposes a definition for this type, but no projection.
case definitionOnly
/// Indicates that the support module exposes both a definition and a projection for this type.
case definitionAndProjection
}

extension SupportModules.WinRT {
public static func hasBuiltInProjection(_ typeDefinition: TypeDefinition) -> Bool {
if typeDefinition.namespace == "Windows.Foundation" {
switch typeDefinition.name {
case "EventRegistrationToken", "HResult", "IReference`1": return true
default: return false
}
private enum BuiltInTypes {
internal static let windowsFoundation: Dictionary<String, BuiltInTypeKind> = [
"DateTime": .definitionOnly,
"EventRegistrationToken": .special,
"HResult": .special,
"IPropertyValue": .definitionOnly,
"IReference`1": .definitionAndProjection,
"IStringable": .definitionAndProjection,
"Point": .definitionOnly,
"PropertyType": .definitionOnly,
"Rect": .definitionOnly,
"Size": .definitionOnly,
"TimeSpan": .definitionOnly
]
internal static let windowsFoundationCollections = [
"IIterable`1",
"IIterator`1"
]
}

public static func getBuiltInTypeKind(_ typeDefinition: TypeDefinition) -> BuiltInTypeKind? {
switch typeDefinition.namespace {
case "Windows.Foundation":
return BuiltInTypes.windowsFoundation[typeDefinition.name]
case "Windows.Foundation.Collections":
return BuiltInTypes.windowsFoundationCollections.contains(typeDefinition.name) ? .definitionOnly : nil
default: return nil
}
return false
}
}
13 changes: 11 additions & 2 deletions Generator/Sources/SwiftWinRT/Writing/ABIProjectionType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal func writeABIProjectionsFile(module: SwiftProjection.Module, toPath pat
for typeDefinition in typeDefinitions.sorted(by: { $0.fullName < $1.fullName }) {
if let classDefinition = typeDefinition as? ClassDefinition, classDefinition.isStatic { continue }
guard typeDefinition.isPublic,
!SupportModules.WinRT.hasBuiltInProjection(typeDefinition),
SupportModules.WinRT.getBuiltInTypeKind(typeDefinition) != .special,
try !typeDefinition.hasAttribute(ApiContractAttribute.self) else { continue }

writer.writeMarkComment(typeDefinition.fullName)
Expand All @@ -25,7 +25,7 @@ internal func writeABIProjectionsFile(module: SwiftProjection.Module, toPath pat
let closedGenericTypesByDefinition = module.closedGenericTypesByDefinition
.sorted { $0.key.fullName < $1.key.fullName }
for (typeDefinition, instantiations) in closedGenericTypesByDefinition {
guard !SupportModules.WinRT.hasBuiltInProjection(typeDefinition) else { continue }
guard SupportModules.WinRT.getBuiltInTypeKind(typeDefinition) != .special else { continue }

for genericArgs in instantiations {
writer.writeMarkComment(try WinRTTypeName.from(type: typeDefinition.bindType(genericArgs: genericArgs)).description)
Expand All @@ -36,6 +36,15 @@ internal func writeABIProjectionsFile(module: SwiftProjection.Module, toPath pat

/// Writes a type or extension providing the ABIProjection conformance for a given projected WinRT type.
internal func writeABIProjectionConformance(_ typeDefinition: TypeDefinition, genericArgs: [TypeNode]?, projection: SwiftProjection, to writer: SwiftSourceFileWriter) throws {
if SupportModules.WinRT.getBuiltInTypeKind(typeDefinition) == .definitionAndProjection {
// The support module already defines a projection, just import and reexport it.
if typeDefinition.isReferenceType {
let projectionTypeName = try projection.toProjectionTypeName(typeDefinition)
writer.writeImport(exported: true, kind: .enum, module: SupportModules.WinRT.moduleName, symbolName: projectionTypeName)
}
return
}

if let structDefinition = typeDefinition as? StructDefinition {
assert(genericArgs == nil)
try writeStructProjectionExtension(structDefinition, projection: projection, to: writer)
Expand Down
43 changes: 13 additions & 30 deletions Generator/Sources/SwiftWinRT/Writing/InterfaceDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@ import WindowsMetadata
//
// var foo: IFoo? = getFoo()
internal func writeInterfaceDefinition(_ interface: InterfaceDefinition, projection: SwiftProjection, to writer: SwiftSourceFileWriter) throws {
try writeProtocolTypeAlias(interface, projection: projection, to: writer)
try writeProtocol(interface, projection: projection, to: writer)
if SupportModules.WinRT.getBuiltInTypeKind(interface) != nil {
// Defined in WindowsRuntime, merely reexport it here.
let protocolName = try projection.toProtocolName(interface)
writer.writeImport(exported: true, kind: .protocol, module: SupportModules.WinRT.moduleName, symbolName: protocolName)

let typeName = try projection.toProtocolName(interface)
switch interface.name {
case "IAsyncAction", "IAsyncActionWithProgress`1":
try writeIAsyncExtensions(protocolName: typeName, resultType: nil, to: writer)
case "IAsyncOperation`1", "IAsyncOperationWithProgress`2":
try writeIAsyncExtensions(
protocolName: typeName,
resultType: .identifier(name: String(interface.genericParams[0].name)),
to: writer)
default: break
// Import the existential typealias as a protocol to work around compiler bug https://github.com/apple/swift/issues/72724:
// "'IFoo' was imported as 'typealias', but is a protocol"
let typeName = try projection.toTypeName(interface)
writer.writeImport(exported: true, kind: .protocol, module: SupportModules.WinRT.moduleName, symbolName: typeName)
}
else {
try writeProtocolTypeAlias(interface, projection: projection, to: writer)
try writeProtocol(interface, projection: projection, to: writer)
try writeInterfaceExtensions(interface, projection: projection, to: writer)
}
}

Expand Down Expand Up @@ -136,21 +137,3 @@ fileprivate func writeProtocolTypeAlias(_ interfaceDefinition: InterfaceDefiniti
name: try projection.toProtocolName(interfaceDefinition),
genericArgs: interfaceDefinition.genericParams.map { .identifier(name: $0.name) }))
}

fileprivate func writeIAsyncExtensions(protocolName: String, resultType: SwiftType?, to writer: SwiftSourceFileWriter) throws {
writer.writeExtension(type: .identifier(protocolName)) { writer in
// public get() async throws
writer.writeFunc(visibility: .public, name: "get", async: true, throws: true, returnType: resultType) { writer in
writer.output.writeIndentedBlock(header: "if try _status() == .started {", footer: "}") {
// We can't await if the completed handler is already set
writer.writeStatement("guard try \(SupportModules.COM.nullResult).catch(_completed()) == nil else { throw \(SupportModules.COM.hresult).Error.illegalMethodCall }")
writer.writeStatement("let awaiter = WindowsRuntime.AsyncAwaiter()")
writer.writeStatement("try _completed({ _, _ in _Concurrency.Task { await awaiter.signal() } })")
writer.writeStatement("await awaiter.wait()")
}

// Handles exceptions and cancelation
writer.writeStatement("return try getResults()")
}
}
}
40 changes: 40 additions & 0 deletions Generator/Sources/SwiftWinRT/Writing/InterfaceExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import CodeWriters
import DotNetMetadata
import WindowsMetadata
import ProjectionModel

internal func writeInterfaceExtensions(_ interface: InterfaceDefinition, projection: SwiftProjection, to writer: SwiftSourceFileWriter) throws {
switch interface.namespace {
case "Windows.Foundation":
let typeName = try projection.toProtocolName(interface)
switch interface.name {
case "IAsyncAction", "IAsyncActionWithProgress`1":
try writeIAsyncExtensions(protocolName: typeName, resultType: nil, to: writer)
case "IAsyncOperation`1", "IAsyncOperationWithProgress`2":
try writeIAsyncExtensions(
protocolName: typeName,
resultType: .identifier(name: String(interface.genericParams[0].name)),
to: writer)
default: break
}
default: break
}
}

internal func writeIAsyncExtensions(protocolName: String, resultType: SwiftType?, to writer: SwiftSourceFileWriter) throws {
writer.writeExtension(type: .identifier(protocolName)) { writer in
// public get() async throws
writer.writeFunc(visibility: .public, name: "get", async: true, throws: true, returnType: resultType) { writer in
writer.output.writeIndentedBlock(header: "if try _status() == .started {", footer: "}") {
// We can't await if the completed handler is already set
writer.writeStatement("guard try \(SupportModules.COM.nullResult).catch(_completed()) == nil else { throw \(SupportModules.COM.hresult).Error.illegalMethodCall }")
writer.writeStatement("let awaiter = WindowsRuntime.AsyncAwaiter()")
writer.writeStatement("try _completed({ _, _ in _Concurrency.Task { await awaiter.signal() } })")
writer.writeStatement("await awaiter.wait()")
}

// Handles exceptions and cancelation
writer.writeStatement("return try getResults()")
}
}
}
2 changes: 1 addition & 1 deletion Generator/Sources/SwiftWinRT/Writing/SourcePreamble.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ internal func writeModulePreamble(_ module: SwiftProjection.Module, to writer: S
writer.writeImport(module: referencedModule.name)
}

writer.writeImport(module: "Foundation", struct: "UUID")
writer.writeImport(kind: .struct, module: "Foundation", symbolName: "UUID")
}
Loading

0 comments on commit a5af8f7

Please sign in to comment.