Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create WinRTBridgeable protocol for simple converting to/from the ABI #193

Merged
merged 3 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
stevenbrix marked this conversation as resolved.
Show resolved Hide resolved
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