Skip to content

Commit

Permalink
add support for array input (#194)
Browse files Browse the repository at this point in the history
* add abihelper and to/from abi

* fixing things up

* test other types

* fix tests

* remove unnecessary try

* move array tests to their own class

* these can't throw

* dont use the swift name for abi args

* fix array input in debug builds

* rename to get_swift_member_name

* add comment on RawRepresentable

* shouldn't have done this

* fix build
  • Loading branch information
stevenbrix authored Dec 23, 2024
1 parent 1095be9 commit 9a4be08
Show file tree
Hide file tree
Showing 28 changed files with 894 additions and 185 deletions.
50 changes: 50 additions & 0 deletions swiftwinrt/Resources/Support/Array+FromAbi.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import C_BINDINGS_MODULE
import Foundation

@_spi(WinRTInternal)
public typealias WinRTArrayAbi<T> = (count: UInt32, start: UnsafeMutablePointer<T>?)

@_spi(WinRTInternal)
extension Array where Element: FromAbi {
public static func from(abi: WinRTArrayAbi<Element.ABI>) -> [Element] {
UnsafeBufferPointer(start: abi.start, count: Int(abi.count)).map { .from(abi: $0) }
}
}

@_spi(WinRTInternal)
extension Array where Element: Numeric {
public static func from(abi: WinRTArrayAbi<Element>) -> [Element] {
Array(UnsafeBufferPointer(start: abi.start, count: Int(abi.count)))
}
}

// RawRepresentable covers Enums, which are simply numberic types, but the where Element: Numeric doesn't
// cover them. These particular cases are written to ensure no accidental conversion of types that can't
// be simply cast to a C-style Array accidentally sneak in
@_spi(WinRTInternal)
extension Array where Element: RawRepresentable, Element.RawValue: Numeric {
public static func from(abi: WinRTArrayAbi<Element>) -> [Element] {
Array(UnsafeBufferPointer(start: abi.start, count: Int(abi.count)))
}
}

@_spi(WinRTInternal)
extension Array {
public static func from<Bridge: AbiInterfaceBridge>(abiBridge: Bridge.Type, abi: WinRTArrayAbi<UnsafeMutablePointer<Bridge.CABI>?>) -> [Element] where Element == Bridge.SwiftProjection? {
UnsafeBufferPointer(start: abi.start, count: Int(abi.count)).map { InterfaceWrapperBase<Bridge>.unwrapFrom(abi: ComPtr($0)) }
}
}

@_spi(WinRTInternal)
extension Array {
public static func from<Bridge: AbiInterfaceBridge>(abiBridge: Bridge.Type, start: UnsafeMutablePointer<UnsafeMutablePointer<Bridge.CABI>?>?, count: UInt32) -> [Element] where Element == Bridge.SwiftProjection? {
UnsafeBufferPointer(start: start, count: Int(count)).map { InterfaceWrapperBase<Bridge>.unwrapFrom(abi: ComPtr($0)) }
}
}

@_spi(WinRTInternal)
extension Array {
public static func from<Bridge: AbiBridge>(abiBridge: Bridge.Type, abi: WinRTArrayAbi<UnsafeMutablePointer<Bridge.CABI>?>) -> [Element] where Element == Bridge.SwiftProjection?, Bridge.SwiftProjection: WinRTClass {
UnsafeBufferPointer(start: abi.1, count: Int(abi.0)).map { Bridge.from(abi: ComPtr($0)) }
}
}
57 changes: 57 additions & 0 deletions swiftwinrt/Resources/Support/Array+ToAbi.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import C_BINDINGS_MODULE
import Foundation

@_spi(WinRTInternal)
extension Array where Element: ToAbi {
public func toABI(_ withAbi: (_ length: UInt32, _ bytes: UnsafeMutablePointer<Element.ABI>?) throws -> Void) throws {
let abiArray: [Element.ABI] = try map { try $0.toABI() }
try abiArray.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
let bytesPtr = bytes.baseAddress?.assumingMemoryBound(to: Element.ABI.self)
try withAbi(UInt32(count), .init(mutating: bytesPtr))
}
}
}

@_spi(WinRTInternal)
extension Array where Element: Numeric {
public func toABI(_ withAbi: (_ length: UInt32, _ bytes: UnsafeMutablePointer<Element>?) throws -> Void) throws {
try withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
let bytesPtr = bytes.baseAddress?.assumingMemoryBound(to: Element.self)
try withAbi(UInt32(count), .init(mutating: bytesPtr))
}
}
}

@_spi(WinRTInternal)
extension Array where Element: RawRepresentable, Element.RawValue: Numeric {
public func toABI(_ withAbi: (_ length: UInt32, _ bytes: UnsafeMutablePointer<Element>?) throws -> Void) throws {
try withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
let bytesPtr = bytes.baseAddress?.assumingMemoryBound(to: Element.self)
try withAbi(UInt32(count), .init(mutating: bytesPtr))
}
}
}

@_spi(WinRTInternal)
extension Array {
public func toABI<Bridge: AbiInterfaceBridge>(abiBridge: Bridge.Type, _ withAbi: (_ length: UInt32, _ bytes: UnsafeMutablePointer<UnsafeMutablePointer<Bridge.CABI>?>?) throws -> Void) throws where Element == Bridge.SwiftProjection? {
let abiWrapperArray: [InterfaceWrapperBase<Bridge>?] = map { .init($0) }
let abiArray = try abiWrapperArray.map { try $0?.toABI{ $0 } }
try abiArray.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
let bytesPtr = bytes.baseAddress?.assumingMemoryBound(to: UnsafeMutablePointer<Bridge.CABI>?.self)
try withAbi(UInt32(count), .init(mutating: bytesPtr))
}
}
}

@_spi(WinRTInternal)
extension Array {
public func toABI<Bridge: AbiBridge>(abiBridge: Bridge.Type, _ withAbi: (_ length: UInt32, _ bytes: UnsafeMutablePointer<UnsafeMutablePointer<Bridge.CABI>?>?) throws -> Void) throws where Element == Bridge.SwiftProjection?, Bridge.SwiftProjection: WinRTClass {
let abiArray: [UnsafeMutablePointer<Bridge.CABI>?] = map { RawPointer($0) }
try abiArray.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
let bytesPtr = bytes.baseAddress?.assumingMemoryBound(to: UnsafeMutablePointer<Bridge.CABI>?.self)
try withAbi(UInt32(count), .init(mutating: bytesPtr))
}
}
}

4 changes: 4 additions & 0 deletions swiftwinrt/Resources/Support/Array+toVector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ internal class ArrayVector<T> : IVector {
func getView() -> AnyIVectorView<T>? { return ArrayVectorView(storage) }

func first() -> AnyIIterator<T>? { ArrayIterator(storage) }

func replaceAll(_ items: [T]) {
storage = items
}
}

extension ArrayVector where T: Equatable {
Expand Down
23 changes: 22 additions & 1 deletion swiftwinrt/Resources/Support/IInspectable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ public enum __ABI_ {
return makeFrom(abi: ref) ?? ref
}
public static func tryUnwrapFrom(raw pUnk: UnsafeMutableRawPointer?) -> AnyObject? {
tryUnwrapFromBase(raw: pUnk)
guard let pUnk else { return nil }
return tryUnwrapFromBase(raw: pUnk)
}

internal static func queryInterface(_ pUnk: UnsafeMutablePointer<C_IInspectable>?, _ riid: UnsafePointer<SUPPORT_MODULE.IID>?, _ ppvObject: UnsafeMutablePointer<UnsafeMutableRawPointer?>?) -> HRESULT {
Expand Down Expand Up @@ -141,3 +142,23 @@ extension ComposableImpl where CABI == C_IInspectable {
return .init(lpVtbl: vtblPtr)
}
}

@_spi(WinRTInternal)
public enum __IMPL_ {
public enum AnyBridge: AbiInterfaceBridge {
public static func makeAbi() -> CABI {
let vtblPtr = withUnsafeMutablePointer(to: &__ABI_.IInspectableVTable) { $0 }
return .init(lpVtbl: vtblPtr)
}

public static func from(abi: ComPtr<CABI>?) -> SwiftProjection? {
guard let abi else { return nil }
let ref = IInspectable(abi)
return makeFrom(abi: ref) ?? ref
}

public typealias SwiftProjection = Any
public typealias CABI = C_IInspectable
public typealias SwiftABI = SUPPORT_MODULE.IInspectable
}
}
6 changes: 6 additions & 0 deletions swiftwinrt/Resources/Support/RawTyped.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,34 @@

import WinSDK

@_spi(WinRTInternal)
public func RawPointer<T: IUnknown, U>(_ pUnk: T) -> UnsafeMutablePointer<U> {
return UnsafeMutableRawPointer(pUnk.pUnk.borrow).bindMemory(to: U.self, capacity: 1)
}

@_spi(WinRTInternal)
public func RawPointer<T: IUnknown, U>(_ pUnk: T) -> ComPtr<U> {
return ComPtr(UnsafeMutableRawPointer(pUnk.pUnk.borrow).bindMemory(to: U.self, capacity: 1))
}

@_spi(WinRTInternal)
public func RawPointer<T: IUnknown, U>(_ pUnk: T?) -> UnsafeMutablePointer<U>? {
guard let pUnk else { return nil }
let result: UnsafeMutablePointer<U> = RawPointer(pUnk)
return result
}

@_spi(WinRTInternal)
public func RawPointer<T: WinRTClass, U>(_ obj: T?) -> UnsafeMutablePointer<U>? {
return obj?._getABI()
}

@_spi(WinRTInternal)
public func RawPointer<T: AbiInterfaceImpl, U>(_ obj: T) -> UnsafeMutablePointer<U> {
return RawPointer(obj._default)
}

@_spi(WinRTInternal)
public func RawPointer<T: AbiInterfaceImpl, U>(_ obj: T?) -> UnsafeMutablePointer<U>? {
guard let obj else { return nil}
let result: UnsafeMutablePointer<U> = RawPointer(obj)
Expand Down
113 changes: 84 additions & 29 deletions swiftwinrt/code_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,21 +176,41 @@ namespace swiftwinrt
s();
w.write("_ %: ", get_swift_name(param));
if (param.out()) w.write("inout ");
write_type(w, *param.type, type_params);
const bool is_array = param.signature.Type().is_array() || param.signature.Type().is_szarray();
if (is_array && type_params.layer == projection_layer::swift)
{
// Can't allow for implicit unwrap in arrays
w.write("[%]", bind<write_type>(*param.type, write_type_params::swift));
}
else
{
write_type(w, *param.type, type_params);
}
}
}
static void write_function_params(writer& w, function_def const& function, write_type_params const& type_params)
{
write_function_params2(w, function.params, type_params);
}

static void write_convert_to_abi_arg(writer& w, std::string_view const& param_name, const metadata_type* type, bool is_out)
template <typename Param>
static void write_convert_to_abi_arg(writer& w, Param const& param)
{
TypeDef signature_type;
auto type = param.type;
auto param_name = param.name;
auto is_out = param.out();

auto category = get_category(type, &signature_type);

auto local_name = local_swift_param_name(param_name);
if (category == param_category::object_type)
if (param.is_array())
{
// Arrays are all converted from the swift array to a c array, so they
// use the local_param_name
w.write("count, %", local_name);
}
else if (category == param_category::object_type)
{
if (is_out) throw std::exception("out parameters of reference types should not be converted directly to abi types");

Expand Down Expand Up @@ -286,7 +306,7 @@ namespace swiftwinrt
s();
if (param.in())
{
write_convert_to_abi_arg(w, param_name, param.type, false);
write_convert_to_abi_arg(w, param);
}
else
{
Expand Down Expand Up @@ -702,43 +722,59 @@ typealias % = InterfaceWrapperBase<%>

for (auto& param : signature.params)
{
if (param.signature.Type().is_szarray())
{
// TODO: WIN-32 swiftwinrt: add support for arrays
w.write("**TODO: implement szarray in write_convert_vtable_params**");
}
else
std::string param_name = "$" + std::to_string(param_number);


if (param.in())
{
std::string param_name = "$" + std::to_string(param_number);
assert(!param.out());

if (param.in())
if (is_delegate(param.type))
{
assert(!param.out());

if (is_delegate(param.type))
w.write("guard let % = % else { return E_INVALIDARG }\n",
get_swift_name(param),
bind<write_consume_type>(param.type, param_name, false));
}
else if (param.is_array())
{
auto array_param_name = "$" + std::to_string(param_number + 1);
auto count_param_name = param_name;
if (is_reference_type(param.type))
{
w.write("guard let % = % else { return E_INVALIDARG }\n",
w.write("let %: [%] = .from(abiBridge: %.self, abi: (count: %, start: %))\n",
get_swift_name(param),
bind<write_consume_type>(param.type, param_name, false));
bind<write_type>(*param.type, write_type_params::swift),
bind_bridge_fullname(*param.type),
count_param_name,
array_param_name);
}
else
{
w.write("let %: % = %\n",
w.write("let %: [%] = .from(abi: (count: %, start: %))\n",
get_swift_name(param),
bind<write_type>(*param.type, write_type_params::swift),
bind<write_consume_type>(param.type, param_name, false));
count_param_name,
array_param_name);
}
++param_number;
}
else
{
assert(!param.in());
assert(param.out());
w.write("var %: %%\n",
w.write("let %: % = %\n",
get_swift_name(param),
bind<write_type>(*param.type, write_type_params::swift),
bind<write_default_init_assignment>(*param.type, projection_layer::swift));
bind<write_consume_type>(param.type, param_name, false));
}
}
else
{
assert(!param.in());
assert(param.out());
w.write("var %: %%\n",
get_swift_name(param),
bind<write_type>(*param.type, write_type_params::swift),
bind<write_default_init_assignment>(*param.type, projection_layer::swift));
}
++param_number;
}
}
Expand Down Expand Up @@ -1628,7 +1664,21 @@ vtable);

if (param.in())
{
if (category == param_category::string_type)
if (param.is_array())
{
if (is_reference_type(param.type))
{
w.write("try %.toABI(abiBridge: %.self) { (count, %) in\n", param_name, bind_bridge_name(*param.type), local_param_name);
}
else
{
w.write("try %.toABI { (count, %) in\n", param_name, local_param_name);
}

guard.push_indent();
guard.push("}\n");
}
else if (category == param_category::string_type)
{
w.write("let % = try! HString(%)\n",
local_param_name,
Expand Down Expand Up @@ -2418,7 +2468,7 @@ public init<Composable: ComposableImpl>(
}));

if (compose)
{
{
w.write("@_spi(WinRTInternal)\n");
w.write("public typealias Composable = %\n", composableName);
}
Expand Down Expand Up @@ -2736,8 +2786,11 @@ override % func _getABI<T>() -> UnsafeMutablePointer<T>? {
}

// assigns return or out parameters in vtable methods
static void do_write_abi_val_assignment(writer& w, const metadata_type* type, std::string_view const& param_name, std::string_view const& return_param_name)
template<typename T>
static void do_write_abi_val_assignment(writer& w, T const& return_type, std::string_view return_param_name)
{
auto type = return_type.type;
auto param_name = get_swift_member_name(return_type.name);
TypeDef signature_type{};
auto category = get_category(type, &signature_type);

Expand Down Expand Up @@ -2768,7 +2821,9 @@ override % func _getABI<T>() -> UnsafeMutablePointer<T>? {

w.write("%?.initialize(to: %)\n",
return_param_name,
bind<write_convert_to_abi_arg>(param_name, type, true)
bind([&](writer& w) {
write_convert_to_abi_arg(w, return_type);
})
);
}

Expand All @@ -2781,15 +2836,15 @@ override % func _getABI<T>() -> UnsafeMutablePointer<T>? {
{
auto return_param_name = "$" + std::to_string(param_number);
auto param_name = get_swift_name(param);
do_write_abi_val_assignment(w, param.type, std::string_view(param_name), return_param_name);
do_write_abi_val_assignment(w, param, return_param_name);
}
param_number++;
}

if (signature.return_type)
{
auto return_param_name = "$" + std::to_string(signature.params.size() + 1);
do_write_abi_val_assignment(w, signature.return_type.value().type, signature.return_type.value().name, return_param_name);
do_write_abi_val_assignment(w, signature.return_type.value(), return_param_name);
}
}

Expand Down
Loading

0 comments on commit 9a4be08

Please sign in to comment.