diff --git a/swiftwinrt/Resources/Support/GUID.swift b/swiftwinrt/Resources/Support/GUID.swift index 4a82f866..80d4eae4 100644 --- a/swiftwinrt/Resources/Support/GUID.swift +++ b/swiftwinrt/Resources/Support/GUID.swift @@ -63,6 +63,18 @@ public extension Foundation.UUID { } } +@_spi(WinRTInternal) +extension Foundation.UUID: WinRTBridgeable { + public typealias ABI = GUID + public static func from(abi: GUID) -> Foundation.UUID { + .init(from: abi) + } + + public func toABI() -> GUID { + .init(from: self) + } +} + public extension GUID { init(from uuid: Foundation.UUID) { self.init( @@ -81,4 +93,4 @@ public extension GUID { ) ) } -} \ No newline at end of file +} diff --git a/swiftwinrt/Resources/Support/HString.swift b/swiftwinrt/Resources/Support/HString.swift index 54e84d52..a25b8576 100644 --- a/swiftwinrt/Resources/Support/HString.swift +++ b/swiftwinrt/Resources/Support/HString.swift @@ -10,25 +10,7 @@ final public class HString { internal private(set) var hString: HSTRING? public init(_ string: String) throws { - let codeUnitCount = string.utf16.count - var pointer: UnsafeMutablePointer? = nil - var hStringBuffer: HSTRING_BUFFER? = nil - - // Note: Methods like String.withCString are not used here because they do a copy to create a null - // terminated string, and requires an additional copy to create an HSTRING. Instead, a single copy is - // done by using WindowsPreallocateStringBuffer to allocate a buffer and directly copying the string into it. - try CHECKED(WindowsPreallocateStringBuffer(UInt32(codeUnitCount), &pointer, &hStringBuffer)); - guard let pointer else { throw Error(hr: E_FAIL) } - _ = UnsafeMutableBufferPointer(start: pointer, count: codeUnitCount).initialize(from: string.utf16) - - do { - var hString: HSTRING? = nil - try CHECKED(WindowsPromoteStringBuffer(hStringBuffer, &hString)); - self.hString = hString - } catch { - WindowsDeleteStringBuffer(hStringBuffer) - throw error - } + self.hString = try string.toABI() } public init(_ hString: HSTRING?) throws { @@ -58,4 +40,3 @@ final public class HString { try! CHECKED(WindowsDeleteString(self.hString)) } } - diff --git a/swiftwinrt/Resources/Support/Swift+Extensions.swift b/swiftwinrt/Resources/Support/Swift+Extensions.swift index f9cfe064..de75a0ac 100644 --- a/swiftwinrt/Resources/Support/Swift+Extensions.swift +++ b/swiftwinrt/Resources/Support/Swift+Extensions.swift @@ -42,12 +42,55 @@ extension StaticString { } } +@_spi(WinRTInternal) +extension String: WinRTBridgeable { + public typealias ABI = HSTRING? + public func toABI() throws -> HSTRING? { + let codeUnitCount = utf16.count + var pointer: UnsafeMutablePointer? = nil + var hStringBuffer: HSTRING_BUFFER? = nil + + // Note: Methods like String.withCString are not used here because they do a copy to create a null + // terminated string, and requires an additional copy to create an HSTRING. Instead, a single copy is + // done by using WindowsPreallocateStringBuffer to allocate a buffer and directly copying the string into it. + try CHECKED(WindowsPreallocateStringBuffer(UInt32(codeUnitCount), &pointer, &hStringBuffer)); + guard let pointer else { throw Error(hr: E_FAIL) } + _ = UnsafeMutableBufferPointer(start: pointer, count: codeUnitCount).initialize(from: utf16) + + do { + var hString: HSTRING? = nil + try CHECKED(WindowsPromoteStringBuffer(hStringBuffer, &hString)); + return hString + } catch { + WindowsDeleteStringBuffer(hStringBuffer) + throw error + } + } + + public static func from(abi: HSTRING?) -> String { + String(from: abi) + } +} + extension Bool { public init(from val: boolean) { self.init(booleanLiteral: val != 0) } } +@_spi(WinRTInternal) +extension Bool: WinRTBridgeable { + public typealias ABI = boolean + + public func toABI() -> boolean { + return .init(from: self) + } + + public static func from(abi: boolean) -> Bool { + return .init(from: abi) + } +} + extension Character { public init(from wchar: WCHAR) { if let scalar = Unicode.Scalar(wchar) { @@ -58,6 +101,19 @@ extension Character { } } +@_spi(WinRTInternal) +extension Character: WinRTBridgeable { + public typealias ABI = WCHAR + + public func toABI() -> WCHAR { + return WCHAR(self.unicodeScalars.first?.value ?? 0) + } + + public static func from(abi: WCHAR) -> Character { + return Character(from: abi) + } +} + extension UnsafeMutableRawPointer { public static var none : UnsafeMutableRawPointer? { return nil } } \ No newline at end of file diff --git a/swiftwinrt/Resources/Support/WinRTBridgeable.swift b/swiftwinrt/Resources/Support/WinRTBridgeable.swift new file mode 100644 index 00000000..00a9edc9 --- /dev/null +++ b/swiftwinrt/Resources/Support/WinRTBridgeable.swift @@ -0,0 +1,14 @@ +@_spi(WinRTInternal) +public protocol ToAbi { + associatedtype ABI + func toABI() throws -> ABI +} + +@_spi(WinRTInternal) +public protocol FromAbi { + associatedtype ABI + static func from(abi: ABI) -> Self +} + +@_spi(WinRTInternal) +public typealias WinRTBridgeable = ToAbi & FromAbi diff --git a/swiftwinrt/code_writers/struct_writers.cpp b/swiftwinrt/code_writers/struct_writers.cpp index 774672a7..97a44a6a 100644 --- a/swiftwinrt/code_writers/struct_writers.cpp +++ b/swiftwinrt/code_writers/struct_writers.cpp @@ -97,9 +97,18 @@ namespace swiftwinrt } } w.write("}\n"); + } + w.write("}\n\n"); + } - w.write("public static func from(abi: %) -> % {\n", - bind_type_mangled(type), type); + void write_struct_bridgeable(writer& w, struct_type const& type) + { + w.write("@_spi(WinRTInternal)\n"); + w.write("extension %: WinRTBridgeable {\n", type); + { + auto indent_guard1 = w.push_indent(); + w.write("public typealias ABI = %\n", bind_type_mangled(type)); + w.write("public static func from(abi: ABI) -> Self {\n"); { auto from_body_indent = w.push_indent(); @@ -120,7 +129,21 @@ namespace swiftwinrt w.write(")\n"); } w.write("}\n"); + + w.write("public func toABI() -> ABI {\n"); + { + auto from_body_indent = w.push_indent(); + if (is_struct_blittable(type)) + { + w.write(".from(swift: self)\n"); + } + else + { + w.write("%._ABI_%(from: self).detach()\n", abi_namespace(type), type.swift_type_name()); + } + } + w.write("}\n"); } w.write("}\n\n"); } -} +} \ No newline at end of file diff --git a/swiftwinrt/code_writers/struct_writers.h b/swiftwinrt/code_writers/struct_writers.h index 50ae087b..057ea257 100644 --- a/swiftwinrt/code_writers/struct_writers.h +++ b/swiftwinrt/code_writers/struct_writers.h @@ -6,4 +6,5 @@ namespace swiftwinrt void write_struct_initializer_params(writer& w, struct_type const& type); void write_struct_init_extension(writer& w, struct_type const& type); void write_struct(writer& w, struct_type const& type); + void write_struct_bridgeable(writer& w, struct_type const& type); } \ No newline at end of file diff --git a/swiftwinrt/file_writers.h b/swiftwinrt/file_writers.h index 44666b6a..77d2acba 100644 --- a/swiftwinrt/file_writers.h +++ b/swiftwinrt/file_writers.h @@ -234,6 +234,9 @@ namespace swiftwinrt w.write("%", w.filter.bind_each(members.delegates)); w.write("%", w.filter.bind_each(members.classes)); } + + w.write("%", w.filter.bind_each(members.structs)); + w.swap(); write_preamble(w, /* swift_code: */ true); diff --git a/swiftwinrt/resources.rc b/swiftwinrt/resources.rc index 708693ea..c524f14c 100644 --- a/swiftwinrt/resources.rc +++ b/swiftwinrt/resources.rc @@ -33,6 +33,7 @@ WinRTWrapperBase RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\WinRTWrap WinSDK+Extensions RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\WinSDK+Extensions.swift" PropertyValue RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\PropertyValue.swift" PropertyValue+ABI RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\PropertyValue+ABI.swift" +WinRTBridgeable RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\WinRTBridgeable.swift" WinRTProtocols RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\WinRTProtocols.swift" Event RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\Events\\Event.swift" EventHandlerSubscription RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\Events\\EventHandlerSubscription.swift" diff --git a/tests/test_component/Sources/test_component/Windows.Data.Text+Impl.swift b/tests/test_component/Sources/test_component/Windows.Data.Text+Impl.swift index 80f91c64..d37ab40d 100644 --- a/tests/test_component/Sources/test_component/Windows.Data.Text+Impl.swift +++ b/tests/test_component/Sources/test_component/Windows.Data.Text+Impl.swift @@ -6,3 +6,14 @@ import Ctest_component @_spi(WinRTInternal) public enum __IMPL_Windows_Data_Text { } +@_spi(WinRTInternal) +extension TextSegment: WinRTBridgeable { + public typealias ABI = __x_ABI_CWindows_CData_CText_CTextSegment + public static func from(abi: ABI) -> Self { + .init(startPosition: abi.StartPosition, length: abi.Length) + } + public func toABI() -> ABI { + .from(swift: self) + } +} + diff --git a/tests/test_component/Sources/test_component/Windows.Data.Text.swift b/tests/test_component/Sources/test_component/Windows.Data.Text.swift index d711d065..34004136 100644 --- a/tests/test_component/Sources/test_component/Windows.Data.Text.swift +++ b/tests/test_component/Sources/test_component/Windows.Data.Text.swift @@ -14,8 +14,5 @@ public struct TextSegment: Hashable, Codable, Sendable { self.startPosition = startPosition self.length = length } - public static func from(abi: __x_ABI_CWindows_CData_CText_CTextSegment) -> TextSegment { - .init(startPosition: abi.StartPosition, length: abi.Length) - } } diff --git a/tests/test_component/Sources/test_component/Windows.Foundation+Impl.swift b/tests/test_component/Sources/test_component/Windows.Foundation+Impl.swift index 1d74d955..e7a9d339 100644 --- a/tests/test_component/Sources/test_component/Windows.Foundation+Impl.swift +++ b/tests/test_component/Sources/test_component/Windows.Foundation+Impl.swift @@ -462,3 +462,58 @@ public enum __IMPL_Windows_Foundation { } } +@_spi(WinRTInternal) +extension DateTime: WinRTBridgeable { + public typealias ABI = __x_ABI_CWindows_CFoundation_CDateTime + public static func from(abi: ABI) -> Self { + .init(universalTime: abi.UniversalTime) + } + public func toABI() -> ABI { + .from(swift: self) + } +} + +@_spi(WinRTInternal) +extension Point: WinRTBridgeable { + public typealias ABI = __x_ABI_CWindows_CFoundation_CPoint + public static func from(abi: ABI) -> Self { + .init(x: abi.X, y: abi.Y) + } + public func toABI() -> ABI { + .from(swift: self) + } +} + +@_spi(WinRTInternal) +extension Rect: WinRTBridgeable { + public typealias ABI = __x_ABI_CWindows_CFoundation_CRect + public static func from(abi: ABI) -> Self { + .init(x: abi.X, y: abi.Y, width: abi.Width, height: abi.Height) + } + public func toABI() -> ABI { + .from(swift: self) + } +} + +@_spi(WinRTInternal) +extension Size: WinRTBridgeable { + public typealias ABI = __x_ABI_CWindows_CFoundation_CSize + public static func from(abi: ABI) -> Self { + .init(width: abi.Width, height: abi.Height) + } + public func toABI() -> ABI { + .from(swift: self) + } +} + +@_spi(WinRTInternal) +extension TimeSpan: WinRTBridgeable { + public typealias ABI = __x_ABI_CWindows_CFoundation_CTimeSpan + public static func from(abi: ABI) -> Self { + .init(duration: abi.Duration) + } + public func toABI() -> ABI { + .from(swift: self) + } +} + diff --git a/tests/test_component/Sources/test_component/Windows.Foundation.swift b/tests/test_component/Sources/test_component/Windows.Foundation.swift index cb890f86..489d3948 100644 --- a/tests/test_component/Sources/test_component/Windows.Foundation.swift +++ b/tests/test_component/Sources/test_component/Windows.Foundation.swift @@ -341,9 +341,6 @@ public struct DateTime: Hashable, Codable, Sendable { public init(universalTime: Int64) { self.universalTime = universalTime } - public static func from(abi: __x_ABI_CWindows_CFoundation_CDateTime) -> DateTime { - .init(universalTime: abi.UniversalTime) - } } /// [Open Microsoft documentation](https://learn.microsoft.com/uwp/api/windows.foundation.point) @@ -357,9 +354,6 @@ public struct Point: Hashable, Codable, Sendable { self.x = x self.y = y } - public static func from(abi: __x_ABI_CWindows_CFoundation_CPoint) -> Point { - .init(x: abi.X, y: abi.Y) - } } /// [Open Microsoft documentation](https://learn.microsoft.com/uwp/api/windows.foundation.rect) @@ -379,9 +373,6 @@ public struct Rect: Hashable, Codable, Sendable { self.width = width self.height = height } - public static func from(abi: __x_ABI_CWindows_CFoundation_CRect) -> Rect { - .init(x: abi.X, y: abi.Y, width: abi.Width, height: abi.Height) - } } /// [Open Microsoft documentation](https://learn.microsoft.com/uwp/api/windows.foundation.size) @@ -395,9 +386,6 @@ public struct Size: Hashable, Codable, Sendable { self.width = width self.height = height } - public static func from(abi: __x_ABI_CWindows_CFoundation_CSize) -> Size { - .init(width: abi.Width, height: abi.Height) - } } /// [Open Microsoft documentation](https://learn.microsoft.com/uwp/api/windows.foundation.timespan) @@ -408,9 +396,6 @@ public struct TimeSpan: Hashable, Codable, Sendable { public init(duration: Int64) { self.duration = duration } - public static func from(abi: __x_ABI_CWindows_CFoundation_CTimeSpan) -> TimeSpan { - .init(duration: abi.Duration) - } } /// [Open Microsoft documentation](https://learn.microsoft.com/uwp/api/windows.foundation.iasyncaction) diff --git a/tests/test_component/Sources/test_component/Windows.Storage.Search+Impl.swift b/tests/test_component/Sources/test_component/Windows.Storage.Search+Impl.swift index 495a773e..319da28f 100644 --- a/tests/test_component/Sources/test_component/Windows.Storage.Search+Impl.swift +++ b/tests/test_component/Sources/test_component/Windows.Storage.Search+Impl.swift @@ -228,3 +228,14 @@ public enum __IMPL_Windows_Storage_Search { } } +@_spi(WinRTInternal) +extension SortEntry: WinRTBridgeable { + public typealias ABI = __x_ABI_CWindows_CStorage_CSearch_CSortEntry + public static func from(abi: ABI) -> Self { + .init(propertyName: .init(from: abi.PropertyName), ascendingOrder: .init(from: abi.AscendingOrder)) + } + public func toABI() -> ABI { + __ABI_Windows_Storage_Search._ABI_SortEntry(from: self).detach() + } +} + diff --git a/tests/test_component/Sources/test_component/Windows.Storage.Search.swift b/tests/test_component/Sources/test_component/Windows.Storage.Search.swift index 32843acf..76bef8bb 100644 --- a/tests/test_component/Sources/test_component/Windows.Storage.Search.swift +++ b/tests/test_component/Sources/test_component/Windows.Storage.Search.swift @@ -414,9 +414,6 @@ public struct SortEntry: Hashable, Codable, Sendable { self.propertyName = propertyName self.ascendingOrder = ascendingOrder } - public static func from(abi: __x_ABI_CWindows_CStorage_CSearch_CSortEntry) -> SortEntry { - .init(propertyName: .init(from: abi.PropertyName), ascendingOrder: .init(from: abi.AscendingOrder)) - } } /// [Open Microsoft documentation](https://learn.microsoft.com/uwp/api/windows.storage.search.istoragefolderqueryoperations) diff --git a/tests/test_component/Sources/test_component/test_component+Impl.swift b/tests/test_component/Sources/test_component/test_component+Impl.swift index 7d5440cd..0b7ee9b4 100644 --- a/tests/test_component/Sources/test_component/test_component+Impl.swift +++ b/tests/test_component/Sources/test_component/test_component+Impl.swift @@ -717,3 +717,69 @@ public enum __IMPL_test_component { } } +@_spi(WinRTInternal) +extension BlittableStruct: WinRTBridgeable { + public typealias ABI = __x_ABI_Ctest__component_CBlittableStruct + public static func from(abi: ABI) -> Self { + .init(first: abi.First, second: abi.Second) + } + public func toABI() -> ABI { + .from(swift: self) + } +} + +@_spi(WinRTInternal) +extension NonBlittableBoolStruct: WinRTBridgeable { + public typealias ABI = __x_ABI_Ctest__component_CNonBlittableBoolStruct + public static func from(abi: ABI) -> Self { + .init(first: .init(from: abi.First), second: .init(from: abi.Second), third: .init(from: abi.Third), fourth: .init(from: abi.Fourth)) + } + public func toABI() -> ABI { + __ABI_test_component._ABI_NonBlittableBoolStruct(from: self).detach() + } +} + +@_spi(WinRTInternal) +extension NonBlittableStruct: WinRTBridgeable { + public typealias ABI = __x_ABI_Ctest__component_CNonBlittableStruct + public static func from(abi: ABI) -> Self { + .init(first: .init(from: abi.First), second: .init(from: abi.Second), third: abi.Third, fourth: .init(from: abi.Fourth)) + } + public func toABI() -> ABI { + __ABI_test_component._ABI_NonBlittableStruct(from: self).detach() + } +} + +@_spi(WinRTInternal) +extension SimpleEventArgs: WinRTBridgeable { + public typealias ABI = __x_ABI_Ctest__component_CSimpleEventArgs + public static func from(abi: ABI) -> Self { + .init(value: abi.Value) + } + public func toABI() -> ABI { + .from(swift: self) + } +} + +@_spi(WinRTInternal) +extension StructWithEnum: WinRTBridgeable { + public typealias ABI = __x_ABI_Ctest__component_CStructWithEnum + public static func from(abi: ABI) -> Self { + .init(names: abi.Names) + } + public func toABI() -> ABI { + .from(swift: self) + } +} + +@_spi(WinRTInternal) +extension StructWithIReference: WinRTBridgeable { + public typealias ABI = __x_ABI_Ctest__component_CStructWithIReference + public static func from(abi: ABI) -> Self { + .init(value1: test_component.__x_ABI_C__FIReference_1_intWrapper.unwrapFrom(abi: ComPtr(abi.Value1)), value2: test_component.__x_ABI_C__FIReference_1_intWrapper.unwrapFrom(abi: ComPtr(abi.Value2))) + } + public func toABI() -> ABI { + __ABI_test_component._ABI_StructWithIReference(from: self).detach() + } +} + diff --git a/tests/test_component/Sources/test_component/test_component.swift b/tests/test_component/Sources/test_component/test_component.swift index 39aa08bb..613aad35 100644 --- a/tests/test_component/Sources/test_component/test_component.swift +++ b/tests/test_component/Sources/test_component/test_component.swift @@ -1456,9 +1456,6 @@ public struct BlittableStruct: Hashable, Codable, Sendable { self.first = first self.second = second } - public static func from(abi: __x_ABI_Ctest__component_CBlittableStruct) -> BlittableStruct { - .init(first: abi.First, second: abi.Second) - } } public struct NonBlittableBoolStruct: Hashable, Codable, Sendable { @@ -1473,9 +1470,6 @@ public struct NonBlittableBoolStruct: Hashable, Codable, Sendable { self.third = third self.fourth = fourth } - public static func from(abi: __x_ABI_Ctest__component_CNonBlittableBoolStruct) -> NonBlittableBoolStruct { - .init(first: .init(from: abi.First), second: .init(from: abi.Second), third: .init(from: abi.Third), fourth: .init(from: abi.Fourth)) - } } public struct NonBlittableStruct: Hashable, Codable, Sendable { @@ -1490,9 +1484,6 @@ public struct NonBlittableStruct: Hashable, Codable, Sendable { self.third = third self.fourth = fourth } - public static func from(abi: __x_ABI_Ctest__component_CNonBlittableStruct) -> NonBlittableStruct { - .init(first: .init(from: abi.First), second: .init(from: abi.Second), third: abi.Third, fourth: .init(from: abi.Fourth)) - } } public struct SimpleEventArgs: Hashable, Codable, Sendable { @@ -1501,9 +1492,6 @@ public struct SimpleEventArgs: Hashable, Codable, Sendable { public init(value: Int32) { self.value = value } - public static func from(abi: __x_ABI_Ctest__component_CSimpleEventArgs) -> SimpleEventArgs { - .init(value: abi.Value) - } } public struct StructWithEnum: Hashable, Codable, Sendable { @@ -1512,9 +1500,6 @@ public struct StructWithEnum: Hashable, Codable, Sendable { public init(names: SwiftifiableNames) { self.names = names } - public static func from(abi: __x_ABI_Ctest__component_CStructWithEnum) -> StructWithEnum { - .init(names: abi.Names) - } } public struct StructWithIReference: Hashable, Codable, Sendable { @@ -1525,9 +1510,6 @@ public struct StructWithIReference: Hashable, Codable, Sendable { self.value1 = value1 self.value2 = value2 } - public static func from(abi: __x_ABI_Ctest__component_CStructWithIReference) -> StructWithIReference { - .init(value1: test_component.__x_ABI_C__FIReference_1_intWrapper.unwrapFrom(abi: ComPtr(abi.Value1)), value2: test_component.__x_ABI_C__FIReference_1_intWrapper.unwrapFrom(abi: ComPtr(abi.Value2))) - } } public protocol IAsyncMethodsWithProgress : WinRTInterface {