Skip to content

Commit

Permalink
Create WinRTBridgeable protocol for simple converting to/from the ABI (
Browse files Browse the repository at this point in the history
…#193)

* add winrt bridgeable

* add winrt bridgeable

* from abi doesn't need to throw
  • Loading branch information
stevenbrix authored Dec 23, 2024
1 parent ad8c2a5 commit 1095be9
Show file tree
Hide file tree
Showing 16 changed files with 258 additions and 63 deletions.
14 changes: 13 additions & 1 deletion swiftwinrt/Resources/Support/GUID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -81,4 +93,4 @@ public extension GUID {
)
)
}
}
}
21 changes: 1 addition & 20 deletions swiftwinrt/Resources/Support/HString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<UInt16>? = 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 {
Expand Down Expand Up @@ -58,4 +40,3 @@ final public class HString {
try! CHECKED(WindowsDeleteString(self.hString))
}
}

56 changes: 56 additions & 0 deletions swiftwinrt/Resources/Support/Swift+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<UInt16>? = 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) {
Expand All @@ -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 }
}
14 changes: 14 additions & 0 deletions swiftwinrt/Resources/Support/WinRTBridgeable.swift
Original file line number Diff line number Diff line change
@@ -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
29 changes: 26 additions & 3 deletions swiftwinrt/code_writers/struct_writers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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");
}
}
}
1 change: 1 addition & 0 deletions swiftwinrt/code_writers/struct_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
3 changes: 3 additions & 0 deletions swiftwinrt/file_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ namespace swiftwinrt
w.write("%", w.filter.bind_each<write_delegate_implementation>(members.delegates));
w.write("%", w.filter.bind_each<write_class_bridge>(members.classes));
}

w.write("%", w.filter.bind_each<write_struct_bridgeable>(members.structs));

w.swap();
write_preamble(w, /* swift_code: */ true);

Expand Down
1 change: 1 addition & 0 deletions swiftwinrt/resources.rc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 1095be9

Please sign in to comment.