Skip to content

Commit

Permalink
Made MetaclassResolver into a protocol (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle authored Apr 5, 2024
1 parent c3807a4 commit 6e39e84
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 92 deletions.
4 changes: 2 additions & 2 deletions Generator/Sources/CodeWriters/Swift/SwiftType+write.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ extension SwiftType: CustomStringConvertible, TextOutputStreamable {
public func write(to output: inout some TextOutputStream) {
switch self {
case let .chain(chain):
if chain.protocolModifier == .existential {
if chain.protocolModifier == .any {
output.write("any ")
}
else if chain.protocolModifier == .opaque {
else if chain.protocolModifier == .some {
output.write("some ")
}

Expand Down
4 changes: 2 additions & 2 deletions Generator/Sources/CodeWriters/Swift/SwiftType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ public enum SwiftType {
case any

public enum ProtocolModifier {
case existential // any
case opaque // some
case any // existential
case some // opaque
}

public struct Chain {
Expand Down
5 changes: 4 additions & 1 deletion Generator/Sources/ProjectionModel/SupportModules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ extension SupportModules.WinRT {
.chain([ .init(moduleName), .init("IReferenceUnboxingProjection"), .init("Of", genericArgs: [ projectionType ]) ])
}

public static var metaclassResolver: SwiftType { .chain(moduleName, "MetaclassResolver") }
public static var iactivationFactoryProjection: SwiftType { .chain(moduleName, "IActivationFactoryProjection") }

public static var anyMetaclassResolver: SwiftType { .chain(protocolModifier: .any, moduleName, "MetaclassResolver") }
public static var systemMetaclassResolver: SwiftType { .chain(moduleName, "SystemMetaclassResolver") }
}

public enum BuiltInTypeKind {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ fileprivate func writeProtocolTypeAlias(_ interfaceDefinition: InterfaceDefiniti
name: try projection.toTypeName(interfaceDefinition),
typeParams: interfaceDefinition.genericParams.map { $0.name },
target: .identifier(
protocolModifier: .existential,
protocolModifier: .any,
name: try projection.toProtocolName(interfaceDefinition),
genericArgs: interfaceDefinition.genericParams.map { .identifier(name: $0.name) }))
}
13 changes: 8 additions & 5 deletions Generator/Sources/SwiftWinRT/Writing/SecondaryInterfaces.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal enum SecondaryInterfaces {
type: SupportModules.COM.comLazyReference(to: abiStructType), initialValue: ".init()")

// private [static] var _istringable: COM.COMInterop<SWRT_WindowsFoundation_IStringable> { get throws {
// try _lazyIStringable.getInterop { try _queryInterface(SWRT_IStringable.iid).reinterpret() }
// try _lazyIStringable.getInterop { try _queryInterface(SWRT_IStringable.iid).cast() }
// } }
let computedPropertyName = getPropertyName(interfaceName: interfaceName)
let abiInteropType: SwiftType = SupportModules.COM.comInterop(of: abiStructType)
Expand All @@ -37,15 +37,18 @@ internal enum SecondaryInterfaces {
let activatableId = try WinRTTypeName.from(type: staticOf.bindType()).description
if interfaceName == "IActivationFactory" {
// Workaround a compiler bug where the compiler doesn't see the SWRT_IActivationFactory extension.
writer.writeStatement("try \(metaclassResolverGlobalName).getActivationFactory(runtimeClass: \"\(activatableId)\")")
writer.writeStatement("try \(metaclassResolverGlobalName).resolve("
+ "runtimeClass: \"\(activatableId)\", "
+ "interfaceID: \(SupportModules.WinRT.iactivationFactoryProjection).interfaceID)")
} else {
writer.writeStatement("try \(metaclassResolverGlobalName).getActivationFactory(\n"
+ "runtimeClass: \"\(activatableId)\", interfaceID: \(abiStructType).iid)")
writer.writeStatement("try \(metaclassResolverGlobalName).resolve("
+ "runtimeClass: \"\(activatableId)\", "
+ "interfaceID: \(abiStructType).iid)")
}
}
else {
let qiMethodName = composable ? "_queryInnerInterface" : "_queryInterface"
writer.writeStatement("try \(qiMethodName)(\(abiStructType).iid).reinterpret()")
writer.writeStatement("try \(qiMethodName)(\(abiStructType).iid).cast()")
}
}
})
Expand Down
4 changes: 2 additions & 2 deletions Generator/Sources/SwiftWinRT/writeProjectionFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ fileprivate func writeGlobalsFile(module: SwiftProjection.Module, toPath path: S
let writer = SwiftSourceFileWriter(output: FileTextOutputStream(path: path, directoryCreation: .ancestors))
writeGeneratedCodePreamble(to: writer)
writeModulePreamble(module, to: writer)
let metaclassResolverType = SupportModules.WinRT.metaclassResolver
let anyMetaclassResolverType = SupportModules.WinRT.anyMetaclassResolver
writer.writeStoredProperty(visibility: .public, declarator: .var, name: metaclassResolverGlobalName,
type: metaclassResolverType, initialValue: "\(metaclassResolverType).default")
type: anyMetaclassResolverType, initialValue: "\(SupportModules.WinRT.systemMetaclassResolver)()")
}
6 changes: 3 additions & 3 deletions InteropTests/Tests/MetaclassResolutionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import WinRTComponent
class MetaclassResolutionTests: WinRTTestCase {
func testCustomResolver() throws {
class Resolver: MetaclassResolver {
private let dllResolver = MetaclassResolver.fromDll(name: "WinRTComponent")
private let dllResolver = DllMetaclassResolver(name: "WinRTComponent.dll")
var lastRuntimeClass: String? = nil

override func getActivationFactory(runtimeClass: String) throws -> COMReference<IActivationFactoryProjection.COMInterface> {
func resolve(runtimeClass: String) throws -> IInspectableReference {
lastRuntimeClass = runtimeClass
return try dllResolver.getActivationFactory(runtimeClass: runtimeClass)
return try dllResolver.resolve(runtimeClass: runtimeClass)
}
}

Expand Down
2 changes: 1 addition & 1 deletion Support/Sources/COM/COMInterop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@ public struct COMInterop<Interface> /* where Interface: COMIUnknownStruct */ {

public func queryInterface<Other /* COMIUnknownStruct */>(
_ id: COMInterfaceID, type: Other.Type = Other.self) throws -> COMReference<Other> {
(try queryInterface(id) as IUnknownReference).reinterpret(to: type)
(try queryInterface(id) as IUnknownReference).cast(to: type)
}
}
12 changes: 10 additions & 2 deletions Support/Sources/COM/COMReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,19 @@ public struct COMReference<Interface>: ~Copyable /* where Interface: COMIUnknown
return pointer
}

public func queryInterface(_ id: COMInterfaceID) throws -> IUnknownReference {
try interop.queryInterface(id)
}

public func queryInterface<Other>(_ id: COMInterfaceID, type: Other.Type = Other.self) throws -> COMReference<Other> /* where Interface: COMIUnknownStruct */ {
try interop.queryInterface(id, type: type)
}

// Should require COMIUnknownStruct but we run into compiler bugs.
public consuming func reinterpret<NewInterface>(to type: NewInterface.Type = NewInterface.self) -> COMReference<NewInterface> /* where Interface: COMIUnknownStruct */ {
public consuming func cast<Other>(to type: Other.Type = Other.self) -> COMReference<Other> /* where Interface: COMIUnknownStruct */ {
let pointer = self.pointer
discard self
return COMReference<NewInterface>(transferringRef: pointer.withMemoryRebound(to: NewInterface.self, capacity: 1) { $0 })
return COMReference<Other>(transferringRef: pointer.withMemoryRebound(to: Other.self, capacity: 1) { $0 })
}

deinit {
Expand Down
2 changes: 1 addition & 1 deletion Support/Sources/COM/IAgileObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public enum IAgileObjectProjection: COMProjection {
public static var interfaceID: COMInterfaceID { COMInterface.iid }

public static func _wrap(_ reference: consuming COMReference<COMInterface>) -> SwiftObject {
IUnknownProjection._wrap(reference.reinterpret())
IUnknownProjection._wrap(reference.cast())
}

public static func toCOM(_ object: SwiftObject) throws -> COMReference<COMInterface> {
Expand Down
2 changes: 1 addition & 1 deletion Support/Sources/COM/IUnknown.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public protocol IUnknownProtocol: AnyObject {
extension IUnknownProtocol {
public func _queryInterface<Interface /* COMIUnknownStruct */>(
_ id: COMInterfaceID, type: Interface.Type = Interface.self) throws -> COMReference<Interface> {
(try _queryInterface(id) as IUnknownReference).reinterpret(to: type)
(try _queryInterface(id) as IUnknownReference).cast(to: type)
}

public func _queryInterface<Projection: COMProjection>(_: Projection.Type) throws -> COMReference<Projection.COMInterface> {
Expand Down
147 changes: 77 additions & 70 deletions Support/Sources/WindowsRuntime/Infra/MetaclassResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,98 +2,105 @@ import WindowsRuntime_ABI
import WinSDK
import class Foundation.NSLock

/// Looks up and resolves the metaclass object (aka activation factory) for runtime classes from their name.
open class MetaclassResolver {
public init() {}

open func getActivationFactory<COMInterface>(runtimeClass: String, interfaceID: COMInterfaceID) throws -> COMReference<COMInterface> {
let activationFactory = try getActivationFactory(runtimeClass: runtimeClass)
return try activationFactory.interop.queryInterface(interfaceID)
}
/// Resolves the metaclass object (aka activation factory) for runtime classes from their name.
///
/// WinRT doesn't have a formal metaclass concept, but the usage and API shape for activation factories
/// matches the metaclass concept: an object that exposes the class's static methods.
/// Activation factory is a misnomer because some classes are composable or static.
public protocol MetaclassResolver {
mutating func resolve(runtimeClass: String) throws -> IInspectableReference
}

open func getActivationFactory(runtimeClass: String) throws -> COMReference<SWRT_IActivationFactory> {
try getActivationFactory(runtimeClass: runtimeClass, interfaceID: SWRT_IActivationFactory.iid)
extension MetaclassResolver {
public mutating func resolve<Interface>(runtimeClass: String, interfaceID: COMInterfaceID,
type: Interface.Type = Interface.self) throws -> COMReference<Interface> {
try resolve(runtimeClass: runtimeClass).queryInterface(interfaceID)
}
}

extension MetaclassResolver {
public static let `default`: MetaclassResolver = Default()
/// The metaclass resolver provided by the system, delegating to RoGetActivationFactory.
/// This resolves system classes in the "Windows." namespaces and classes listed in the application manifest.
public struct SystemMetaclassResolver: MetaclassResolver {
public init() {}

private class Default: MetaclassResolver {
override func getActivationFactory<COMInterface>(runtimeClass: String, interfaceID: COMInterfaceID) throws -> COMReference<COMInterface> {
var activatableId = try PrimitiveProjection.String.toABI(runtimeClass)
defer { PrimitiveProjection.String.release(&activatableId) }
public func resolve(runtimeClass: String) throws -> IInspectableReference {
try Self.getActivationFactory(runtimeClass: runtimeClass, interfaceID: IInspectableProjection.interfaceID)
}

var iid = GUIDProjection.toABI(interfaceID)
var rawPointer: UnsafeMutableRawPointer?
try WinRTError.throwIfFailed(WindowsRuntime_ABI.SWRT_RoGetActivationFactory(activatableId, &iid, &rawPointer))
guard let rawPointer else { throw HResult.Error.noInterface }
public static func getActivationFactory<Interface>(runtimeClass: String, interfaceID: COMInterfaceID,
type: Interface.Type = Interface.self) throws -> COMReference<Interface> {
var activatableId = try PrimitiveProjection.String.toABI(runtimeClass)
defer { PrimitiveProjection.String.release(&activatableId) }

let pointer = rawPointer.bindMemory(to: COMInterface.self, capacity: 1)
return COM.COMReference(transferringRef: pointer)
}
var iid = GUIDProjection.toABI(interfaceID)
var rawPointer: UnsafeMutableRawPointer?
try WinRTError.throwIfFailed(WindowsRuntime_ABI.SWRT_RoGetActivationFactory(activatableId, &iid, &rawPointer))
guard let rawPointer else { throw HResult.Error.noInterface }

let pointer = rawPointer.bindMemory(to: Interface.self, capacity: 1)
return COM.COMReference(transferringRef: pointer)
}
}

extension MetaclassResolver {
public static func fromDll(name: String) -> MetaclassResolver { Dll(name: name) }
public static func fromDll(moduleHandle: HMODULE) -> MetaclassResolver { Dll(moduleHandle: moduleHandle) }

private class Dll: MetaclassResolver {
private let libraryNameToLoad: String?
private let lock = NSLock()
private var libraryHandle: WinSDK.HMODULE?
private var cachedFuncPointer: WindowsRuntime_ABI.SWRT_DllGetActivationFactory?

init(name: String) {
self.libraryNameToLoad = name
self.libraryHandle = nil // Loaded on demand
}
/// A metaclass resolver which uses exported functions from a WinRT component dll to resolve metaclasses.
public final class DllMetaclassResolver: MetaclassResolver {
private let libraryNameToLoad: String?
private let lock = NSLock()
private var libraryHandle: WinSDK.HMODULE?
private var cachedGetActivationFactoryFunc: WindowsRuntime_ABI.SWRT_DllGetActivationFactory?

init(moduleHandle: HMODULE) {
self.libraryNameToLoad = nil
self.libraryHandle = moduleHandle
}
public init(name: String) {
self.libraryNameToLoad = name
self.libraryHandle = nil // Loaded on demand
}

deinit {
if libraryNameToLoad != nil, let libraryHandle {
WinSDK.FreeLibrary(libraryHandle)
}
}
public init(moduleHandle: HMODULE) {
self.libraryNameToLoad = nil
self.libraryHandle = moduleHandle
}

var funcPointer: WindowsRuntime_ABI.SWRT_DllGetActivationFactory {
get throws {
if let cachedFuncPointer { return cachedFuncPointer }
deinit {
if libraryNameToLoad != nil, let libraryHandle {
WinSDK.FreeLibrary(libraryHandle)
}
}

lock.lock()
defer { lock.unlock() }
private var getActivationFactoryFunc: WindowsRuntime_ABI.SWRT_DllGetActivationFactory {
get throws {
if let cachedGetActivationFactoryFunc { return cachedGetActivationFactoryFunc }

if let libraryNameToLoad, libraryHandle == nil {
libraryNameToLoad.withCString(encodedAs: UTF16.self) { name in
libraryHandle = WinSDK.LoadLibraryW(name)
}
guard libraryHandle != nil else { throw HResult.Error.fail }
}
lock.lock()
defer { lock.unlock() }

guard let rawFuncPointer = WinSDK.GetProcAddress(libraryHandle, "DllGetActivationFactory") else {
throw HResult.Error.fail
if let libraryNameToLoad, libraryHandle == nil {
libraryNameToLoad.withCString(encodedAs: UTF16.self) { name in
libraryHandle = WinSDK.LoadLibraryW(name)
}
guard libraryHandle != nil else { throw HResult.Error.fail }
}

let funcPointer = unsafeBitCast(rawFuncPointer, to: WindowsRuntime_ABI.SWRT_DllGetActivationFactory.self)
self.cachedFuncPointer = funcPointer
return funcPointer
guard let rawFuncPointer = WinSDK.GetProcAddress(libraryHandle, "DllGetActivationFactory") else {
throw HResult.Error.fail
}

let funcPointer = unsafeBitCast(rawFuncPointer, to: WindowsRuntime_ABI.SWRT_DllGetActivationFactory.self)
self.cachedGetActivationFactoryFunc = funcPointer
return funcPointer
}
}

override func getActivationFactory(runtimeClass: String) throws -> COMReference<SWRT_IActivationFactory> {
var activatableId = try PrimitiveProjection.String.toABI(runtimeClass)
defer { PrimitiveProjection.String.release(&activatableId) }
public func getActivationFactory(runtimeClass: String) throws -> COMReference<IActivationFactoryProjection.COMInterface> {
var activatableId = try PrimitiveProjection.String.toABI(runtimeClass)
defer { PrimitiveProjection.String.release(&activatableId) }

var factoryPointer: UnsafeMutablePointer<SWRT_IActivationFactory>?
try WinRTError.throwIfFailed(funcPointer(activatableId, &factoryPointer))
guard let factoryPointer else { throw HResult.Error.noInterface }
var factoryPointer: UnsafeMutablePointer<SWRT_IActivationFactory>?
try WinRTError.throwIfFailed(getActivationFactoryFunc(activatableId, &factoryPointer))
guard let factoryPointer else { throw HResult.Error.noInterface }

return COM.COMReference(transferringRef: factoryPointer)
}
return COM.COMReference(transferringRef: factoryPointer)
}

public func resolve(runtimeClass: String) throws -> IInspectableReference {
try getActivationFactory(runtimeClass: runtimeClass).cast() // IActivationFactory isa IInspectable
}
}
2 changes: 1 addition & 1 deletion Support/Sources/WindowsRuntime/PropertyValueStatics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal enum PropertyValueStatics {
private static var this: UnsafeMutablePointer<WindowsRuntime_ABI.SWRT_WindowsFoundation_IPropertyValueStatics> {
get throws {
try lazyReference.getPointer {
try MetaclassResolver.default.getActivationFactory(
try SystemMetaclassResolver.getActivationFactory(
runtimeClass: "Windows.Foundation.PropertyValue",
interfaceID: iid)
}
Expand Down

0 comments on commit 6e39e84

Please sign in to comment.